diff --git a/account_loan/README.rst b/account_loan/README.rst index 561fc7c66..9299b0073 100644 --- a/account_loan/README.rst +++ b/account_loan/README.rst @@ -14,13 +14,13 @@ Account Loan management :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--financial--tools-lightgray.png?logo=github - :target: https://github.com/OCA/account-financial-tools/tree/12.0/account_loan + :target: https://github.com/OCA/account-financial-tools/tree/13.0/account_loan :alt: OCA/account-financial-tools .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/account-financial-tools-12-0/account-financial-tools-12-0-account_loan + :target: https://translation.odoo-community.org/projects/account-financial-tools-13-0/account-financial-tools-13-0-account_loan :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/92/12.0 + :target: https://runbot.odoo-community.org/runbot/92/13.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -71,7 +71,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -103,6 +103,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/account-financial-tools `_ project on GitHub. +This module is part of the `OCA/account-financial-tools `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_loan/__manifest__.py b/account_loan/__manifest__.py index f272f42c9..a049a4b68 100644 --- a/account_loan/__manifest__.py +++ b/account_loan/__manifest__.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { "name": "Account Loan management", - "version": "12.0.1.1.0", + "version": "13.0.1.1.0", "author": "Creu Blanca,Odoo Community Association (OCA)", "website": "http://github.com/OCA/account-financial-tools", "license": "AGPL-3", @@ -19,5 +19,5 @@ "views/account_move_view.xml", ], "installable": True, - "external_dependencies": {"python": ["numpy",],}, + "external_dependencies": {"python": ["numpy", "numpy-financial<=1.0.0"]}, } diff --git a/account_loan/i18n/account_loan.pot b/account_loan/i18n/account_loan.pot index 6176d714f..3b22be62a 100644 --- a/account_loan/i18n/account_loan.pot +++ b/account_loan/i18n/account_loan.pot @@ -1,12 +1,12 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: -# * account_loan +# * account_loan # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 12.0\n" +"Project-Id-Version: Odoo Server 13.0\n" "Report-Msgid-Bugs-To: \n" -"Last-Translator: <>\n" +"Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -54,25 +54,30 @@ msgstr "" msgid "Activities" msgstr "" +#. module: account_loan +#: model:ir.model.fields,field_description:account_loan.field_account_loan__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "" + #. module: account_loan #: model:ir.model.fields,field_description:account_loan.field_account_loan__activity_state msgid "Activity State" msgstr "" #. module: account_loan -#: code:addons/account_loan/wizard/account_loan_pay_amount.py:82 +#: code:addons/account_loan/wizard/account_loan_pay_amount.py:0 #, python-format msgid "Amount cannot be bigger than debt" msgstr "" #. module: account_loan -#: code:addons/account_loan/wizard/account_loan_pay_amount.py:84 +#: code:addons/account_loan/wizard/account_loan_pay_amount.py:0 #, python-format msgid "Amount cannot be less than zero" msgstr "" #. module: account_loan -#: code:addons/account_loan/model/account_loan_line.py:186 +#: code:addons/account_loan/model/account_loan_line.py:0 #, python-format msgid "Amount cannot be recomputed if moves or invoices exists already" msgstr "" @@ -125,17 +130,19 @@ msgid "Cancel Loan" msgstr "" #. module: account_loan -#: selection:account.loan,state:0 +#: model:ir.model.fields.selection,name:account_loan.selection__account_loan__state__cancelled msgid "Cancelled" msgstr "" #. module: account_loan #: model:ir.model.fields,help:account_loan.field_account_loan_generate_wizard__date -msgid "Choose the period for which you want to automatically post the depreciation lines of running assets" +msgid "" +"Choose the period for which you want to automatically post the depreciation " +"lines of running assets" msgstr "" #. module: account_loan -#: selection:account.loan,state:0 +#: model:ir.model.fields.selection,name:account_loan.selection__account_loan__state__closed msgid "Closed" msgstr "" @@ -205,12 +212,12 @@ msgid "Display Name" msgstr "" #. module: account_loan -#: selection:account.loan,state:0 +#: model:ir.model.fields.selection,name:account_loan.selection__account_loan__state__draft msgid "Draft" msgstr "" #. module: account_loan -#: selection:account.loan,rate_type:0 +#: model:ir.model.fields.selection,name:account_loan.selection__account_loan__rate_type__ear msgid "EAR" msgstr "" @@ -225,12 +232,12 @@ msgid "Fixed Amount" msgstr "" #. module: account_loan -#: selection:account.loan,loan_type:0 +#: model:ir.model.fields.selection,name:account_loan.selection__account_loan__loan_type__fixed-annuity msgid "Fixed Annuity" msgstr "" #. module: account_loan -#: selection:account.loan,loan_type:0 +#: model:ir.model.fields.selection,name:account_loan.selection__account_loan__loan_type__fixed-annuity-begin msgid "Fixed Annuity Begin" msgstr "" @@ -245,7 +252,7 @@ msgid "Fixed Periods" msgstr "" #. module: account_loan -#: selection:account.loan,loan_type:0 +#: model:ir.model.fields.selection,name:account_loan.selection__account_loan__loan_type__fixed-principal msgid "Fixed Principal" msgstr "" @@ -295,17 +302,24 @@ msgid "ID" msgstr "" #. module: account_loan -#: model:ir.model.fields,help:account_loan.field_account_loan__message_unread -msgid "If checked new messages require your attention." +#: model:ir.model.fields,field_description:account_loan.field_account_loan__activity_exception_icon +msgid "Icon" +msgstr "" + +#. module: account_loan +#: model:ir.model.fields,help:account_loan.field_account_loan__activity_exception_icon +msgid "Icon to indicate an exception activity." msgstr "" #. module: account_loan #: model:ir.model.fields,help:account_loan.field_account_loan__message_needaction +#: model:ir.model.fields,help:account_loan.field_account_loan__message_unread msgid "If checked, new messages require your attention." msgstr "" #. module: account_loan #: model:ir.model.fields,help:account_loan.field_account_loan__message_has_error +#: model:ir.model.fields,help:account_loan.field_account_loan__message_has_sms_error msgid "If checked, some messages have a delivery error." msgstr "" @@ -324,12 +338,6 @@ msgstr "" msgid "Interests account" msgstr "" -#. module: account_loan -#: model:ir.model,name:account_loan.model_account_invoice -#: model:ir.model.fields,field_description:account_loan.field_account_loan_line__invoice_ids -msgid "Invoice" -msgstr "" - #. module: account_loan #: model_terms:ir.ui.view,arch_db:account_loan.account_loan_form msgid "Invoices" @@ -410,7 +418,7 @@ msgid "Leasing" msgstr "" #. module: account_loan -#: selection:account.loan.generate.wizard,loan_type:0 +#: model:ir.model.fields.selection,name:account_loan.selection__account_loan_generate_wizard__loan_type__leasing msgid "Leasings" msgstr "" @@ -426,7 +434,6 @@ msgstr "" #. module: account_loan #: model:ir.model,name:account_loan.model_account_loan -#: model:ir.model.fields,field_description:account_loan.field_account_invoice__loan_id #: model:ir.model.fields,field_description:account_loan.field_account_loan_line__loan_id #: model:ir.model.fields,field_description:account_loan.field_account_loan_pay_amount__loan_id #: model:ir.model.fields,field_description:account_loan.field_account_loan_post__loan_id @@ -441,7 +448,6 @@ msgid "Loan Amount" msgstr "" #. module: account_loan -#: model:ir.model.fields,field_description:account_loan.field_account_invoice__loan_line_id #: model:ir.model.fields,field_description:account_loan.field_account_move__loan_line_id msgid "Loan Line" msgstr "" @@ -464,7 +470,7 @@ msgid "Loan items" msgstr "" #. module: account_loan -#: sql_constraint:account.loan:0 +#: model:ir.model.constraint,message:account_loan.constraint_account_loan_name_uniq msgid "Loan name must be unique" msgstr "" @@ -484,8 +490,8 @@ msgid "Loan product" msgstr "" #. module: account_loan -#: selection:account.loan.generate.wizard,loan_type:0 #: model:ir.actions.act_window,name:account_loan.account_loan_action +#: model:ir.model.fields.selection,name:account_loan.selection__account_loan_generate_wizard__loan_type__loan #: model:ir.ui.menu,name:account_loan.account_loan_menu #: model_terms:ir.ui.view,arch_db:account_loan.account_loan_tree msgid "Loans" @@ -566,7 +572,7 @@ msgid "Next Activity Type" msgstr "" #. module: account_loan -#: selection:account.loan,rate_type:0 +#: model:ir.model.fields.selection,name:account_loan.selection__account_loan__rate_type__napr msgid "Nominal APR" msgstr "" @@ -577,7 +583,7 @@ msgstr "" #. module: account_loan #: model:ir.model.fields,field_description:account_loan.field_account_loan__message_has_error_counter -msgid "Number of error" +msgid "Number of errors" msgstr "" #. module: account_loan @@ -601,21 +607,16 @@ msgid "Number of unread messages" msgstr "" #. module: account_loan -#: selection:account.loan,loan_type:0 +#: model:ir.model.fields.selection,name:account_loan.selection__account_loan__loan_type__interest msgid "Only interest" msgstr "" #. module: account_loan -#: code:addons/account_loan/wizard/account_loan_post.py:91 +#: code:addons/account_loan/wizard/account_loan_post.py:0 #, python-format msgid "Only loans in draft state can be posted" msgstr "" -#. module: account_loan -#: selection:account.loan,activity_state:0 -msgid "Overdue" -msgstr "" - #. module: account_loan #: model:ir.actions.act_window,name:account_loan.account_loan_pay_amount_action #: model_terms:ir.ui.view,arch_db:account_loan.account_loan_form @@ -652,7 +653,9 @@ msgstr "" #. module: account_loan #: model:ir.model.fields,help:account_loan.field_account_loan_line__long_term_pending_principal_amount -msgid "Pending amount of the loan before the payment that will not be payed in, at least, 12 months" +msgid "" +"Pending amount of the loan before the payment that will not be payed in, at " +"least, 12 months" msgstr "" #. module: account_loan @@ -665,11 +668,6 @@ msgstr "" msgid "Periods" msgstr "" -#. module: account_loan -#: selection:account.loan,activity_state:0 -msgid "Planned" -msgstr "" - #. module: account_loan #: model_terms:ir.ui.view,arch_db:account_loan.account_loan_form msgid "Post" @@ -686,7 +684,7 @@ msgid "Post loan" msgstr "" #. module: account_loan -#: selection:account.loan,state:0 +#: model:ir.model.fields.selection,name:account_loan.selection__account_loan__state__posted msgid "Posted" msgstr "" @@ -702,12 +700,16 @@ msgstr "" #. module: account_loan #: model:ir.model.fields,help:account_loan.field_account_loan__interests_product_id -msgid "Product where the amount of interests will be assigned when the invoice is created" +msgid "" +"Product where the amount of interests will be assigned when the invoice is " +"created" msgstr "" #. module: account_loan #: model:ir.model.fields,help:account_loan.field_account_loan__product_id -msgid "Product where the amount of the loan will be assigned when the invoice is created" +msgid "" +"Product where the amount of the loan will be assigned when the invoice is " +"created" msgstr "" #. module: account_loan @@ -727,7 +729,7 @@ msgid "Rate Type" msgstr "" #. module: account_loan -#: selection:account.loan,rate_type:0 +#: model:ir.model.fields.selection,name:account_loan.selection__account_loan__rate_type__real msgid "Real rate" msgstr "" @@ -743,7 +745,9 @@ msgstr "" #. module: account_loan #: model:ir.model.fields,help:account_loan.field_account_loan__residual_amount -msgid "Residual amount of the lease that must be payed on the end in order to acquire the asset" +msgid "" +"Residual amount of the lease that must be payed on the end in order to " +"acquire the asset" msgstr "" #. module: account_loan @@ -763,13 +767,18 @@ msgstr "" msgid "Run" msgstr "" +#. module: account_loan +#: model:ir.model.fields,field_description:account_loan.field_account_loan__message_has_sms_error +msgid "SMS Delivery error" +msgstr "" + #. module: account_loan #: model:ir.model.fields,field_description:account_loan.field_account_loan_line__sequence msgid "Sequence" msgstr "" #. module: account_loan -#: sql_constraint:account.loan.line:0 +#: model:ir.model.constraint,message:account_loan.constraint_account_loan_line_sequence_loan msgid "Sequence must be unique in a loan" msgstr "" @@ -779,37 +788,37 @@ msgid "Short term account" msgstr "" #. module: account_loan -#: code:addons/account_loan/wizard/account_loan_pay_amount.py:65 +#: code:addons/account_loan/wizard/account_loan_pay_amount.py:0 #, python-format msgid "Some future invoices already exists" msgstr "" #. module: account_loan -#: code:addons/account_loan/wizard/account_loan_pay_amount.py:73 +#: code:addons/account_loan/wizard/account_loan_pay_amount.py:0 #, python-format msgid "Some future moves already exists" msgstr "" #. module: account_loan -#: code:addons/account_loan/wizard/account_loan_pay_amount.py:61 +#: code:addons/account_loan/wizard/account_loan_pay_amount.py:0 #, python-format msgid "Some invoices are not created" msgstr "" #. module: account_loan -#: code:addons/account_loan/model/account_loan_line.py:358 +#: code:addons/account_loan/model/account_loan_line.py:0 #, python-format msgid "Some invoices must be created first" msgstr "" #. module: account_loan -#: code:addons/account_loan/wizard/account_loan_pay_amount.py:69 +#: code:addons/account_loan/wizard/account_loan_pay_amount.py:0 #, python-format msgid "Some moves are not created" msgstr "" #. module: account_loan -#: code:addons/account_loan/model/account_loan_line.py:340 +#: code:addons/account_loan/model/account_loan_line.py:0 #, python-format msgid "Some moves must be created first" msgstr "" @@ -837,17 +846,13 @@ msgstr "" #. module: account_loan #: model:ir.model.fields,help:account_loan.field_account_loan__activity_state -msgid "Status based on activities\n" +msgid "" +"Status based on activities\n" "Overdue: Due date is already passed\n" "Today: Activity date is today\n" "Planned: Future activities." msgstr "" -#. module: account_loan -#: selection:account.loan,activity_state:0 -msgid "Today" -msgstr "" - #. module: account_loan #: model:ir.model.fields,help:account_loan.field_account_loan_line__payment_amount msgid "Total amount that will be payed (Annuity)" @@ -873,6 +878,11 @@ msgstr "" msgid "Total payments" msgstr "" +#. module: account_loan +#: model:ir.model.fields,help:account_loan.field_account_loan__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "" + #. module: account_loan #: model:ir.model.fields,field_description:account_loan.field_account_loan__message_unread msgid "Unread Messages" @@ -900,7 +910,9 @@ msgstr "" #. module: account_loan #: model:ir.model.fields,help:account_loan.field_account_loan__round_on_end -msgid "When checked, the differences will be applied on the last period, if it is unchecked, the annuity will be recalculated on each period." +msgid "" +"When checked, the differences will be applied on the last period, if it is " +"unchecked, the annuity will be recalculated on each period." msgstr "" #. module: account_loan @@ -914,4 +926,3 @@ msgstr "" #: model_terms:ir.ui.view,arch_db:account_loan.account_loan_post_form msgid "or" msgstr "" - diff --git a/account_loan/migrations/13.0.1.1.0/post-migration.py b/account_loan/migrations/13.0.1.1.0/post-migration.py new file mode 100644 index 000000000..0854e860a --- /dev/null +++ b/account_loan/migrations/13.0.1.1.0/post-migration.py @@ -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""", + ) diff --git a/account_loan/model/__init__.py b/account_loan/model/__init__.py index ad26bdeef..d07b9f2fb 100644 --- a/account_loan/model/__init__.py +++ b/account_loan/model/__init__.py @@ -1,7 +1,6 @@ # Copyright 2018 Creu Blanca # 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_line from . import account_move diff --git a/account_loan/model/account_invoice.py b/account_loan/model/account_invoice.py deleted file mode 100644 index c60e86aa0..000000000 --- a/account_loan/model/account_invoice.py +++ /dev/null @@ -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 diff --git a/account_loan/model/account_loan.py b/account_loan/model/account_loan.py index 5a4fe4fc0..6917549db 100644 --- a/account_loan/model/account_loan.py +++ b/account_loan/model/account_loan.py @@ -11,7 +11,7 @@ from odoo import api, fields, models _logger = logging.getLogger(__name__) try: - import numpy + import numpy_financial except (ImportError, IOError) as err: _logger.debug(err) @@ -96,7 +96,7 @@ class AccountLoan(models.Model): help="Real rate that will be applied on each period", ) rate_type = fields.Selection( - [("napr", "Nominal APR"), ("ear", "EAR"), ("real", "Real rate"),], + [("napr", "Nominal APR"), ("ear", "EAR"), ("real", "Real rate")], required=True, help="Method of computation of the applied rate", default="napr", @@ -253,7 +253,7 @@ class AccountLoan(models.Model): for record in self: if record.loan_type == "fixed-annuity": record.fixed_amount = -record.currency_id.round( - numpy.pmt( + numpy_financial.pmt( record.loan_rate() / 100, record.fixed_periods, record.fixed_loan_amount, @@ -262,7 +262,7 @@ class AccountLoan(models.Model): ) elif record.loan_type == "fixed-annuity-begin": record.fixed_amount = -record.currency_id.round( - numpy.pmt( + numpy_financial.pmt( record.loan_rate() / 100, record.fixed_periods, record.fixed_loan_amount, @@ -341,7 +341,6 @@ class AccountLoan(models.Model): vals["name"] = self.get_default_name(vals) return super().create(vals) - @api.multi def post(self): self.ensure_one() if not self.start_date: @@ -349,11 +348,9 @@ class AccountLoan(models.Model): self.compute_draft_lines() self.write({"state": "posted"}) - @api.multi def close(self): self.write({"state": "closed"}) - @api.multi def compute_lines(self): self.ensure_one() if self.state == "draft": @@ -407,7 +404,6 @@ class AccountLoan(models.Model): "rate": self.rate_period, } - @api.multi def compute_draft_lines(self): self.ensure_one() self.fixed_periods = self.periods @@ -431,7 +427,6 @@ class AccountLoan(models.Model): if self.long_term_loan_account_id: self.check_long_term_principal_amount() - @api.multi def view_account_moves(self): self.ensure_one() action = self.env.ref("account.action_move_line_form") @@ -439,10 +434,9 @@ class AccountLoan(models.Model): result["domain"] = [("loan_id", "=", self.id)] return result - @api.multi def view_account_invoices(self): 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["domain"] = [("loan_id", "=", self.id), ("type", "=", "in_invoice")] return result @@ -471,6 +465,6 @@ class AccountLoan(models.Model): [("state", "=", "posted"), ("is_leasing", "=", True)] ): 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() return res diff --git a/account_loan/model/account_loan_line.py b/account_loan/model/account_loan_line.py index 9ff568172..075340a30 100644 --- a/account_loan/model/account_loan_line.py +++ b/account_loan/model/account_loan_line.py @@ -8,7 +8,7 @@ from odoo.exceptions import UserError _logger = logging.getLogger(__name__) try: - import numpy + import numpy_financial except (ImportError, IOError) as err: _logger.error(err) @@ -90,7 +90,6 @@ class AccountLoanLine(models.Model): ) move_ids = fields.One2many("account.move", inverse_name="loan_line_id",) 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") _sql_constraints = [ ( @@ -105,10 +104,10 @@ class AccountLoanLine(models.Model): for record in self: record.has_moves = bool(record.move_ids) - @api.depends("invoice_ids") + @api.depends("move_ids") def _compute_has_invoices(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") def _compute_name(self): @@ -146,7 +145,7 @@ class AccountLoanLine(models.Model): return self.loan_id.fixed_amount if self.loan_type == "fixed-annuity": return self.currency_id.round( - -numpy.pmt( + -numpy_financial.pmt( self.loan_id.loan_rate() / 100, self.loan_id.periods - self.sequence + 1, self.pending_principal_amount, @@ -157,7 +156,7 @@ class AccountLoanLine(models.Model): return self.loan_id.fixed_amount if self.loan_type == "fixed-annuity-begin": return self.currency_id.round( - -numpy.pmt( + -numpy_financial.pmt( self.loan_id.loan_rate() / 100, self.loan_id.periods - self.sequence + 1, self.pending_principal_amount, @@ -168,7 +167,7 @@ class AccountLoanLine(models.Model): def check_amount(self): """Recompute amounts if the annuity has not been processed""" - if self.move_ids or self.invoice_ids: + if self.move_ids: raise UserError( _("Amount cannot be recomputed if moves or invoices exists " "already") ) @@ -192,7 +191,7 @@ class AccountLoanLine(models.Model): def compute_interest(self): if self.loan_type == "fixed-annuity-begin": - return -numpy.ipmt( + return -numpy_financial.ipmt( self.loan_id.loan_rate() / 100, 2, 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 - @api.multi def check_move_amount(self): """ Changes the amounts of the annuity once the move is posted @@ -286,16 +284,12 @@ class AccountLoanLine(models.Model): return vals def invoice_vals(self): - partner = self.loan_id.partner_id.with_context( - force_company=self.loan_id.company_id.id - ) return { "loan_line_id": self.id, "loan_id": self.loan_id.id, "type": "in_invoice", "partner_id": self.loan_id.partner_id.id, - "date_invoice": self.date, - "account_id": partner.property_account_payable_id.id, + "invoice_date": self.date, "journal_id": self.loan_id.journal_id.id, "company_id": self.loan_id.company_id.id, "invoice_line_ids": [(0, 0, vals) for vals in self.invoice_line_vals()], @@ -323,7 +317,6 @@ class AccountLoanLine(models.Model): ) return vals - @api.multi def generate_move(self): """ Computes and post the moves of loans @@ -341,29 +334,59 @@ class AccountLoanLine(models.Model): res.append(move.id) return res - @api.multi def generate_invoice(self): """ Computes invoices of leases - :return: list of account.invoice generated + :return: list of account.move generated """ res = [] for record in self: - if not record.invoice_ids: + if not record.move_ids: 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")) - invoice = self.env["account.invoice"].create(record.invoice_vals()) + invoice = self.env["account.move"].create(record.invoice_vals()) res.append(invoice.id) for line in invoice.invoice_line_ids: - line._set_taxes() - invoice.compute_taxes() + line.tax_ids = line._get_computed_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: - invoice.action_invoice_open() + invoice.post() 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): """Shows the invoice if it is a leasing or the move if it is a loan""" self.ensure_one() @@ -371,7 +394,6 @@ class AccountLoanLine(models.Model): return self.view_account_invoices() return self.view_account_moves() - @api.multi def view_process_values(self): """Computes the annuity and returns the result""" self.ensure_one() @@ -381,7 +403,6 @@ class AccountLoanLine(models.Model): self.generate_move() return self.view_account_values() - @api.multi def view_account_moves(self): self.ensure_one() action = self.env.ref("account.action_move_line_form") @@ -397,18 +418,17 @@ class AccountLoanLine(models.Model): result["res_id"] = self.move_ids.id return result - @api.multi def view_account_invoices(self): 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["context"] = { "default_loan_line_id": self.id, "default_loan_id": self.loan_id.id, } result["domain"] = [("loan_line_id", "=", self.id), ("type", "=", "in_invoice")] - if len(self.invoice_ids) == 1: - res = self.env.ref("account.invoice.supplier.form", False) + if len(self.move_ids) == 1: + res = self.env.ref("account.view_move_form", False) 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 diff --git a/account_loan/model/account_move.py b/account_loan/model/account_move.py index 2cbb6e766..1691397fc 100644 --- a/account_loan/model/account_move.py +++ b/account_loan/model/account_move.py @@ -1,7 +1,7 @@ # Copyright 2018 Creu Blanca # 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): @@ -14,11 +14,10 @@ class AccountMove(models.Model): "account.loan", readonly=True, store=True, ondelete="restrict", ) - @api.multi - def post(self, invoice=False): - res = super().post(invoice=invoice) + def post(self): + res = super().post() 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 not record.loan_line_id: record.loan_line_id = loan_line_id diff --git a/account_loan/static/description/index.html b/account_loan/static/description/index.html index 16f54a526..604a55f20 100644 --- a/account_loan/static/description/index.html +++ b/account_loan/static/description/index.html @@ -367,7 +367,7 @@ ul.auto-toc { !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/account-financial-tools Translate me on Weblate Try me on Runbot

