diff --git a/account_loan_analytic_account/README.rst b/account_loan_analytic_account/README.rst new file mode 100644 index 00000000000..7f30f049eaa --- /dev/null +++ b/account_loan_analytic_account/README.rst @@ -0,0 +1,47 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 + +============================= +Account Loan Analytic Account +============================= + +Add analytic distributions on account loan + + +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 smash it by providing detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Lindsay Marion + + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +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. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/account_loan_analytic_account/__init__.py b/account_loan_analytic_account/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/account_loan_analytic_account/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/account_loan_analytic_account/__manifest__.py b/account_loan_analytic_account/__manifest__.py new file mode 100644 index 00000000000..1281f470825 --- /dev/null +++ b/account_loan_analytic_account/__manifest__.py @@ -0,0 +1,16 @@ +# Copyright 2024 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Account Loan Analytic Account", + "summary": """ + Add analytic distributions on account loan""", + "version": "16.0.1.0.0", + "website": "https://github.com/OCA/account-financial-tools", + "license": "AGPL-3", + "author": "ACSONE SA/NV,Odoo Community Association (OCA)", + "depends": ["account_loan", "analytic"], + "data": [ + "views/account_loan.xml", + ], +} diff --git a/account_loan_analytic_account/models/__init__.py b/account_loan_analytic_account/models/__init__.py new file mode 100644 index 00000000000..cf4d206c05b --- /dev/null +++ b/account_loan_analytic_account/models/__init__.py @@ -0,0 +1,2 @@ +from . import account_loan +from . import account_move_line diff --git a/account_loan_analytic_account/models/account_loan.py b/account_loan_analytic_account/models/account_loan.py new file mode 100644 index 00000000000..1306ca5467d --- /dev/null +++ b/account_loan_analytic_account/models/account_loan.py @@ -0,0 +1,9 @@ +# Copyright 2024 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class AccountLoan(models.Model): + _name = "account.loan" + _inherit = ["account.loan", "analytic.mixin"] diff --git a/account_loan_analytic_account/models/account_move_line.py b/account_loan_analytic_account/models/account_move_line.py new file mode 100644 index 00000000000..cf5200bbbbd --- /dev/null +++ b/account_loan_analytic_account/models/account_move_line.py @@ -0,0 +1,23 @@ +# Copyright 2024 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class AccountMoveLine(models.Model): + + _inherit = "account.move.line" + + loan_id = fields.Many2one( + related="move_id.loan_id", + store=True, + ) + + @api.depends("loan_id", "loan_id.analytic_distribution") + def _compute_analytic_distribution(self): + res = super()._compute_analytic_distribution() + for rec in self: + if rec.loan_id and rec.loan_id.analytic_distribution: + if rec.account_id.id == rec.loan_id.interest_expenses_account_id.id: + rec.analytic_distribution = rec.loan_id.analytic_distribution + return res diff --git a/account_loan_analytic_account/static/description/icon.png b/account_loan_analytic_account/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/account_loan_analytic_account/static/description/icon.png differ diff --git a/account_loan_analytic_account/tests/__init__.py b/account_loan_analytic_account/tests/__init__.py new file mode 100644 index 00000000000..a1353b35220 --- /dev/null +++ b/account_loan_analytic_account/tests/__init__.py @@ -0,0 +1 @@ +from . import test_account_loan_analytic_account diff --git a/account_loan_analytic_account/tests/test_account_loan_analytic_account.py b/account_loan_analytic_account/tests/test_account_loan_analytic_account.py new file mode 100644 index 00000000000..6a7514dff52 --- /dev/null +++ b/account_loan_analytic_account/tests/test_account_loan_analytic_account.py @@ -0,0 +1,161 @@ +# Copyright 2024 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase + + +class TestAccountLoanAnalyticAccount(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.company = cls.env.ref("base.main_company") + cls.company_02 = cls.env["res.company"].create({"name": "Auxiliar company"}) + cls.journal = cls.env["account.journal"].create( + { + "company_id": cls.company.id, + "type": "purchase", + "name": "Debts", + "code": "DBT", + } + ) + cls.loan_account = cls.create_account( + "DEP", + "depreciation", + "liability_current", + ) + cls.payable_account = cls.create_account("PAY", "payable", "liability_payable") + cls.asset_account = cls.create_account("ASSET", "asset", "liability_payable") + cls.interests_account = cls.create_account("FEE", "Fees", "expense") + cls.lt_loan_account = cls.create_account( + "LTD", + "Long term depreciation", + "liability_non_current", + ) + cls.partner = cls.env["res.partner"].create({"name": "Bank"}) + cls.product = cls.env["product.product"].create( + {"name": "Payment", "type": "service"} + ) + cls.interests_product = cls.env["product.product"].create( + {"name": "Bank fee", "type": "service"} + ) + cls.env.user.groups_id += cls.env.ref("analytic.group_analytic_accounting") + + cls.default_plan = cls.env["account.analytic.plan"].create( + {"name": "Default", "company_id": False} + ) + cls.analytic_account_a = cls.env["account.analytic.account"].create( + { + "name": "analytic_account_a", + "plan_id": cls.default_plan.id, + "company_id": False, + } + ) + cls.analytic_account_b = cls.env["account.analytic.account"].create( + { + "name": "analytic_account_b", + "plan_id": cls.default_plan.id, + "company_id": False, + } + ) + cls.analytic_account_c = cls.env["account.analytic.account"].create( + { + "name": "analytic_account_c", + "plan_id": cls.default_plan.id, + "company_id": False, + } + ) + + cls.loan = cls.create_loan("fixed-annuity", 500000, 1, 60) + + @classmethod + def create_account(cls, code, name, account_type): + return cls.env["account.account"].create( + { + "company_id": cls.company.id, + "name": name, + "code": code, + "account_type": account_type, + "reconcile": True, + } + ) + + @classmethod + def create_loan(cls, type_loan, amount, rate, periods): + loan = cls.env["account.loan"].create( + { + "journal_id": cls.journal.id, + "rate_type": "napr", + "loan_type": type_loan, + "loan_amount": amount, + "payment_on_first_period": True, + "rate": rate, + "periods": periods, + "leased_asset_account_id": cls.asset_account.id, + "short_term_loan_account_id": cls.loan_account.id, + "interest_expenses_account_id": cls.interests_account.id, + "product_id": cls.product.id, + "interests_product_id": cls.interests_product.id, + "partner_id": cls.partner.id, + "analytic_distribution": { + cls.analytic_account_a.id: 50, + cls.analytic_account_b.id: 50, + }, + } + ) + loan.compute_lines() + return loan + + def test_analytic_account_propagates_to_moves(self): + post = ( + self.env["account.loan.post"] + .with_context(default_loan_id=self.loan.id) + .create({}) + ) + post.run() + + self.assertTrue(self.loan.move_ids) + + loan_line = self.loan.line_ids.filtered(lambda r: r.sequence == 1) + loan_line.view_process_values() + move_lines = loan_line.move_ids.mapped("line_ids") + self.assertTrue(loan_line.move_ids) + for line in move_lines: + if line.account_id == self.loan.interest_expenses_account_id: + self.assertEqual( + line.analytic_distribution, self.loan.analytic_distribution + ) + + def test_analytic_account_propagates_to_moves_after_validation(self): + post = ( + self.env["account.loan.post"] + .with_context(default_loan_id=self.loan.id) + .create({}) + ) + post.run() + + self.assertTrue(self.loan.move_ids) + + loan_line = self.loan.line_ids.filtered(lambda r: r.sequence == 1) + loan_line.view_process_values() + move_lines = loan_line.move_ids.mapped("line_ids") + self.assertTrue(loan_line.move_ids) + self.loan.write( + { + "analytic_distribution": { + self.analytic_account_a.id: 50, + self.analytic_account_c.id: 50, + }, + } + ) + for line in move_lines: + if line.account_id == self.loan.interest_expenses_account_id: + self.assertEqual( + line.analytic_distribution, self.loan.analytic_distribution + ) + self.assertEqual( + line.analytic_distribution, + { + str(self.analytic_account_a.id): 50, + str(self.analytic_account_c.id): 50, + }, + ) diff --git a/account_loan_analytic_account/views/account_loan.xml b/account_loan_analytic_account/views/account_loan.xml new file mode 100644 index 00000000000..ec9d89b9e66 --- /dev/null +++ b/account_loan_analytic_account/views/account_loan.xml @@ -0,0 +1,19 @@ + + + + + account.loan.form (in account_loan_analytic_account) + account.loan + + + + + + + + diff --git a/setup/account_loan_analytic_account/odoo/addons/account_loan_analytic_account b/setup/account_loan_analytic_account/odoo/addons/account_loan_analytic_account new file mode 120000 index 00000000000..cefa415fa70 --- /dev/null +++ b/setup/account_loan_analytic_account/odoo/addons/account_loan_analytic_account @@ -0,0 +1 @@ +../../../../account_loan_analytic_account \ No newline at end of file diff --git a/setup/account_loan_analytic_account/setup.py b/setup/account_loan_analytic_account/setup.py new file mode 100644 index 00000000000..28c57bb6403 --- /dev/null +++ b/setup/account_loan_analytic_account/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)