2
0

[MIG] account_loan: Migration to 13.0

Co-authored-by: Enric Tobella <etobella@creublanca.es>
This commit is contained in:
Alba Riera 2021-02-23 12:15:36 +01:00 committed by Enric Tobella
parent e6972252f5
commit e56f6f9e26
11 changed files with 138 additions and 143 deletions

View File

@ -2,7 +2,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{ {
"name": "Account Loan management", "name": "Account Loan management",
"version": "12.0.1.1.0", "version": "13.0.1.1.0",
"author": "Creu Blanca,Odoo Community Association (OCA)", "author": "Creu Blanca,Odoo Community Association (OCA)",
"website": "http://github.com/OCA/account-financial-tools", "website": "http://github.com/OCA/account-financial-tools",
"license": "AGPL-3", "license": "AGPL-3",
@ -19,5 +19,5 @@
"views/account_move_view.xml", "views/account_move_view.xml",
], ],
"installable": True, "installable": True,
"external_dependencies": {"python": ["numpy",],}, "external_dependencies": {"python": ["numpy", "numpy-financial<=1.0.0"]},
} }

View File

@ -0,0 +1,16 @@
# Copyright 2021 Creu Blanca - Alba Riera
from openupgradelib import openupgrade
@openupgrade.migrate()
def migrate(env, version):
openupgrade.logged_query(
env.cr,
"""
UPDATE account_move am
SET loan_line_id = ai.loan_line_id,
loan_id = ai.loan_id
FROM account_invoice ai
WHERE ai.id = am.old_invoice_id and ai.loan_id is not null""",
)

View File

@ -1,7 +1,6 @@
# Copyright 2018 Creu Blanca # Copyright 2018 Creu Blanca
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import account_invoice
from . import account_loan from . import account_loan
from . import account_loan_line from . import account_loan_line
from . import account_move from . import account_move

View File

@ -1,45 +0,0 @@
# Copyright 2018 Creu Blanca
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models
class AccountInvoice(models.Model):
_inherit = "account.invoice"
loan_line_id = fields.Many2one(
"account.loan.line", readonly=True, ondelete="restrict",
)
loan_id = fields.Many2one(
"account.loan", readonly=True, store=True, ondelete="restrict",
)
@api.multi
def finalize_invoice_move_lines(self, move_lines):
vals = super().finalize_invoice_move_lines(move_lines)
if self.loan_line_id:
ll = self.loan_line_id
if ll.long_term_loan_account_id and ll.long_term_principal_amount != 0:
vals.append(
(
0,
0,
{
"account_id": ll.loan_id.short_term_loan_account_id.id,
"credit": ll.long_term_principal_amount,
"debit": 0,
},
)
)
vals.append(
(
0,
0,
{
"account_id": ll.long_term_loan_account_id.id,
"credit": 0,
"debit": ll.long_term_principal_amount,
},
)
)
return vals

View File