+

Beta License: AGPL-3 OCA/account-financial-tools Translate me on Weblate Try me on Runbot

This module extends the functionality of accounting to support loans. It will create automatically moves or invoices for loans. Moreover, you can check the pending amount to be paid and reduce the debt.

@@ -420,7 +420,7 @@ leases before a selected date

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

@@ -446,7 +446,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

-

This module is part of the OCA/account-financial-tools project on GitHub.

+

This module is part of the OCA/account-financial-tools project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

diff --git a/account_loan/tests/test_loan.py b/account_loan/tests/test_loan.py index 0671c9ad1..3fbb15257 100644 --- a/account_loan/tests/test_loan.py +++ b/account_loan/tests/test_loan.py @@ -7,15 +7,16 @@ from dateutil.relativedelta import relativedelta from odoo import fields from odoo.exceptions import UserError -from odoo.tests import TransactionCase +from odoo.tests import TransactionCase, tagged _logger = logging.getLogger(__name__) try: - import numpy + import numpy_financial except (ImportError, IOError) as err: _logger.error(err) +@tagged("post_install", "-at_install") class TestLoan(TransactionCase): def setUp(self): super().setUp() @@ -108,7 +109,7 @@ class TestLoan(TransactionCase): self.assertEqual(len(loan.line_ids), periods) line = loan.line_ids.filtered(lambda r: r.sequence == 1) 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) 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) self.assertTrue(line) self.assertFalse(line.move_ids) - self.assertFalse(line.invoice_ids) wzd = self.env["account.loan.generate.wizard"].create({}) action = wzd.run() self.assertTrue(action) self.assertFalse(wzd.run()) self.assertTrue(line.move_ids) 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): self.env["account.loan.pay.amount"].create( { @@ -139,15 +140,15 @@ class TestLoan(TransactionCase): ).run() with self.assertRaises(UserError): 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() with self.assertRaises(UserError): 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() with self.assertRaises(UserError): 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() def test_fixed_annuity_begin_loan(self): @@ -158,7 +159,9 @@ class TestLoan(TransactionCase): self.assertEqual(len(loan.line_ids), periods) line = loan.line_ids.filtered(lambda r: r.sequence == 1) 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) 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) self.assertTrue(line) self.assertFalse(line.move_ids) - self.assertFalse(line.invoice_ids) wzd = self.env["account.loan.generate.wizard"].create({}) action = wzd.run() self.assertTrue(action) self.assertFalse(wzd.run()) self.assertTrue(line.move_ids) 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.compute_lines() line = loan.line_ids.filtered(lambda r: r.sequence == 1) self.assertAlmostEqual( - -numpy.pmt(1 / 100 / 12, periods, amount, when="begin"), + -numpy_financial.pmt(1 / 100 / 12, periods, amount, when="begin"), line.payment_amount, 2, ) line = loan.line_ids.filtered(lambda r: r.sequence == 2) self.assertAlmostEqual( - -numpy.pmt( + -numpy_financial.pmt( 2 / 100 / 12, periods - 1, line.pending_principal_amount, when="begin" ), line.payment_amount, @@ -206,7 +209,7 @@ class TestLoan(TransactionCase): self.assertEqual(len(loan.line_ids), periods) line = loan.line_ids.filtered(lambda r: r.sequence == 1) 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) 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) self.assertTrue(line) self.assertFalse(line.move_ids) - self.assertFalse(line.invoice_ids) wzd = self.env["account.loan.generate.wizard"].create({}) action = wzd.run() self.assertTrue(action) self.assertFalse(wzd.run()) self.assertTrue(line.move_ids) 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.compute_lines() line = loan.line_ids.filtered(lambda r: r.sequence == 1) 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) 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, 2, ) @@ -242,7 +247,7 @@ class TestLoan(TransactionCase): with self.assertRaises(UserError): line.view_process_values() - def test_fixed_principal_loan(self): + def test_fixed_principal_loan_leasing(self): amount = 24000 periods = 24 loan = self.create_loan("fixed-principal", amount, 1, periods) @@ -266,12 +271,18 @@ class TestLoan(TransactionCase): self.assertFalse(line.has_moves) action = ( 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() ) self.assertTrue(line.has_invoices) - self.assertFalse(line.has_moves) - self.assertIn(line.invoice_ids.id, action["domain"][0][2]) + self.assertTrue(line.has_moves) + self.assertIn(line.move_ids.id, action["domain"][0][2]) + loan.refresh() with self.assertRaises(UserError): self.env["account.loan.pay.amount"].create( { @@ -291,17 +302,18 @@ class TestLoan(TransactionCase): + relativedelta(months=-1), } ).run() - line.invoice_ids.action_invoice_open() + self.assertTrue(line.move_ids) + self.assertEqual(line.move_ids.state, "draft") self.assertTrue(line.has_moves) + line.move_ids.post() + self.assertEqual(line.move_ids.state, "posted") self.assertIn( line.move_ids.id, self.env["account.move"].search(loan.view_account_moves()["domain"]).ids, ) self.assertEqual( - line.invoice_ids.id, - self.env["account.invoice"] - .search(loan.view_account_invoices()["domain"]) - .id, + line.move_ids.id, + self.env["account.move"].search(loan.view_account_invoices()["domain"]).id, ) with self.assertRaises(UserError): self.env["account.loan.pay.amount"].create( @@ -333,7 +345,7 @@ class TestLoan(TransactionCase): with self.assertRaises(UserError): line.view_process_values() - def test_fixed_principal_loan_auto_post(self): + def test_fixed_principal_loan_auto_post_leasing(self): amount = 24000 periods = 24 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_moves) self.env["account.loan.generate.wizard"].create( - {"date": fields.date.today(), "loan_type": "leasing",} + {"date": fields.date.today(), "loan_type": "leasing"} ).run() self.assertTrue(line.has_invoices) self.assertTrue(line.has_moves) @@ -383,9 +395,10 @@ class TestLoan(TransactionCase): for line in loan.line_ids: self.assertEqual(loan.state, "posted") 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") - + loan.refresh() self.assertEqual(loan.payment_amount - loan.interests_amount, amount) self.assertEqual(loan.pending_principal_amount, 0) @@ -396,7 +409,8 @@ class TestLoan(TransactionCase): self.post(loan) line = loan.line_ids.filtered(lambda r: r.sequence == 1) 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( {"loan_id": loan.id, "amount": 0, "fees": 100, "date": line.date} ) diff --git a/account_loan/wizard/account_loan_generate_entries.py b/account_loan/wizard/account_loan_generate_entries.py index c749a18af..e34208867 100644 --- a/account_loan/wizard/account_loan_generate_entries.py +++ b/account_loan/wizard/account_loan_generate_entries.py @@ -1,6 +1,6 @@ # 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): @@ -15,12 +15,12 @@ class AccountLoanGenerateWizard(models.TransientModel): default=fields.Date.context_today, ) loan_type = fields.Selection( - [("leasing", "Leasings"), ("loan", "Loans"),], required=True, default="loan" + [("leasing", "Leasings"), ("loan", "Loans")], required=True, default="loan" ) def run_leasing(self): 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] if len(created_ids) == 0: return @@ -36,7 +36,6 @@ class AccountLoanGenerateWizard(models.TransientModel): result["domain"] = [("id", "in", created_ids)] return result - @api.multi def run(self): self.ensure_one() if self.loan_type == "leasing": diff --git a/account_loan/wizard/account_loan_pay_amount.py b/account_loan/wizard/account_loan_pay_amount.py index 802f0f120..91089398d 100644 --- a/account_loan/wizard/account_loan_pay_amount.py +++ b/account_loan/wizard/account_loan_pay_amount.py @@ -24,9 +24,9 @@ class AccountLoan(models.TransientModel): def _onchange_cancel_loan(self): if self.cancel_loan: self.amount = max( - self.loan_id.line_ids.filtered( - lambda r: not r.move_ids and not r.invoice_ids - ).mapped("pending_principal_amount") + self.loan_id.line_ids.filtered(lambda r: not r.move_ids).mapped( + "pending_principal_amount" + ) ) def new_line_vals(self, sequence): @@ -39,16 +39,15 @@ class AccountLoan(models.TransientModel): "date": self.date, } - @api.multi def run(self): self.ensure_one() if self.loan_id.is_leasing: 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")) 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")) if self.loan_id.line_ids.filtered( @@ -63,6 +62,7 @@ class AccountLoan(models.TransientModel): sequence = min(lines.mapped("sequence")) for line in lines: line.sequence += 1 + line.flush() old_line = lines.filtered(lambda r: r.sequence == sequence + 1) pending = old_line.pending_principal_amount if self.loan_id.currency_id.compare_amounts(self.amount, pending) == 1: diff --git a/account_loan/wizard/account_loan_post.py b/account_loan/wizard/account_loan_post.py index fc39d6cfb..c7ceb7f56 100644 --- a/account_loan/wizard/account_loan_post.py +++ b/account_loan/wizard/account_loan_post.py @@ -10,13 +10,13 @@ class AccountLoanPost(models.TransientModel): @api.model 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: return self.env["account.loan"].browse(loan_id).journal_id.id @api.model 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: loan = self.env["account.loan"].browse(loan_id) if loan.is_leasing: @@ -28,10 +28,10 @@ class AccountLoanPost(models.TransientModel): loan_id = fields.Many2one("account.loan", required=True, readonly=True,) 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.account", required=True, default=_default_account_id + "account.account", required=True, default=lambda r: r._default_account_id() ) 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()], } - @api.multi def run(self): self.ensure_one() if self.loan_id.state != "draft":