@ -11,7 +11,7 @@ from odoo import api, fields, models
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
try: try:
import numpy import numpy_financial
except (ImportError, IOError) as err: except (ImportError, IOError) as err:
_logger.debug(err) _logger.debug(err)
@ -96,7 +96,7 @@ class AccountLoan(models.Model):
help="Real rate that will be applied on each period", help="Real rate that will be applied on each period",
) )
rate_type = fields.Selection( rate_type = fields.Selection(
[("napr", "Nominal APR"), ("ear", "EAR"), ("real", "Real rate"),], [("napr", "Nominal APR"), ("ear", "EAR"), ("real", "Real rate")],
required=True, required=True,
help="Method of computation of the applied rate", help="Method of computation of the applied rate",
default="napr", default="napr",
@ -253,7 +253,7 @@ class AccountLoan(models.Model):
for record in self: for record in self:
if record.loan_type == "fixed-annuity": if record.loan_type == "fixed-annuity":
record.fixed_amount = -record.currency_id.round( record.fixed_amount = -record.currency_id.round(
numpy.pmt( numpy_financial.pmt(
record.loan_rate() / 100, record.loan_rate() / 100,
record.fixed_periods, record.fixed_periods,
record.fixed_loan_amount, record.fixed_loan_amount,
@ -262,7 +262,7 @@ class AccountLoan(models.Model):
) )
elif record.loan_type == "fixed-annuity-begin": elif record.loan_type == "fixed-annuity-begin":
record.fixed_amount = -record.currency_id.round( record.fixed_amount = -record.currency_id.round(
numpy.pmt( numpy_financial.pmt(
record.loan_rate() / 100, record.loan_rate() / 100,
record.fixed_periods, record.fixed_periods,
record.fixed_loan_amount, record.fixed_loan_amount,
@ -341,7 +341,6 @@ class AccountLoan(models.Model):
vals["name"] = self.get_default_name(vals) vals["name"] = self.get_default_name(vals)
return super().create(vals) return super().create(vals)
@api.multi
def post(self): def post(self):
self.ensure_one() self.ensure_one()
if not self.start_date: if not self.start_date:
@ -349,11 +348,9 @@ class AccountLoan(models.Model):
self.compute_draft_lines() self.compute_draft_lines()
self.write({"state": "posted"}) self.write({"state": "posted"})
@api.multi
def close(self): def close(self):
self.write({"state": "closed"}) self.write({"state": "closed"})
@api.multi
def compute_lines(self): def compute_lines(self):
self.ensure_one() self.ensure_one()
if self.state == "draft": if self.state == "draft":
@ -407,7 +404,6 @@ class AccountLoan(models.Model):
"rate": self.rate_period, "rate": self.rate_period,
} }
@api.multi
def compute_draft_lines(self): def compute_draft_lines(self):
self.ensure_one() self.ensure_one()
self.fixed_periods = self.periods self.fixed_periods = self.periods
@ -431,7 +427,6 @@ class AccountLoan(models.Model):
if self.long_term_loan_account_id: if self.long_term_loan_account_id:
self.check_long_term_principal_amount() self.check_long_term_principal_amount()
@api.multi
def view_account_moves(self): def view_account_moves(self):
self.ensure_one() self.ensure_one()
action = self.env.ref("account.action_move_line_form") action = self.env.ref("account.action_move_line_form")
@ -439,10 +434,9 @@ class AccountLoan(models.Model):
result["domain"] = [("loan_id", "=", self.id)] result["domain"] = [("loan_id", "=", self.id)]
return result return result
@api.multi
def view_account_invoices(self): def view_account_invoices(self):
self.ensure_one() self.ensure_one()
action = self.env.ref("account.action_invoice_tree2") action = self.env.ref("account.action_move_out_invoice_type")
result = action.read()[0] result = action.read()[0]
result["domain"] = [("loan_id", "=", self.id), ("type", "=", "in_invoice")] result["domain"] = [("loan_id", "=", self.id), ("type", "=", "in_invoice")]
return result return result
@ -471,6 +465,6 @@ class AccountLoan(models.Model):
[("state", "=", "posted"), ("is_leasing", "=", True)] [("state", "=", "posted"), ("is_leasing", "=", True)]
): ):
res += record.line_ids.filtered( res += record.line_ids.filtered(
lambda r: r.date <= date and not r.invoice_ids lambda r: r.date <= date and not r.move_ids
).generate_invoice() ).generate_invoice()
return res return res

View File

@ -8,7 +8,7 @@ from odoo.exceptions import UserError
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
try: try:
import numpy import numpy_financial
except (ImportError, IOError) as err: except (ImportError, IOError) as err:
_logger.error(err) _logger.error(err)
@ -90,7 +90,6 @@ class AccountLoanLine(models.Model):
) )
move_ids = fields.One2many("account.move", inverse_name="loan_line_id",) move_ids = fields.One2many("account.move", inverse_name="loan_line_id",)
has_moves = fields.Boolean(compute="_compute_has_moves") has_moves = fields.Boolean(compute="_compute_has_moves")
invoice_ids = fields.One2many("account.invoice", inverse_name="loan_line_id",)
has_invoices = fields.Boolean(compute="_compute_has_invoices") has_invoices = fields.Boolean(compute="_compute_has_invoices")
_sql_constraints = [ _sql_constraints = [
( (
@ -105,10 +104,10 @@ class AccountLoanLine(models.Model):
for record in self: for record in self:
record.has_moves = bool(record.move_ids) record.has_moves = bool(record.move_ids)
@api.depends("invoice_ids") @api.depends("move_ids")
def _compute_has_invoices(self): def _compute_has_invoices(self):
for record in self: for record in self:
record.has_invoices = bool(record.invoice_ids) record.has_invoices = bool(record.move_ids)
@api.depends("loan_id.name", "sequence") @api.depends("loan_id.name", "sequence")
def _compute_name(self): def _compute_name(self):
@ -146,7 +145,7 @@ class AccountLoanLine(models.Model):
return self.loan_id.fixed_amount return self.loan_id.fixed_amount
if self.loan_type == "fixed-annuity": if self.loan_type == "fixed-annuity":
return self.currency_id.round( return self.currency_id.round(
-numpy.pmt( -numpy_financial.pmt(
self.loan_id.loan_rate() / 100, self.loan_id.loan_rate() / 100,
self.loan_id.periods - self.sequence + 1, self.loan_id.periods - self.sequence + 1,
self.pending_principal_amount, self.pending_principal_amount,
@ -157,7 +156,7 @@ class AccountLoanLine(models.Model):
return self.loan_id.fixed_amount return self.loan_id.fixed_amount
if self.loan_type == "fixed-annuity-begin": if self.loan_type == "fixed-annuity-begin":
return self.currency_id.round( return self.currency_id.round(
-numpy.pmt( -numpy_financial.pmt(
self.loan_id.loan_rate() / 100, self.loan_id.loan_rate() / 100,
self.loan_id.periods - self.sequence + 1, self.loan_id.periods - self.sequence + 1,
self.pending_principal_amount, self.pending_principal_amount,
@ -168,7 +167,7 @@ class AccountLoanLine(models.Model):
def check_amount(self): def check_amount(self):
"""Recompute amounts if the annuity has not been processed""" """Recompute amounts if the annuity has not been processed"""
if self.move_ids or self.invoice_ids: if self.move_ids:
raise UserError( raise UserError(
_("Amount cannot be recomputed if moves or invoices exists " "already") _("Amount cannot be recomputed if moves or invoices exists " "already")
) )
@ -192,7 +191,7 @@ class AccountLoanLine(models.Model):
def compute_interest(self): def compute_interest(self):
if self.loan_type == "fixed-annuity-begin": if self.loan_type == "fixed-annuity-begin":
return -numpy.ipmt( return -numpy_financial.ipmt(
self.loan_id.loan_rate() / 100, self.loan_id.loan_rate() / 100,
2, 2,
self.loan_id.periods - self.sequence + 1, self.loan_id.periods - self.sequence + 1,
@ -202,7 +201,6 @@ class AccountLoanLine(models.Model):
) )
return self.pending_principal_amount * self.loan_id.loan_rate() / 100 return self.pending_principal_amount * self.loan_id.loan_rate() / 100
@api.multi
def check_move_amount(self): def check_move_amount(self):
""" """
Changes the amounts of the annuity once the move is posted Changes the amounts of the annuity once the move is posted
@ -286,16 +284,12 @@ class AccountLoanLine(models.Model):
return vals return vals
def invoice_vals(self): def invoice_vals(self):
partner = self.loan_id.partner_id.with_context(
force_company=self.loan_id.company_id.id
)
return { return {
"loan_line_id": self.id, "loan_line_id": self.id,
"loan_id": self.loan_id.id, "loan_id": self.loan_id.id,
"type": "in_invoice", "type": "in_invoice",
"partner_id": self.loan_id.partner_id.id, "partner_id": self.loan_id.partner_id.id,
"date_invoice": self.date, "invoice_date": self.date,
"account_id": partner.property_account_payable_id.id,
"journal_id": self.loan_id.journal_id.id, "journal_id": self.loan_id.journal_id.id,
"company_id": self.loan_id.company_id.id, "company_id": self.loan_id.company_id.id,
"invoice_line_ids": [(0, 0, vals) for vals in self.invoice_line_vals()], "invoice_line_ids": [(0, 0, vals) for vals in self.invoice_line_vals()],
@ -323,7 +317,6 @@ class AccountLoanLine(models.Model):
) )
return vals return vals
@api.multi
def generate_move(self): def generate_move(self):
""" """
Computes and post the moves of loans Computes and post the moves of loans
@ -341,29 +334,59 @@ class AccountLoanLine(models.Model):
res.append(move.id) res.append(move.id)
return res return res
@api.multi
def generate_invoice(self): def generate_invoice(self):
""" """
Computes invoices of leases Computes invoices of leases
:return: list of account.invoice generated :return: list of account.move generated
""" """
res = [] res = []
for record in self: for record in self:
if not record.invoice_ids: if not record.move_ids:
if record.loan_id.line_ids.filtered( if record.loan_id.line_ids.filtered(
lambda r: r.date < record.date and not r.invoice_ids lambda r: r.date < record.date and not r.move_ids
): ):
raise UserError(_("Some invoices must be created first")) raise UserError(_("Some invoices must be created first"))
invoice = self.env["account.invoice"].create(record.invoice_vals()) invoice = self.env["account.move"].create(record.invoice_vals())
res.append(invoice.id) res.append(invoice.id)
for line in invoice.invoice_line_ids: for line in invoice.invoice_line_ids:
line._set_taxes() line.tax_ids = line._get_computed_taxes()
invoice.compute_taxes() invoice.with_context(
check_move_validity=False
)._recompute_dynamic_lines(recompute_all_taxes=True)
invoice._check_balanced()
if (
record.long_term_loan_account_id
and record.long_term_principal_amount != 0
):
invoice.write({"line_ids": record._get_long_term_move_line_vals()})
if record.loan_id.post_invoice: if record.loan_id.post_invoice:
invoice.action_invoice_open() invoice.post()
return res return res
@api.multi def _get_long_term_move_line_vals(self):
return [
(
0,
0,
{
"account_id": self.loan_id.short_term_loan_account_id.id,
"credit": self.long_term_principal_amount,
"debit": 0,
"exclude_from_invoice_tab": True,
},
),
(
0,
0,
{
"account_id": self.long_term_loan_account_id.id,
"credit": 0,
"debit": self.long_term_principal_amount,
"exclude_from_invoice_tab": True,
},
),
]
def view_account_values(self): def view_account_values(self):
"""Shows the invoice if it is a leasing or the move if it is a loan""" """Shows the invoice if it is a leasing or the move if it is a loan"""
self.ensure_one() self.ensure_one()
@ -371,7 +394,6 @@ class AccountLoanLine(models.Model):
return self.view_account_invoices() return self.view_account_invoices()
return self.view_account_moves() return self.view_account_moves()
@api.multi
def view_process_values(self): def view_process_values(self):
"""Computes the annuity and returns the result""" """Computes the annuity and returns the result"""
self.ensure_one() self.ensure_one()
@ -381,7 +403,6 @@ class AccountLoanLine(models.Model):
self.generate_move() self.generate_move()
return self.view_account_values() return self.view_account_values()
@api.multi
def view_account_moves(self): def view_account_moves(self):
self.ensure_one() self.ensure_one()
action = self.env.ref("account.action_move_line_form") action = self.env.ref("account.action_move_line_form")
@ -397,18 +418,17 @@ class AccountLoanLine(models.Model):
result["res_id"] = self.move_ids.id result["res_id"] = self.move_ids.id
return result return result
@api.multi
def view_account_invoices(self): def view_account_invoices(self):
self.ensure_one() self.ensure_one()
action = self.env.ref("account.action_invoice_tree2") action = self.env.ref("account.action_move_out_invoice_type")
result = action.read()[0] result = action.read()[0]
result["context"] = { result["context"] = {
"default_loan_line_id": self.id, "default_loan_line_id": self.id,
"default_loan_id": self.loan_id.id, "default_loan_id": self.loan_id.id,
} }
result["domain"] = [("loan_line_id", "=", self.id), ("type", "=", "in_invoice")] result["domain"] = [("loan_line_id", "=", self.id), ("type", "=", "in_invoice")]
if len(self.invoice_ids) == 1: if len(self.move_ids) == 1:
res = self.env.ref("account.invoice.supplier.form", False) res = self.env.ref("account.view_move_form", False)
result["views"] = [(res and res.id or False, "form")] result["views"] = [(res and res.id or False, "form")]
result["res_id"] = self.invoice_ids.id result["res_id"] = self.move_ids.id
return result return result

View File

@ -1,7 +1,7 @@
# Copyright 2018 Creu Blanca # Copyright 2018 Creu Blanca
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models from odoo import fields, models
class AccountMove(models.Model): class AccountMove(models.Model):
@ -14,11 +14,10 @@ class AccountMove(models.Model):
"account.loan", readonly=True, store=True, ondelete="restrict", "account.loan", readonly=True, store=True, ondelete="restrict",
) )
@api.multi def post(self):
def post(self, invoice=False): res = super().post()
res = super().post(invoice=invoice)
for record in self: for record in self:
loan_line_id = record.loan_line_id or (invoice and invoice.loan_line_id) loan_line_id = record.loan_line_id
if loan_line_id: if loan_line_id:
if not record.loan_line_id: if not record.loan_line_id:
record.loan_line_id = loan_line_id record.loan_line_id = loan_line_id

View File

@ -7,15 +7,16 @@ from dateutil.relativedelta import relativedelta
from odoo import fields from odoo import fields
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.tests import TransactionCase from odoo.tests import TransactionCase, tagged
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
try: try:
import numpy import numpy_financial
except (ImportError, IOError) as err: except (ImportError, IOError) as err:
_logger.error(err) _logger.error(err)
@tagged("post_install", "-at_install")
class TestLoan(TransactionCase): class TestLoan(TransactionCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
@ -108,7 +109,7 @@ class TestLoan(TransactionCase):
self.assertEqual(len(loan.line_ids), periods) self.assertEqual(len(loan.line_ids), periods)
line = loan.line_ids.filtered(lambda r: r.sequence == 1) line = loan.line_ids.filtered(lambda r: r.sequence == 1)
self.assertAlmostEqual( self.assertAlmostEqual(
-numpy.pmt(1 / 100 / 12, 24, 10000), line.payment_amount, 2 -numpy_financial.pmt(1 / 100 / 12, 24, 10000), line.payment_amount, 2
) )
self.assertEqual(line.long_term_principal_amount, 0) self.assertEqual(line.long_term_principal_amount, 0)
loan.long_term_loan_account_id = self.lt_loan_account loan.long_term_loan_account_id = self.lt_loan_account
@ -120,14 +121,14 @@ class TestLoan(TransactionCase):
line = loan.line_ids.filtered(lambda r: r.sequence == 1) line = loan.line_ids.filtered(lambda r: r.sequence == 1)
self.assertTrue(line) self.assertTrue(line)
self.assertFalse(line.move_ids) self.assertFalse(line.move_ids)
self.assertFalse(line.invoice_ids)
wzd = self.env["account.loan.generate.wizard"].create({}) wzd = self.env["account.loan.generate.wizard"].create({})
action = wzd.run() action = wzd.run()
self.assertTrue(action) self.assertTrue(action)
self.assertFalse(wzd.run()) self.assertFalse(wzd.run())
self.assertTrue(line.move_ids) self.assertTrue(line.move_ids)
self.assertIn(line.move_ids.id, action["domain"][0][2]) self.assertIn(line.move_ids.id, action["domain"][0][2])
line.move_ids.post() self.assertTrue(line.move_ids)
self.assertEqual(line.move_ids.state, "posted")
with self.assertRaises(UserError): with self.assertRaises(UserError):
self.env["account.loan.pay.amount"].create( self.env["account.loan.pay.amount"].create(
{ {
@ -139,15 +140,15 @@ class TestLoan(TransactionCase):
).run() ).run()
with self.assertRaises(UserError): with self.assertRaises(UserError):
self.env["account.loan.pay.amount"].create( self.env["account.loan.pay.amount"].create(
{"loan_id": loan.id, "amount": amount, "fees": 100, "date": line.date,} {"loan_id": loan.id, "amount": amount, "fees": 100, "date": line.date}
).run() ).run()
with self.assertRaises(UserError): with self.assertRaises(UserError):
self.env["account.loan.pay.amount"].create( self.env["account.loan.pay.amount"].create(
{"loan_id": loan.id, "amount": 0, "fees": 100, "date": line.date,} {"loan_id": loan.id, "amount": 0, "fees": 100, "date": line.date}
).run() ).run()
with self.assertRaises(UserError): with self.assertRaises(UserError):
self.env["account.loan.pay.amount"].create( self.env["account.loan.pay.amount"].create(
{"loan_id": loan.id, "amount": -100, "fees": 100, "date": line.date,} {"loan_id": loan.id, "amount": -100, "fees": 100, "date": line.date}
).run() ).run()
def test_fixed_annuity_begin_loan(self): def test_fixed_annuity_begin_loan(self):
@ -158,7 +159,9 @@ class TestLoan(TransactionCase):
self.assertEqual(len(loan.line_ids), periods) self.assertEqual(len(loan.line_ids), periods)
line = loan.line_ids.filtered(lambda r: r.sequence == 1) line = loan.line_ids.filtered(lambda r: r.sequence == 1)
self.assertAlmostEqual( self.assertAlmostEqual(
-numpy.pmt(1 / 100 / 12, 24, 10000, when="begin"), line.payment_amount, 2 -numpy_financial.pmt(1 / 100 / 12, 24, 10000, when="begin"),
line.payment_amount,
2,
) )
self.assertEqual(line.long_term_principal_amount, 0) self.assertEqual(line.long_term_principal_amount, 0)
loan.long_term_loan_account_id = self.lt_loan_account loan.long_term_loan_account_id = self.lt_loan_account
@ -170,25 +173,25 @@ class TestLoan(TransactionCase):
line = loan.line_ids.filtered(lambda r: r.sequence == 1) line = loan.line_ids.filtered(lambda r: r.sequence == 1)
self.assertTrue(line) self.assertTrue(line)
self.assertFalse(line.move_ids) self.assertFalse(line.move_ids)
self.assertFalse(line.invoice_ids)
wzd = self.env["account.loan.generate.wizard"].create({}) wzd = self.env["account.loan.generate.wizard"].create({})
action = wzd.run() action = wzd.run()
self.assertTrue(action) self.assertTrue(action)
self.assertFalse(wzd.run()) self.assertFalse(wzd.run())
self.assertTrue(line.move_ids) self.assertTrue(line.move_ids)
self.assertIn(line.move_ids.id, action["domain"][0][2]) self.assertIn(line.move_ids.id, action["domain"][0][2])
line.move_ids.post() self.assertTrue(line.move_ids)
self.assertEqual(line.move_ids.state, "posted")
loan.rate = 2 loan.rate = 2
loan.compute_lines() loan.compute_lines()
line = loan.line_ids.filtered(lambda r: r.sequence == 1) line = loan.line_ids.filtered(lambda r: r.sequence == 1)
self.assertAlmostEqual( self.assertAlmostEqual(
-numpy.pmt(1 / 100 / 12, periods, amount, when="begin"), -numpy_financial.pmt(1 / 100 / 12, periods, amount, when="begin"),
line.payment_amount, line.payment_amount,
2, 2,
) )
line = loan.line_ids.filtered(lambda r: r.sequence == 2) line = loan.line_ids.filtered(lambda r: r.sequence == 2)
self.assertAlmostEqual( self.assertAlmostEqual(
-numpy.pmt( -numpy_financial.pmt(
2 / 100 / 12, periods - 1, line.pending_principal_amount, when="begin" 2 / 100 / 12, periods - 1, line.pending_principal_amount, when="begin"
), ),
line.payment_amount, line.payment_amount,
@ -206,7 +209,7 @@ class TestLoan(TransactionCase):
self.assertEqual(len(loan.line_ids), periods) self.assertEqual(len(loan.line_ids), periods)
line = loan.line_ids.filtered(lambda r: r.sequence == 1) line = loan.line_ids.filtered(lambda r: r.sequence == 1)
self.assertAlmostEqual( self.assertAlmostEqual(
-numpy.pmt(1 / 100 / 12, 24, 10000), line.payment_amount, 2 -numpy_financial.pmt(1 / 100 / 12, 24, 10000), line.payment_amount, 2
) )
self.assertEqual(line.long_term_principal_amount, 0) self.assertEqual(line.long_term_principal_amount, 0)
loan.long_term_loan_account_id = self.lt_loan_account loan.long_term_loan_account_id = self.lt_loan_account
@ -218,23 +221,25 @@ class TestLoan(TransactionCase):
line = loan.line_ids.filtered(lambda r: r.sequence == 1) line = loan.line_ids.filtered(lambda r: r.sequence == 1)
self.assertTrue(line) self.assertTrue(line)
self.assertFalse(line.move_ids) self.assertFalse(line.move_ids)
self.assertFalse(line.invoice_ids)
wzd = self.env["account.loan.generate.wizard"].create({}) wzd = self.env["account.loan.generate.wizard"].create({})
action = wzd.run() action = wzd.run()
self.assertTrue(action) self.assertTrue(action)
self.assertFalse(wzd.run()) self.assertFalse(wzd.run())
self.assertTrue(line.move_ids) self.assertTrue(line.move_ids)
self.assertIn(line.move_ids.id, action["domain"][0][2]) self.assertIn(line.move_ids.id, action["domain"][0][2])
line.move_ids.post() self.assertTrue(line.move_ids)
self.assertEqual(line.move_ids.state, "posted")
loan.rate = 2 loan.rate = 2
loan.compute_lines() loan.compute_lines()
line = loan.line_ids.filtered(lambda r: r.sequence == 1) line = loan.line_ids.filtered(lambda r: r.sequence == 1)
self.assertAlmostEqual( self.assertAlmostEqual(
-numpy.pmt(1 / 100 / 12, periods, amount), line.payment_amount, 2 -numpy_financial.pmt(1 / 100 / 12, periods, amount), line.payment_amount, 2
) )
line = loan.line_ids.filtered(lambda r: r.sequence == 2) line = loan.line_ids.filtered(lambda r: r.sequence == 2)
self.assertAlmostEqual( self.assertAlmostEqual(
-numpy.pmt(2 / 100 / 12, periods - 1, line.pending_principal_amount), -numpy_financial.pmt(
2 / 100 / 12, periods - 1, line.pending_principal_amount
),
line.payment_amount, line.payment_amount,
2, 2,
) )
@ -242,7 +247,7 @@ class TestLoan(TransactionCase):
with self.assertRaises(UserError): with self.assertRaises(UserError):
line.view_process_values() line.view_process_values()
def test_fixed_principal_loan(self): def test_fixed_principal_loan_leasing(self):
amount = 24000 amount = 24000
periods = 24 periods = 24
loan = self.create_loan("fixed-principal", amount, 1, periods) loan = self.create_loan("fixed-principal", amount, 1, periods)
@ -266,12 +271,18 @@ class TestLoan(TransactionCase):
self.assertFalse(line.has_moves) self.assertFalse(line.has_moves)
action = ( action = (
self.env["account.loan.generate.wizard"] self.env["account.loan.generate.wizard"]
.create({"date": fields.date.today(), "loan_type": "leasing",}) .create(
{
"date": fields.date.today() + relativedelta(days=1),
"loan_type": "leasing",
}
)
.run() .run()
) )
self.assertTrue(line.has_invoices) self.assertTrue(line.has_invoices)
self.assertFalse(line.has_moves) self.assertTrue(line.has_moves)
self.assertIn(line.invoice_ids.id, action["domain"][0][2]) self.assertIn(line.move_ids.id, action["domain"][0][2])
loan.refresh()
with self.assertRaises(UserError): with self.assertRaises(UserError):
self.env["account.loan.pay.amount"].create( self.env["account.loan.pay.amount"].create(
{ {
@ -291,17 +302,18 @@ class TestLoan(TransactionCase):
+ relativedelta(months=-1), + relativedelta(months=-1),
} }
).run() ).run()
line.invoice_ids.action_invoice_open() self.assertTrue(line.move_ids)
self.assertEqual(line.move_ids.state, "draft")
self.assertTrue(line.has_moves) self.assertTrue(line.has_moves)
line.move_ids.post()
self.assertEqual(line.move_ids.state, "posted")
self.assertIn( self.assertIn(
line.move_ids.id, line.move_ids.id,
self.env["account.move"].search(loan.view_account_moves()["domain"]).ids, self.env["account.move"].search(loan.view_account_moves()["domain"]).ids,
) )
self.assertEqual( self.assertEqual(
line.invoice_ids.id, line.move_ids.id,
self.env["account.invoice"] self.env["account.move"].search(loan.view_account_invoices()["domain"]).id,
.search(loan.view_account_invoices()["domain"])
.id,
) )
with self.assertRaises(UserError): with self.assertRaises(UserError):
self.env["account.loan.pay.amount"].create( self.env["account.loan.pay.amount"].create(
@ -333,7 +345,7 @@ class TestLoan(TransactionCase):
with self.assertRaises(UserError): with self.assertRaises(UserError):
line.view_process_values() line.view_process_values()
def test_fixed_principal_loan_auto_post(self): def test_fixed_principal_loan_auto_post_leasing(self):
amount = 24000 amount = 24000
periods = 24 periods = 24
loan = self.create_loan("fixed-principal", amount, 1, periods) loan = self.create_loan("fixed-principal", amount, 1, periods)
@ -355,7 +367,7 @@ class TestLoan(TransactionCase):
self.assertFalse(line.has_invoices) self.assertFalse(line.has_invoices)
self.assertFalse(line.has_moves) self.assertFalse(line.has_moves)
self.env["account.loan.generate.wizard"].create( self.env["account.loan.generate.wizard"].create(
{"date": fields.date.today(), "loan_type": "leasing",} {"date": fields.date.today(), "loan_type": "leasing"}
).run() ).run()
self.assertTrue(line.has_invoices) self.assertTrue(line.has_invoices)
self.assertTrue(line.has_moves) self.assertTrue(line.has_moves)
@ -383,9 +395,10 @@ class TestLoan(TransactionCase):
for line in loan.line_ids: for line in loan.line_ids:
self.assertEqual(loan.state, "posted") self.assertEqual(loan.state, "posted")
line.view_process_values() line.view_process_values()
line.move_ids.post() self.assertTrue(line.move_ids)
self.assertEqual(line.move_ids.state, "posted")
self.assertEqual(loan.state, "closed") self.assertEqual(loan.state, "closed")
loan.refresh()
self.assertEqual(loan.payment_amount - loan.interests_amount, amount) self.assertEqual(loan.payment_amount - loan.interests_amount, amount)
self.assertEqual(loan.pending_principal_amount, 0) self.assertEqual(loan.pending_principal_amount, 0)
@ -396,7 +409,8 @@ class TestLoan(TransactionCase):
self.post(loan) self.post(loan)
line = loan.line_ids.filtered(lambda r: r.sequence == 1) line = loan.line_ids.filtered(lambda r: r.sequence == 1)
line.view_process_values() line.view_process_values()
line.move_ids.post() self.assertTrue(line.move_ids)
self.assertEqual(line.move_ids.state, "posted")
pay = self.env["account.loan.pay.amount"].create( pay = self.env["account.loan.pay.amount"].create(
{"loan_id": loan.id, "amount": 0, "fees": 100, "date": line.date} {"loan_id": loan.id, "amount": 0, "fees": 100, "date": line.date}
) )

View File

@ -1,6 +1,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details. # Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models from odoo import fields, models
class AccountLoanGenerateWizard(models.TransientModel): class AccountLoanGenerateWizard(models.TransientModel):
@ -15,12 +15,12 @@ class AccountLoanGenerateWizard(models.TransientModel):
default=fields.Date.context_today, default=fields.Date.context_today,
) )
loan_type = fields.Selection( loan_type = fields.Selection(
[("leasing", "Leasings"), ("loan", "Loans"),], required=True, default="loan" [("leasing", "Leasings"), ("loan", "Loans")], required=True, default="loan"
) )
def run_leasing(self): def run_leasing(self):
created_ids = self.env["account.loan"].generate_leasing_entries(self.date) created_ids = self.env["account.loan"].generate_leasing_entries(self.date)
action = self.env.ref("account.action_invoice_tree2") action = self.env.ref("account.action_move_out_invoice_type")
result = action.read()[0] result = action.read()[0]
if len(created_ids) == 0: if len(created_ids) == 0:
return return
@ -36,7 +36,6 @@ class AccountLoanGenerateWizard(models.TransientModel):
result["domain"] = [("id", "in", created_ids)] result["domain"] = [("id", "in", created_ids)]
return result return result
@api.multi
def run(self): def run(self):
self.ensure_one() self.ensure_one()
if self.loan_type == "leasing": if self.loan_type == "leasing":

View File

@ -24,9 +24,9 @@ class AccountLoan(models.TransientModel):
def _onchange_cancel_loan(self): def _onchange_cancel_loan(self):
if self.cancel_loan: if self.cancel_loan:
self.amount = max( self.amount = max(
self.loan_id.line_ids.filtered( self.loan_id.line_ids.filtered(lambda r: not r.move_ids).mapped(
lambda r: not r.move_ids and not r.invoice_ids "pending_principal_amount"
).mapped("pending_principal_amount") )
) )
def new_line_vals(self, sequence): def new_line_vals(self, sequence):
@ -39,16 +39,15 @@ class AccountLoan(models.TransientModel):
"date": self.date, "date": self.date,
} }
@api.multi
def run(self): def run(self):
self.ensure_one() self.ensure_one()
if self.loan_id.is_leasing: if self.loan_id.is_leasing:
if self.loan_id.line_ids.filtered( if self.loan_id.line_ids.filtered(
lambda r: r.date < self.date and not r.invoice_ids lambda r: r.date <= self.date and not r.move_ids
): ):
raise UserError(_("Some invoices are not created")) raise UserError(_("Some invoices are not created"))
if self.loan_id.line_ids.filtered( if self.loan_id.line_ids.filtered(
lambda r: r.date > self.date and r.invoice_ids lambda r: r.date > self.date and r.move_ids
): ):
raise UserError(_("Some future invoices already exists")) raise UserError(_("Some future invoices already exists"))
if self.loan_id.line_ids.filtered( if self.loan_id.line_ids.filtered(
@ -63,6 +62,7 @@ class AccountLoan(models.TransientModel):
sequence = min(lines.mapped("sequence")) sequence = min(lines.mapped("sequence"))
for line in lines: for line in lines:
line.sequence += 1 line.sequence += 1
line.flush()
old_line = lines.filtered(lambda r: r.sequence == sequence + 1) old_line = lines.filtered(lambda r: r.sequence == sequence + 1)
pending = old_line.pending_principal_amount pending = old_line.pending_principal_amount
if self.loan_id.currency_id.compare_amounts(self.amount, pending) == 1: if self.loan_id.currency_id.compare_amounts(self.amount, pending) == 1:

View File

@ -10,13 +10,13 @@ class AccountLoanPost(models.TransientModel):
@api.model @api.model
def _default_journal_id(self): def _default_journal_id(self):
loan_id = self._context.get("default_loan_id") loan_id = self.env.context.get("default_loan_id")
if loan_id: if loan_id:
return self.env["account.loan"].browse(loan_id).journal_id.id return self.env["account.loan"].browse(loan_id).journal_id.id
@api.model @api.model
def _default_account_id(self): def _default_account_id(self):
loan_id = self._context.get("default_loan_id") loan_id = self.env.context.get("default_loan_id")
if loan_id: if loan_id:
loan = self.env["account.loan"].browse(loan_id) loan = self.env["account.loan"].browse(loan_id)
if loan.is_leasing: if loan.is_leasing:
@ -28,10 +28,10 @@ class AccountLoanPost(models.TransientModel):
loan_id = fields.Many2one("account.loan", required=True, readonly=True,) loan_id = fields.Many2one("account.loan", required=True, readonly=True,)
journal_id = fields.Many2one( journal_id = fields.Many2one(
"account.journal", required=True, default=_default_journal_id "account.journal", required=True, default=lambda r: r._default_journal_id()
) )
account_id = fields.Many2one( account_id = fields.Many2one(
"account.account", required=True, default=_default_account_id "account.account", required=True, default=lambda r: r._default_account_id()
) )
def move_line_vals(self): def move_line_vals(self):
@ -82,7 +82,6 @@ class AccountLoanPost(models.TransientModel):
"line_ids": [(0, 0, vals) for vals in self.move_line_vals()], "line_ids": [(0, 0, vals) for vals in self.move_line_vals()],
} }
@api.multi
def run(self): def run(self):
self.ensure_one() self.ensure_one()
if self.loan_id.state != "draft": if self.loan_id.state != "draft":