From a4156a2fbc62d7ba638e6a84310adc1ae7af21b1 Mon Sep 17 00:00:00 2001 From: Christopher Ormaza Date: Tue, 29 Mar 2022 14:55:26 -0500 Subject: [PATCH 001/197] [14.0][ADD] account_move_line_repair_info --- account_move_line_repair_info/README.rst | 81 ++++ account_move_line_repair_info/__init__.py | 2 + account_move_line_repair_info/__manifest__.py | 21 + account_move_line_repair_info/hooks.py | 71 +++ .../models/__init__.py | 3 + .../models/account_move.py | 99 ++++ .../models/repair_order.py | 16 + .../models/stock_move.py | 39 ++ .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 1 + .../readme/USAGE.rst | 4 + .../security/account_security.xml | 8 + .../static/description/index.html | 428 ++++++++++++++++++ .../tests/__init__.py | 1 + .../test_account_move_line_repair_info.py | 398 ++++++++++++++++ .../views/account_move_view.xml | 52 +++ .../odoo/addons/account_move_line_repair_info | 1 + setup/account_move_line_repair_info/setup.py | 6 + 18 files changed, 1232 insertions(+) create mode 100644 account_move_line_repair_info/README.rst create mode 100644 account_move_line_repair_info/__init__.py create mode 100644 account_move_line_repair_info/__manifest__.py create mode 100644 account_move_line_repair_info/hooks.py create mode 100644 account_move_line_repair_info/models/__init__.py create mode 100644 account_move_line_repair_info/models/account_move.py create mode 100644 account_move_line_repair_info/models/repair_order.py create mode 100644 account_move_line_repair_info/models/stock_move.py create mode 100644 account_move_line_repair_info/readme/CONTRIBUTORS.rst create mode 100644 account_move_line_repair_info/readme/DESCRIPTION.rst create mode 100644 account_move_line_repair_info/readme/USAGE.rst create mode 100644 account_move_line_repair_info/security/account_security.xml create mode 100644 account_move_line_repair_info/static/description/index.html create mode 100644 account_move_line_repair_info/tests/__init__.py create mode 100644 account_move_line_repair_info/tests/test_account_move_line_repair_info.py create mode 100644 account_move_line_repair_info/views/account_move_view.xml create mode 120000 setup/account_move_line_repair_info/odoo/addons/account_move_line_repair_info create mode 100644 setup/account_move_line_repair_info/setup.py diff --git a/account_move_line_repair_info/README.rst b/account_move_line_repair_info/README.rst new file mode 100644 index 000000000..9d39d6371 --- /dev/null +++ b/account_move_line_repair_info/README.rst @@ -0,0 +1,81 @@ +============================= +Account Move Line Repair Info +============================= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :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/14.0/account_move_line_repair_info + :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-14-0/account-financial-tools-14-0-account_move_line_repair_info + :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/14.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module will add the repair order to journal items. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +The repair order will be automatically copied to the journal items. + +* When Invoice(s) is made from repair order, field repair_order_id +* The journal entries created by the consumption of materials and the repaired product, are associated with the repair order + +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 `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* ForgeFlow S.L. + +Contributors +~~~~~~~~~~~~ + +* Christopher Ormaza + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +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. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_move_line_repair_info/__init__.py b/account_move_line_repair_info/__init__.py new file mode 100644 index 000000000..cc6b6354a --- /dev/null +++ b/account_move_line_repair_info/__init__.py @@ -0,0 +1,2 @@ +from . import models +from .hooks import post_init_hook diff --git a/account_move_line_repair_info/__manifest__.py b/account_move_line_repair_info/__manifest__.py new file mode 100644 index 000000000..bfcfca8e3 --- /dev/null +++ b/account_move_line_repair_info/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2022 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "Account Move Line Repair Info", + "summary": "Introduces the repair order to the journal items", + "version": "14.0.1.0.0", + "author": "ForgeFlow S.L., " "Odoo Community Association (OCA)", + "website": "https://github.com/OCA/account-financial-tools", + "category": "Generic", + "depends": [ + "account", + "repair", + "stock_account", + "stock_account_prepare_anglo_saxon_out_lines_hook", + ], + "license": "AGPL-3", + "data": ["security/account_security.xml", "views/account_move_view.xml"], + "installable": True, + "post_init_hook": "post_init_hook", +} diff --git a/account_move_line_repair_info/hooks.py b/account_move_line_repair_info/hooks.py new file mode 100644 index 000000000..653b9c141 --- /dev/null +++ b/account_move_line_repair_info/hooks.py @@ -0,0 +1,71 @@ +# Copyright 2022 ForgeFlow S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + + +def post_init_hook(cr, registry): + # Material consumed on repair + cr.execute( + """ + update account_move_line as aml set repair_order_id = q.order_id + from ( + select aml.id, ro.id as order_id + from account_move_line as aml + left join account_move as am on am.id = aml.move_id + left join stock_valuation_layer svl on svl.account_move_id = am.id + left join stock_move as sm on sm.id = svl.stock_move_id + left join repair_line as rl on rl.move_id = sm.id + left join repair_order as ro on rl.repair_id = ro.id + where ro.id is not null + ) as q + where q.id = aml.id + """ + ) + + # Product Repaired + cr.execute( + """ + update account_move_line as aml set repair_order_id = q.order_id + from ( + select aml.id, ro.id as order_id + from account_move_line as aml + left join account_move as am on am.id = aml.move_id + left join stock_valuation_layer svl on svl.account_move_id = am.id + left join stock_move as sm on sm.id = svl.stock_move_id + left join repair_order as ro on sm.id = ro.move_id + where ro.id is not null + ) as q + where q.id = aml.id + """ + ) + + # Invoice Lines for Products Consumed + cr.execute( + """ + update account_move_line as aml set repair_order_id = q.order_id + from ( + select aml.id, ro.id as order_id + from account_move_line as aml + left join account_move as am on am.id = aml.move_id + left join repair_line as rl on aml.id = rl.invoice_line_id + left join repair_order as ro on rl.repair_id = ro.id + where ro.id is not null + ) as q + where q.id = aml.id + """ + ) + + # Invoice Lines for Fees + cr.execute( + """ + update account_move_line as aml set repair_order_id = q.order_id + from ( + select aml.id, ro.id as order_id + from account_move_line as aml + left join account_move as am on am.id = aml.move_id + left join repair_fee as rf on aml.id = rf.invoice_line_id + left join repair_order as ro on rf.repair_id = ro.id + where ro.id is not null + ) as q + where q.id = aml.id + """ + ) diff --git a/account_move_line_repair_info/models/__init__.py b/account_move_line_repair_info/models/__init__.py new file mode 100644 index 000000000..16528a5e2 --- /dev/null +++ b/account_move_line_repair_info/models/__init__.py @@ -0,0 +1,3 @@ +from . import account_move +from . import repair_order +from . import stock_move diff --git a/account_move_line_repair_info/models/account_move.py b/account_move_line_repair_info/models/account_move.py new file mode 100644 index 000000000..facbf1819 --- /dev/null +++ b/account_move_line_repair_info/models/account_move.py @@ -0,0 +1,99 @@ +# Copyright 2022 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class AccountMove(models.Model): + + _inherit = "account.move" + + def _prepare_interim_account_line_vals(self, line, move, debit_interim_account): + res = super()._prepare_interim_account_line_vals( + line, move, debit_interim_account + ) + if len(line.repair_line_ids) == 1: + res.update( + { + "repair_order_id": line.repair_line_ids.repair_id.id, + } + ) + if len(line.repair_fee_ids) == 1: + res.update( + { + "repair_order_id": line.repair_fee_ids.repair_id.id, + } + ) + return res + + def _prepare_expense_account_line_vals(self, line, move, debit_interim_account): + res = super()._prepare_expense_account_line_vals( + line, move, debit_interim_account + ) + if len(line.repair_line_ids) == 1: + res.update( + { + "repair_order_id": line.repair_line_ids.repair_id.id, + } + ) + if len(line.repair_fee_ids) == 1: + res.update( + { + "repair_order_id": line.repair_fee_ids.repair_id.id, + } + ) + return res + + @api.model_create_multi + def create(self, values): + rline_model = self.env["repair.line"] + fline_model = self.env["repair.fee"] + rorder_model = self.env["repair.order"] + for val in values: + for invoice_line_ids in val.get("invoice_line_ids", []): + line_val = invoice_line_ids[2] + if line_val.get("repair_line_ids", []): + repair_line_ids = [ + r[1] for r in line_val.get("repair_line_ids", []) + ] + rline = rline_model.browse(repair_line_ids) + rorder = rline.mapped("repair_id") + if len(rorder) == 1: + line_val.update( + { + "repair_order_id": rline.mapped("repair_id").id, + } + ) + if line_val.get("repair_fee_ids", False): + repair_fee_ids = [r[1] for r in line_val.get("repair_fee_ids", [])] + fline = fline_model.browse(repair_fee_ids) + rorder = fline.mapped("repair_id") + if len(rorder) == 1: + line_val.update( + { + "repair_order_id": fline.mapped("repair_id").id, + } + ) + if line_val.get("repair_ids", False): + repair_ids = [r[1] for r in line_val.get("repair_ids", [])] + rorder = rorder_model.browse(repair_ids) + if len(rorder) == 1: + line_val.update( + { + "repair_order_id": rorder.id, + } + ) + return super(AccountMove, self).create(values) + + +class AccountMoveLine(models.Model): + + _inherit = "account.move.line" + + repair_order_id = fields.Many2one( + comodel_name="repair.order", + string="Repair Order", + ondelete="set null", + index=True, + copy=False, + ) diff --git a/account_move_line_repair_info/models/repair_order.py b/account_move_line_repair_info/models/repair_order.py new file mode 100644 index 000000000..fb7c743d7 --- /dev/null +++ b/account_move_line_repair_info/models/repair_order.py @@ -0,0 +1,16 @@ +# Copyright 2022 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class RepairOrder(models.Model): + + _inherit = "repair.order" + + account_move_line_ids = fields.One2many( + comodel_name="account.move.line", + inverse_name="repair_order_id", + string="Journal Entries", + required=False, + ) diff --git a/account_move_line_repair_info/models/stock_move.py b/account_move_line_repair_info/models/stock_move.py new file mode 100644 index 000000000..2812848a1 --- /dev/null +++ b/account_move_line_repair_info/models/stock_move.py @@ -0,0 +1,39 @@ +# Copyright 2022 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import models + + +class StockMove(models.Model): + + _inherit = "stock.move" + + def _create_account_move_line( + self, + credit_account_id, + debit_account_id, + journal_id, + qty, + description, + svl_id, + cost, + ): + am_model = self.env["account.move"] + res = super()._create_account_move_line( + credit_account_id, + debit_account_id, + journal_id, + qty, + description, + svl_id, + cost, + ) + if self.repair_id: + current_move = am_model.search([("stock_move_id", "=", self.id)]) + if current_move: + current_move.line_ids.write( + { + "repair_order_id": self.repair_id.id, + } + ) + return res diff --git a/account_move_line_repair_info/readme/CONTRIBUTORS.rst b/account_move_line_repair_info/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..b647a292e --- /dev/null +++ b/account_move_line_repair_info/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Christopher Ormaza diff --git a/account_move_line_repair_info/readme/DESCRIPTION.rst b/account_move_line_repair_info/readme/DESCRIPTION.rst new file mode 100644 index 000000000..2416227b4 --- /dev/null +++ b/account_move_line_repair_info/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module will add the repair order to journal items. diff --git a/account_move_line_repair_info/readme/USAGE.rst b/account_move_line_repair_info/readme/USAGE.rst new file mode 100644 index 000000000..e33eb60fc --- /dev/null +++ b/account_move_line_repair_info/readme/USAGE.rst @@ -0,0 +1,4 @@ +The repair order will be automatically copied to the journal items. + +* When Invoice(s) is made from repair order, field repair_order_id +* The journal entries created by the consumption of materials and the repaired product, are associated with the repair order diff --git a/account_move_line_repair_info/security/account_security.xml b/account_move_line_repair_info/security/account_security.xml new file mode 100644 index 000000000..56158e9c7 --- /dev/null +++ b/account_move_line_repair_info/security/account_security.xml @@ -0,0 +1,8 @@ + + + + Repair Order info in Journal Items + + + + diff --git a/account_move_line_repair_info/static/description/index.html b/account_move_line_repair_info/static/description/index.html new file mode 100644 index 000000000..3c55f3b1b --- /dev/null +++ b/account_move_line_repair_info/static/description/index.html @@ -0,0 +1,428 @@ + + + + + + +Account Move Line Repair Info + + + +
+

Account Move Line Repair Info

+ + +

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

+

This module will add the repair order to journal items.

+

Table of contents

+ +
+

Usage

+

The repair order will be automatically copied to the journal items.

+
    +
  • When Invoice(s) is made from repair order, field repair_order_id
  • +
  • The journal entries created by the consumption of materials and the repaired product, are associated with the repair order
  • +
+
+
+

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.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • ForgeFlow S.L.
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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.

+

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

+
+
+
+ + diff --git a/account_move_line_repair_info/tests/__init__.py b/account_move_line_repair_info/tests/__init__.py new file mode 100644 index 000000000..ceb307b61 --- /dev/null +++ b/account_move_line_repair_info/tests/__init__.py @@ -0,0 +1 @@ +from . import test_account_move_line_repair_info diff --git a/account_move_line_repair_info/tests/test_account_move_line_repair_info.py b/account_move_line_repair_info/tests/test_account_move_line_repair_info.py new file mode 100644 index 000000000..a81a2ba39 --- /dev/null +++ b/account_move_line_repair_info/tests/test_account_move_line_repair_info.py @@ -0,0 +1,398 @@ +# Copyright 2022 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +# flake8: noqa: B950 + +from odoo import fields +from odoo.tests.common import TransactionCase + + +class TestAccountMoveLineRepairInfo(TransactionCase): + def setUp(self): + super().setUp() + self.aml_model = self.env["account.move.line"] + self.am_model = self.env["account.move"] + self.chart_template = self.env["account.chart.template"].create( + { + "name": "Test chart", + "currency_id": self.env.ref("base.EUR").id, + "code_digits": 6, + "cash_account_code_prefix": "570", + "bank_account_code_prefix": "572", + "transfer_account_code_prefix": "100000", + "use_anglo_saxon": True, + } + ) + self.chart_template.try_loading(company=self.env.user.company_id) + self.valuation_account = self.env["account.account"].create( + { + "name": "Test stock valuation", + "code": "tv", + "user_type_id": self.env["account.account.type"].search([], limit=1).id, + "reconcile": True, + "company_id": self.env.ref("base.main_company").id, + } + ) + self.payable_account = self.env["account.account"].create( + { + "name": "Test Payable", + "code": "tpayable", + "user_type_id": self.env["account.account.type"] + .search([("type", "=", "payable")], limit=1) + .id, + "reconcile": True, + "company_id": self.env.ref("base.main_company").id, + } + ) + self.receivable_account = self.env["account.account"].create( + { + "name": "Test Receivable", + "code": "treceivable", + "user_type_id": self.env["account.account.type"] + .search([("type", "=", "receivable")], limit=1) + .id, + "reconcile": True, + "company_id": self.env.ref("base.main_company").id, + } + ) + + self.stock_input_account = self.env["account.account"].create( + { + "name": "Test stock input", + "code": "tsti", + "user_type_id": self.env["account.account.type"].search([], limit=1).id, + "reconcile": True, + "company_id": self.env.ref("base.main_company").id, + } + ) + self.stock_output_account = self.env["account.account"].create( + { + "name": "Test stock output", + "code": "tout", + "user_type_id": self.env["account.account.type"].search([], limit=1).id, + "reconcile": True, + "company_id": self.env.ref("base.main_company").id, + } + ) + self.stock_income_account = self.env["account.account"].create( + { + "name": "Test stock income", + "code": "tincome", + "user_type_id": self.env["account.account.type"] + .search([("internal_group", "=", "income")], limit=1) + .id, + "reconcile": True, + "company_id": self.env.ref("base.main_company").id, + } + ) + self.stock_expense_account = self.env["account.account"].create( + { + "name": "Test stock outcome", + "code": "texpense", + "user_type_id": self.env["account.account.type"] + .search([("internal_group", "=", "expense")], limit=1) + .id, + "reconcile": True, + "company_id": self.env.ref("base.main_company").id, + } + ) + self.stock_journal = self.env["account.journal"].create( + {"name": "Stock Journal", "code": "STJTEST", "type": "general"} + ) + self.categ_real = self.env["product.category"].create( + { + "name": "REAL", + "property_cost_method": "fifo", + "property_valuation": "real_time", + "property_stock_valuation_account_id": self.valuation_account.id, + "property_stock_account_input_categ_id": self.stock_input_account.id, + "property_stock_account_output_categ_id": self.stock_output_account.id, + "property_account_expense_categ_id": self.stock_expense_account.id, + "property_account_income_categ_id": self.stock_income_account.id, + "property_stock_journal": self.stock_journal.id, + } + ) + self.res_partner_1 = self.env["res.partner"].create( + { + "name": "Wood Corner", + "property_account_payable_id": self.payable_account.id, + "property_account_receivable_id": self.receivable_account.id, + } + ) + self.res_partner_address_1 = self.env["res.partner"].create( + {"name": "Willie Burke", "parent_id": self.res_partner_1.id} + ) + self.res_partner_12 = self.env["res.partner"].create( + { + "name": "Partner 12", + "property_account_payable_id": self.payable_account.id, + "property_account_receivable_id": self.receivable_account.id, + } + ) + + # Products + self.product_product_3 = self.env["product.product"].create( + { + "name": "Desk Combination", + "categ_id": self.categ_real.id, + "standard_price": 1.0, + "type": "product", + } + ) + self.product_product_11 = self.env["product.product"].create( + { + "name": "Conference Chair", + "categ_id": self.categ_real.id, + "standard_price": 1.0, + "type": "product", + } + ) + self.product_product_5 = self.env["product.product"].create( + { + "name": "Product 5", + "categ_id": self.categ_real.id, + "standard_price": 1.0, + "type": "product", + } + ) + self.product_product_6 = self.env["product.product"].create( + { + "name": "Large Cabinet", + "categ_id": self.categ_real.id, + "standard_price": 1.0, + "type": "product", + } + ) + self.product_product_12 = self.env["product.product"].create( + { + "name": "Office Chair Black", + "categ_id": self.categ_real.id, + "standard_price": 1.0, + "type": "product", + } + ) + self.product_product_13 = self.env["product.product"].create( + { + "name": "Corner Desk Left Sit", + "categ_id": self.categ_real.id, + "standard_price": 1.0, + "type": "product", + } + ) + self.product_product_2 = self.env["product.product"].create( + { + "name": "Virtual Home Staging", + "categ_id": self.categ_real.id, + "standard_price": 1.0, + "type": "product", + } + ) + self.product_service_order_repair = self.env["product.product"].create( + { + "name": "Repair Services", + "type": "service", + "categ_id": self.categ_real.id, + } + ) + + # Location + self.stock_warehouse = self.env["stock.warehouse"].search( + [("company_id", "=", self.env.company.id)], limit=1 + ) + self.stock_location_14 = self.env["stock.location"].create( + { + "name": "Shelf 2", + "location_id": self.stock_warehouse.lot_stock_id.id, + } + ) + + # Repair Orders + self.repair1 = self.env["repair.order"].create( + { + "address_id": self.res_partner_address_1.id, + "guarantee_limit": fields.Date.today(), + "invoice_method": "none", + "user_id": False, + "product_id": self.product_product_3.id, + "product_uom": self.env.ref("uom.product_uom_unit").id, + "partner_invoice_id": self.res_partner_address_1.id, + "location_id": self.stock_warehouse.lot_stock_id.id, + "operations": [ + ( + 0, + 0, + { + "location_dest_id": self.product_product_11.property_stock_production.id, + "location_id": self.stock_warehouse.lot_stock_id.id, + "name": self.product_product_11.get_product_multiline_description_sale(), + "product_id": self.product_product_11.id, + "product_uom": self.env.ref("uom.product_uom_unit").id, + "product_uom_qty": 1.0, + "price_unit": 50.0, + "state": "draft", + "type": "add", + "company_id": self.env.company.id, + }, + ) + ], + "fees_lines": [ + ( + 0, + 0, + { + "name": self.product_service_order_repair.get_product_multiline_description_sale(), + "product_id": self.product_service_order_repair.id, + "product_uom_qty": 1.0, + "product_uom": self.env.ref("uom.product_uom_unit").id, + "price_unit": 50.0, + "company_id": self.env.company.id, + }, + ) + ], + "partner_id": self.res_partner_12.id, + } + ) + + self.repair0 = self.env["repair.order"].create( + { + "product_id": self.product_product_5.id, + "product_uom": self.env.ref("uom.product_uom_unit").id, + "address_id": self.res_partner_address_1.id, + "guarantee_limit": fields.Date.today(), + "invoice_method": "after_repair", + "user_id": False, + "partner_invoice_id": self.res_partner_address_1.id, + "location_id": self.stock_warehouse.lot_stock_id.id, + "operations": [ + ( + 0, + 0, + { + "location_dest_id": self.product_product_12.property_stock_production.id, + "location_id": self.stock_warehouse.lot_stock_id.id, + "name": self.product_product_12.get_product_multiline_description_sale(), + "price_unit": 50.0, + "product_id": self.product_product_12.id, + "product_uom": self.env.ref("uom.product_uom_unit").id, + "product_uom_qty": 1.0, + "state": "draft", + "type": "add", + "company_id": self.env.company.id, + }, + ) + ], + "fees_lines": [ + ( + 0, + 0, + { + "name": self.product_service_order_repair.get_product_multiline_description_sale(), + "product_id": self.product_service_order_repair.id, + "product_uom_qty": 1.0, + "product_uom": self.env.ref("uom.product_uom_unit").id, + "price_unit": 50.0, + "company_id": self.env.company.id, + }, + ) + ], + "partner_id": self.res_partner_12.id, + } + ) + + self.repair2 = self.env["repair.order"].create( + { + "product_id": self.product_product_6.id, + "product_uom": self.env.ref("uom.product_uom_unit").id, + "address_id": self.res_partner_address_1.id, + "guarantee_limit": fields.Date.today(), + "invoice_method": "b4repair", + "user_id": False, + "partner_invoice_id": self.res_partner_address_1.id, + "location_id": self.stock_location_14.id, + "operations": [ + ( + 0, + 0, + { + "location_dest_id": self.product_product_13.property_stock_production.id, + "location_id": self.stock_warehouse.lot_stock_id.id, + "name": self.product_product_13.get_product_multiline_description_sale(), + "price_unit": 50.0, + "product_id": self.product_product_13.id, + "product_uom": self.env.ref("uom.product_uom_unit").id, + "product_uom_qty": 1.0, + "state": "draft", + "type": "add", + "company_id": self.env.company.id, + }, + ) + ], + "fees_lines": [ + ( + 0, + 0, + { + "name": self.product_service_order_repair.get_product_multiline_description_sale(), + "product_id": self.product_service_order_repair.id, + "product_uom_qty": 1.0, + "product_uom": self.env.ref("uom.product_uom_unit").id, + "price_unit": 50.0, + "company_id": self.env.company.id, + }, + ) + ], + "partner_id": self.res_partner_12.id, + } + ) + + self.env.user.groups_id |= self.env.ref("stock.group_stock_user") + + def test_move_line_repair_info(self): + self.product_product_3.write({"categ_id": self.categ_real.id}) + self.product_product_11.write({"categ_id": self.categ_real.id}) + self.product_product_5.write({"categ_id": self.categ_real.id}) + self.product_product_6.write({"categ_id": self.categ_real.id}) + self.product_product_12.write({"categ_id": self.categ_real.id}) + self.product_product_13.write({"categ_id": self.categ_real.id}) + self.product_product_2.write({"categ_id": self.categ_real.id}) + repairs = self.repair0 + self.repair1 + self.repair2 + for repair in repairs: + repair.action_repair_confirm() + if repair.state != "2binvoiced": + repair.action_repair_start() + repair.action_repair_end() + for operation in repairs.filtered( + lambda x: x.state == "done" and x.move_id + ).mapped("operations"): + aml = self.aml_model.search( + [("move_id.stock_move_id", "=", operation.move_id.id)] + ) + if aml: + self.assertEqual( + aml.mapped("repair_order_id").ids, operation.repair_id.ids + ) + make_invoice = self.env["repair.order.make_invoice"].create({"group": True}) + context = { + "active_model": "repair.order", + "active_ids": repairs.ids, + } + res = make_invoice.with_context(context).make_invoices() + invoices = res.get("domain", []) and self.am_model.browse( + res.get("domain", [])[0][2] + ) + for invoice in invoices: + if invoice.state == "draft": + invoice.action_post() + for fee in repairs.mapped("fees_lines"): + invoice_lines = fee.repair_id.invoice_id.invoice_line_ids.filtered( + lambda x: fee.id in x.repair_fee_ids.ids + ) + if invoice_lines: + self.assertEqual(fee.repair_id.id, invoice_lines.repair_order_id.id) + for operation in repairs.mapped("operations"): + invoice_lines = operation.repair_id.invoice_id.invoice_line_ids.filtered( + lambda x: operation.id in x.repair_line_ids.ids + ) + if invoice_lines: + self.assertEqual( + operation.repair_id.id, invoice_lines.repair_order_id.id + ) diff --git a/account_move_line_repair_info/views/account_move_view.xml b/account_move_line_repair_info/views/account_move_view.xml new file mode 100644 index 000000000..af6cca51c --- /dev/null +++ b/account_move_line_repair_info/views/account_move_view.xml @@ -0,0 +1,52 @@ + + + + account.move.line.form + account.move.line + + + + + + + + + account.move.line.tree + account.move.line + + + + + + + + + Journal Items + account.move.line + + + + + + + + + + + diff --git a/setup/account_move_line_repair_info/odoo/addons/account_move_line_repair_info b/setup/account_move_line_repair_info/odoo/addons/account_move_line_repair_info new file mode 120000 index 000000000..761f686da --- /dev/null +++ b/setup/account_move_line_repair_info/odoo/addons/account_move_line_repair_info @@ -0,0 +1 @@ +../../../../account_move_line_repair_info \ No newline at end of file diff --git a/setup/account_move_line_repair_info/setup.py b/setup/account_move_line_repair_info/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/account_move_line_repair_info/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From 80907169f4d0ea82f40506675c3c97c80d848cac Mon Sep 17 00:00:00 2001 From: Christopher Ormaza Date: Thu, 7 Apr 2022 15:17:19 -0500 Subject: [PATCH 002/197] [14.0][ADD] account_move_transfer_partner --- account_move_transfer_partner/README.rst | 91 ++++ account_move_transfer_partner/__init__.py | 4 + account_move_transfer_partner/__manifest__.py | 20 + .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 1 + .../readme/USAGE.rst | 1 + .../security/ir.model.access.csv | 2 + .../static/description/index.html | 432 ++++++++++++++++++ .../tests/__init__.py | 1 + .../test_account_move_transfer_partner.py | 334 ++++++++++++++ .../wizard/__init__.py | 1 + .../wizard_account_move_transfer_partner.py | 188 ++++++++ ...ard_account_move_transfer_partner_view.xml | 98 ++++ .../odoo/addons/account_move_transfer_partner | 1 + setup/account_move_transfer_partner/setup.py | 6 + 15 files changed, 1181 insertions(+) create mode 100644 account_move_transfer_partner/README.rst create mode 100644 account_move_transfer_partner/__init__.py create mode 100644 account_move_transfer_partner/__manifest__.py create mode 100644 account_move_transfer_partner/readme/CONTRIBUTORS.rst create mode 100644 account_move_transfer_partner/readme/DESCRIPTION.rst create mode 100644 account_move_transfer_partner/readme/USAGE.rst create mode 100644 account_move_transfer_partner/security/ir.model.access.csv create mode 100644 account_move_transfer_partner/static/description/index.html create mode 100644 account_move_transfer_partner/tests/__init__.py create mode 100644 account_move_transfer_partner/tests/test_account_move_transfer_partner.py create mode 100644 account_move_transfer_partner/wizard/__init__.py create mode 100644 account_move_transfer_partner/wizard/wizard_account_move_transfer_partner.py create mode 100644 account_move_transfer_partner/wizard/wizard_account_move_transfer_partner_view.xml create mode 120000 setup/account_move_transfer_partner/odoo/addons/account_move_transfer_partner create mode 100644 setup/account_move_transfer_partner/setup.py diff --git a/account_move_transfer_partner/README.rst b/account_move_transfer_partner/README.rst new file mode 100644 index 000000000..72357ad90 --- /dev/null +++ b/account_move_transfer_partner/README.rst @@ -0,0 +1,91 @@ +============================= +Account Move Transfer Partner +============================= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :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/14.0/account_move_transfer_partner + :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-14-0/account-financial-tools-14-0-account_move_transfer_partner + :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/14.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Add wizard to make transfer amount due from invoice / refund documents to specific partner + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +This wizard just can be used on invoice / refund documents + +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 `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* ForgeFlow S.L. + +Contributors +~~~~~~~~~~~~ + +* Christopher Ormaza + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +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. + +.. |maintainer-ChrisOForgeFlow| image:: https://github.com/ChrisOForgeFlow.png?size=40px + :target: https://github.com/ChrisOForgeFlow + :alt: ChrisOForgeFlow + +Current `maintainer `__: + +|maintainer-ChrisOForgeFlow| + +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_move_transfer_partner/__init__.py b/account_move_transfer_partner/__init__.py new file mode 100644 index 000000000..5343bab38 --- /dev/null +++ b/account_move_transfer_partner/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2022 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import wizard diff --git a/account_move_transfer_partner/__manifest__.py b/account_move_transfer_partner/__manifest__.py new file mode 100644 index 000000000..a2ee715de --- /dev/null +++ b/account_move_transfer_partner/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2022 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "Account Move Transfer Partner", + "summary": "Automation to translate amount due from many partners to one partner", + "version": "14.0.1.0.0", + "author": "ForgeFlow S.L., " "Odoo Community Association (OCA)", + "website": "https://github.com/OCA/account-financial-tools", + "category": "Generic", + "depends": ["account"], + "license": "AGPL-3", + "data": [ + "security/ir.model.access.csv", + "wizard/wizard_account_move_transfer_partner_view.xml", + ], + "installable": True, + "development_status": "Alpha", + "maintainers": ["ChrisOForgeFlow"], +} diff --git a/account_move_transfer_partner/readme/CONTRIBUTORS.rst b/account_move_transfer_partner/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..b647a292e --- /dev/null +++ b/account_move_transfer_partner/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Christopher Ormaza diff --git a/account_move_transfer_partner/readme/DESCRIPTION.rst b/account_move_transfer_partner/readme/DESCRIPTION.rst new file mode 100644 index 000000000..328a29c41 --- /dev/null +++ b/account_move_transfer_partner/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Add wizard to make transfer amount due from invoice / refund documents to specific partner diff --git a/account_move_transfer_partner/readme/USAGE.rst b/account_move_transfer_partner/readme/USAGE.rst new file mode 100644 index 000000000..f8cb706f9 --- /dev/null +++ b/account_move_transfer_partner/readme/USAGE.rst @@ -0,0 +1 @@ +This wizard just can be used on invoice / refund documents diff --git a/account_move_transfer_partner/security/ir.model.access.csv b/account_move_transfer_partner/security/ir.model.access.csv new file mode 100644 index 000000000..8591f32c0 --- /dev/null +++ b/account_move_transfer_partner/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_wizard_account_move_transfer_partner_all,access_wizard_account_move_transfer_partner_all,model_wizard_account_move_transfer_partner,,1,1,1,1 diff --git a/account_move_transfer_partner/static/description/index.html b/account_move_transfer_partner/static/description/index.html new file mode 100644 index 000000000..8ea815edb --- /dev/null +++ b/account_move_transfer_partner/static/description/index.html @@ -0,0 +1,432 @@ + + + + + + +Account Move Transfer Partner + + + +
+

Account Move Transfer Partner

+ + +

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

+

Add wizard to make transfer amount due from invoice / refund documents to specific partner

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Usage

+

This wizard just can be used on invoice / refund documents

+
+
+

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.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • ForgeFlow S.L.
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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.

+

Current maintainer:

+

ChrisOForgeFlow

+

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_move_transfer_partner/tests/__init__.py b/account_move_transfer_partner/tests/__init__.py new file mode 100644 index 000000000..a895df890 --- /dev/null +++ b/account_move_transfer_partner/tests/__init__.py @@ -0,0 +1 @@ +from . import test_account_move_transfer_partner diff --git a/account_move_transfer_partner/tests/test_account_move_transfer_partner.py b/account_move_transfer_partner/tests/test_account_move_transfer_partner.py new file mode 100644 index 000000000..772bb13f3 --- /dev/null +++ b/account_move_transfer_partner/tests/test_account_move_transfer_partner.py @@ -0,0 +1,334 @@ +# Copyright 2022 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import _, fields +from odoo.exceptions import ValidationError +from odoo.tests.common import Form, TransactionCase + +CURRENCY_RATE = 0.5 + + +class TestAccountMoveTransferPartner(TransactionCase): + def setUp(self): + super().setUp() + self.company = self.env.ref("base.main_company") + self.partner_1 = self.env.ref("base.res_partner_1") + self.partner_2 = self.env.ref("base.res_partner_2") + self.partner_3 = self.env.ref("base.res_partner_3") + self.today = fields.Date.today() + + self.AccountJournal = self.env["account.journal"] + self.wizard_model = self.env["wizard.account.move.transfer.partner"] + self.sale_journal = self.AccountJournal.create( + { + "name": "Sale journal", + "code": "SALE", + "type": "sale", + } + ) + self.purchase_journal = self.AccountJournal.create( + { + "name": "Purchase journal", + "code": "PURCHASE", + "type": "purchase", + } + ) + self.general_journal = self.AccountJournal.create( + { + "name": "General journal", + "code": "GENERAL", + "type": "general", + } + ) + + self.ProductProduct = self.env["product.product"] + self.product = self.ProductProduct.create( + {"name": "Product", "price": 100.0, "standard_price": 100.0} + ) + charts = self.env["account.chart.template"].search([]) + if charts: + self.chart = charts[0] + else: + raise ValidationError(_("No Chart of Account Template has been defined !")) + self.AccountMove = self.env["account.move"] + with Form( + self.AccountMove.with_context(default_move_type="out_invoice") + ) as invoice_form: + invoice_form.invoice_date = self.today + invoice_form.partner_id = self.partner_2 + invoice_form.journal_id = self.sale_journal + with invoice_form.invoice_line_ids.new() as line_form: + line_form.product_id = self.product + self.invoice_1 = invoice_form.save() + self.invoice_2 = self.invoice_1.copy() + self.invoice_3 = self.invoice_1.copy() + invoice_form = Form(self.invoice_3) + invoice_form.partner_id = self.partner_2 + self.invoice_3 = invoice_form.save() + self.invoice_1.action_post() + self.invoice_2.action_post() + self.invoice_3.action_post() + + with Form( + self.AccountMove.with_context(default_move_type="in_invoice") + ) as invoice_form: + invoice_form.invoice_date = self.today + invoice_form.partner_id = self.partner_1 + invoice_form.journal_id = self.purchase_journal + with invoice_form.invoice_line_ids.new() as line_form: + line_form.product_id = self.product + self.in_invoice = invoice_form.save() + self.in_invoice.action_post() + + with Form( + self.AccountMove.with_context(default_move_type="out_refund") + ) as invoice_form: + invoice_form.invoice_date = self.today + invoice_form.partner_id = self.partner_1 + invoice_form.journal_id = self.sale_journal + with invoice_form.invoice_line_ids.new() as line_form: + line_form.product_id = self.product + self.out_refund = invoice_form.save() + self.out_refund.action_post() + with Form( + self.AccountMove.with_context(default_move_type="in_refund") + ) as invoice_form: + invoice_form.invoice_date = self.today + invoice_form.partner_id = self.partner_1 + invoice_form.journal_id = self.purchase_journal + with invoice_form.invoice_line_ids.new() as line_form: + line_form.product_id = self.product + self.in_refund = invoice_form.save() + self.in_refund.action_post() + + self.general_move = self.AccountMove.create( + { + "journal_id": self.general_journal.id, + "ref": "sample move", + "line_ids": [ + ( + 0, + 0, + { + "debit": 100.0, + "credit": 0, + "account_id": self.env["account.account"] + .search([("user_type_id.type", "=", "other")], limit=1) + .id, + }, + ), + ( + 0, + 0, + { + "debit": 0.0, + "credit": 100.0, + "account_id": self.env["account.account"] + .search([("user_type_id.type", "=", "other")], limit=1) + .id, + }, + ), + ], + } + ) + self.general_move.action_post() + self.payment_term = self.env["account.payment.term"].create( + { + "name": "Pay in 2 installments", + "line_ids": [ + # Pay 50% immediately + ( + 0, + 0, + { + "value": "percent", + "value_amount": 50, + }, + ), + # Pay the rest after 14 days + ( + 0, + 0, + { + "value": "balance", + "days": 14, + }, + ), + ], + } + ) + self.currency_2 = self.env["res.currency"].create( + { + "name": "test", + "symbol": "TEST", + "rate_ids": [ + ( + 0, + 0, + { + "name": fields.Date.today(), + "rate": CURRENCY_RATE, + }, + ) + ], + } + ) + self.AccountMoveReversal = self.env["account.move.reversal"] + + def test_01_account_move_transfer_partner(self): + self.assertEqual(self.invoice_1.payment_state, "not_paid") + self.assertEqual(self.invoice_2.payment_state, "not_paid") + self.assertEqual(self.invoice_3.payment_state, "not_paid") + all_invoices = self.invoice_1 + self.invoice_2 + self.invoice_3 + total_residual = sum(i.amount_residual for i in all_invoices) + wizard_form = Form( + self.wizard_model.with_context({"active_ids": self.invoice_1.ids}) + ) + self.assertTrue(wizard_form.allow_edit_amount_to_transfer) + wizard_form.amount_to_transfer = self.invoice_1.amount_residual + 1 + wizard_form.destination_partner_id = self.partner_3 + wizard = wizard_form.save() + with self.assertRaises(ValidationError): + wizard.action_create_move() + wizard.write( + { + "amount_to_transfer": 0, + } + ) + with self.assertRaises(ValidationError): + wizard.action_create_move() + wizard_form = Form( + self.wizard_model.with_context({"active_ids": all_invoices.ids}) + ) + with self.assertRaises(AssertionError): + wizard_form.amount_to_transfer = total_residual + 1 + wizard_form.destination_partner_id = self.partner_3 + self.assertFalse(wizard_form.allow_edit_amount_to_transfer) + wizard = wizard_form.save() + action = wizard.action_create_move() + self.assertEqual(self.invoice_1.payment_state, "paid") + self.assertEqual(self.invoice_2.payment_state, "paid") + self.assertEqual(self.invoice_3.payment_state, "paid") + new_moves = action.get("domain", []) and self.AccountMove.browse( + action.get("domain", [])[0][2] + ) + unreconciled_lines = new_moves.mapped("line_ids").filtered( + lambda x: not x.reconciled + ) + self.assertEqual( + unreconciled_lines.mapped("partner_id").ids, self.partner_3.ids + ) + + def test_02_account_move_transfer_partner(self): + wizard_form = Form( + self.wizard_model.with_context({"active_ids": self.in_invoice.ids}) + ) + self.assertTrue(wizard_form.allow_edit_amount_to_transfer) + current_residual_amount = self.invoice_1.amount_residual + wizard_form.amount_to_transfer = current_residual_amount - 1 + wizard_form.destination_partner_id = self.partner_3 + wizard = wizard_form.save() + action = wizard.action_create_move() + self.assertEqual(self.in_invoice.amount_residual, 1.0) + new_moves = action.get("domain", []) and self.AccountMove.browse( + action.get("domain", [])[0][2] + ) + partner_1_aml = new_moves.mapped("line_ids").filtered( + lambda x: x.partner_id.id == self.partner_1.id + ) + self.assertEqual(abs(partner_1_aml.amount_residual), 0.0) + partner_3_aml = new_moves.mapped("line_ids").filtered( + lambda x: x.partner_id.id == self.partner_3.id + ) + self.assertEqual( + abs(partner_3_aml.amount_residual), current_residual_amount - 1 + ) + + def test_03_account_move_transfer_partner(self): + wizard_form = Form( + self.wizard_model.with_context({"active_ids": self.out_refund.ids}) + ) + self.assertTrue(wizard_form.allow_edit_amount_to_transfer) + wizard_form.destination_partner_id = self.partner_3 + wizard = wizard_form.save() + wizard.action_create_move() + self.assertEqual(self.out_refund.amount_residual, 0.0) + + def test_04_account_move_transfer_partner(self): + wizard_form = Form( + self.wizard_model.with_context({"active_ids": self.in_refund.ids}) + ) + self.assertTrue(wizard_form.allow_edit_amount_to_transfer) + wizard_form.destination_partner_id = self.partner_3 + wizard = wizard_form.save() + wizard.action_create_move() + self.assertEqual(self.in_refund.amount_residual, 0.0) + + def test_05_account_move_transfer_partner(self): + wizard_form = Form( + self.wizard_model.with_context({"active_ids": self.general_move.ids}) + ) + self.assertTrue(wizard_form.allow_edit_amount_to_transfer) + self.assertTrue(wizard_form.no_invoice_documents) + wizard_form.destination_partner_id = self.partner_3 + wizard = wizard_form.save() + with self.assertRaises(ValidationError): + wizard.action_create_move() + + def test_06_account_move_transfer_partner(self): + with Form( + self.AccountMove.with_context(default_move_type="out_invoice") + ) as invoice_form: + invoice_form.invoice_date = self.today + invoice_form.partner_id = self.partner_2 + invoice_form.journal_id = self.sale_journal + invoice_form.currency_id = self.currency_2 + with invoice_form.invoice_line_ids.new() as line_form: + line_form.product_id = self.product + self.invoice_with_payment_term = invoice_form.save() + self.invoice_with_payment_term.action_post() + wizard_form = Form( + self.wizard_model.with_context( + {"active_ids": self.invoice_with_payment_term.ids} + ) + ) + self.assertTrue(wizard_form.allow_edit_amount_to_transfer) + wizard_form.destination_partner_id = self.partner_3 + self.assertEqual( + wizard_form.total_amount_due, + self.invoice_with_payment_term.currency_id.compute( + self.invoice_with_payment_term.amount_residual, + self.env.company.currency_id, + ), + ) + wizard_form.currency_id = self.currency_2 + self.assertEqual( + wizard_form.total_amount_due, + self.invoice_with_payment_term.amount_residual, + ) + + def test_07_account_move_transfer_partner(self): + with Form( + self.AccountMove.with_context(default_move_type="out_invoice") + ) as invoice_form: + invoice_form.invoice_date = self.today + invoice_form.partner_id = self.partner_2 + invoice_form.journal_id = self.sale_journal + invoice_form.invoice_payment_term_id = self.payment_term + with invoice_form.invoice_line_ids.new() as line_form: + line_form.product_id = self.product + self.invoice_with_payment_term = invoice_form.save() + self.invoice_with_payment_term.action_post() + wizard_form = Form( + self.wizard_model.with_context( + {"active_ids": self.invoice_with_payment_term.ids} + ) + ) + self.assertTrue(wizard_form.allow_edit_amount_to_transfer) + wizard_form.destination_partner_id = self.partner_3 + wizard = wizard_form.save() + action = wizard.action_create_move() + self.assertEqual(self.invoice_with_payment_term.amount_residual, 0.0) + new_moves = action.get("domain", []) and self.AccountMove.browse( + action.get("domain", [])[0][2] + ) + self.assertEqual(len(new_moves.mapped("line_ids")), 4) diff --git a/account_move_transfer_partner/wizard/__init__.py b/account_move_transfer_partner/wizard/__init__.py new file mode 100644 index 000000000..4d833f77d --- /dev/null +++ b/account_move_transfer_partner/wizard/__init__.py @@ -0,0 +1 @@ +from . import wizard_account_move_transfer_partner diff --git a/account_move_transfer_partner/wizard/wizard_account_move_transfer_partner.py b/account_move_transfer_partner/wizard/wizard_account_move_transfer_partner.py new file mode 100644 index 000000000..4e1ba189c --- /dev/null +++ b/account_move_transfer_partner/wizard/wizard_account_move_transfer_partner.py @@ -0,0 +1,188 @@ +# Copyright 2022 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError +from odoo.tools import float_compare + + +class WizardAccountMoveTransferPartner(models.TransientModel): + + _name = "wizard.account.move.transfer.partner" + _description = "Wizard to transfer due amount to another partner" + + origin_partner_ids = fields.Many2many( + comodel_name="res.partner", string="Origin Partner", readonly=True + ) + currency_id = fields.Many2one( + comodel_name="res.currency", + string="Currency", + default=lambda self: self.env.company.currency_id.id, + ) + move_ids = fields.Many2many( + comodel_name="account.move", string="Moves Selected", readonly=True + ) + destination_partner_id = fields.Many2one( + comodel_name="res.partner", string="Destination partner", required=True + ) + journal_id = fields.Many2one( + comodel_name="account.journal", + string="Journal", + required=True, + domain=[("type", "=", "general")], + default=lambda self: self.env["account.journal"] + .search([("type", "=", "general")], limit=1) + .id, + ) + account_date = fields.Date( + string="Account date", required=True, default=fields.Date.today() + ) + total_amount_due = fields.Monetary( + string="Total Amount Due", + readonly=True, + currency_field="currency_id", + ) + allow_edit_amount_to_transfer = fields.Boolean( + string="Allow edit amount to transfer?", + readonly=True, + ) + no_invoice_documents = fields.Boolean(string="No invoice documents?", readonly=True) + amount_to_transfer = fields.Monetary( + string="Amount to transfer", + required=True, + currency_field="currency_id", + ) + + @api.onchange("currency_id", "account_date") + def _onchange_currency_id(self): + if self.currency_id: + total_amount_due = 0 + for move in self.move_ids: + if move._origin: + invoice = move._origin + else: + invoice = move + total_amount_due += invoice.currency_id.with_context( + date=self.account_date or fields.Date.today() + ).compute(invoice.amount_residual, self.currency_id) + self.total_amount_due = total_amount_due + self.amount_to_transfer = self.total_amount_due + + @api.model + def default_get(self, fields_list): + values = super(WizardAccountMoveTransferPartner, self).default_get(fields_list) + moves = ( + self.env["account.move"] + .browse(self.env.context.get("active_ids")) + .filtered(lambda x: x.state == "posted") + ) + values["origin_partner_ids"] = moves.mapped("partner_id").ids + values["move_ids"] = moves.filtered(lambda x: x.is_invoice()).ids + values["no_invoice_documents"] = ( + len(moves.filtered(lambda x: not x.is_invoice())) >= 1 + ) + due_amount = abs(sum(moves.mapped("amount_residual"))) + values["total_amount_due"] = due_amount + values["amount_to_transfer"] = due_amount + values["allow_edit_amount_to_transfer"] = len(moves) == 1 + return values + + def action_create_move(self): + am_model = self.env["account.move"] + aml_model = self.env["account.move.line"].with_context( + check_move_validity=False + ) + if self.amount_to_transfer <= 0: + raise ValidationError(_("Amount to transfer should be bigger than zero")) + if ( + float_compare( + self.amount_to_transfer, + self.total_amount_due, + precision_rounding=self.env.company.currency_id.rounding or 0.01, + ) + == 1 + ): + raise ValidationError( + _( + "Amount to transfer %s should be equal or lower than total amount due %s" + ) + % (self.amount_to_transfer, self.total_amount_due) + ) + new_moves = am_model.browse() + for move in self.move_ids: + new_move = am_model.create( + { + "date": self.account_date, + "journal_id": self.journal_id.id, + "ref": _("Transfer amount due from %s") % (move.display_name), + "state": "draft", + "move_type": "entry", + } + ) + reconcilable_account = move.line_ids.mapped("account_id").filtered( + lambda x: x.user_type_id.type in ("receivable", "payable") + ) + lines = move.line_ids.filtered( + lambda line: line.account_id == reconcilable_account + and not line.reconciled + ) + common_data = { + "account_id": reconcilable_account.id, + "move_id": new_move.id, + "currency_id": self.currency_id.id, + "ref": _("Transfer due amount from %s") % move.display_name, + } + amount_to_apply = ( + self.allow_edit_amount_to_transfer + and self.amount_to_transfer + or move.amount_residual + ) + credit_aml = aml_model.browse() + debit_aml = aml_model.browse() + for line in lines: + amount = min(amount_to_apply, abs(line.amount_residual)) + amount_to_apply -= amount + amount_currency = ( + move.currency_id.id != self.currency_id.id and amount or 0.0 + ) + amount = self.currency_id.with_context(date=self.account_date).compute( + amount, move.currency_id + ) + credit_line_data = common_data.copy() + debit_line_data = common_data.copy() + credit_line_data.update( + { + "partner_id": move.is_inbound() + and move.partner_id.id + or move.is_outbound() + and self.destination_partner_id.id, + "credit": amount, + "debit": 0.0, + "amount_currency": -amount_currency, + "date_maturity": line.date_maturity, + } + ) + debit_line_data.update( + { + "partner_id": move.is_inbound() + and self.destination_partner_id.id + or move.is_outbound() + and move.partner_id.id, + "credit": 0.0, + "debit": amount, + "amount_currency": amount_currency, + "date_maturity": line.date_maturity, + } + ) + credit_aml += aml_model.create(credit_line_data) + debit_aml += aml_model.create(debit_line_data) + new_move.action_post() + if move.is_inbound(): + for aml in credit_aml: + move.js_assign_outstanding_line(aml.id) + if move.is_outbound(): + for aml in debit_aml: + move.js_assign_outstanding_line(aml.id) + new_moves |= new_move + action = self.env.ref("account.action_move_journal_line").read()[0] + action.update({"domain": [("id", "in", new_moves.ids)]}) + return action diff --git a/account_move_transfer_partner/wizard/wizard_account_move_transfer_partner_view.xml b/account_move_transfer_partner/wizard/wizard_account_move_transfer_partner_view.xml new file mode 100644 index 000000000..a9462e891 --- /dev/null +++ b/account_move_transfer_partner/wizard/wizard_account_move_transfer_partner_view.xml @@ -0,0 +1,98 @@ + + + + + + wizard_account_move_transfer_partner_view_form + wizard.account.move.transfer.partner + +
+ + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ + + Transfer amount due to another partner + ir.actions.act_window + wizard.account.move.transfer.partner + form + new + + + +
+
diff --git a/setup/account_move_transfer_partner/odoo/addons/account_move_transfer_partner b/setup/account_move_transfer_partner/odoo/addons/account_move_transfer_partner new file mode 120000 index 000000000..ae059f9e4 --- /dev/null +++ b/setup/account_move_transfer_partner/odoo/addons/account_move_transfer_partner @@ -0,0 +1 @@ +../../../../account_move_transfer_partner \ No newline at end of file diff --git a/setup/account_move_transfer_partner/setup.py b/setup/account_move_transfer_partner/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/account_move_transfer_partner/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From 272fe110e531977b4b54d694a099fd713c749b3a Mon Sep 17 00:00:00 2001 From: Christopher Ormaza Date: Fri, 8 Apr 2022 15:21:32 -0500 Subject: [PATCH 003/197] [FIX] test scripts --- .../tests/test_account_move_transfer_partner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/account_move_transfer_partner/tests/test_account_move_transfer_partner.py b/account_move_transfer_partner/tests/test_account_move_transfer_partner.py index 772bb13f3..fcc3d253e 100644 --- a/account_move_transfer_partner/tests/test_account_move_transfer_partner.py +++ b/account_move_transfer_partner/tests/test_account_move_transfer_partner.py @@ -302,8 +302,8 @@ class TestAccountMoveTransferPartner(TransactionCase): ) wizard_form.currency_id = self.currency_2 self.assertEqual( - wizard_form.total_amount_due, - self.invoice_with_payment_term.amount_residual, + round(wizard_form.total_amount_due, 2), + round(self.invoice_with_payment_term.amount_residual, 2), ) def test_07_account_move_transfer_partner(self): From d5a46d7d1982cf1c65e8b3287ed03a5c6e16f289 Mon Sep 17 00:00:00 2001 From: Lois Rilo Date: Tue, 12 Apr 2022 18:20:12 +0200 Subject: [PATCH 004/197] [FIX] account_chart_update: do not repartion in groups + do append False IDs 1) In tax groups repartition lines are not used, however when creating tax groups from templates the default repartition lines are created. If you run the chart update again, it will detect those "useless" default repartition lines and mark them to removal, raising an error when trying to do so as a minial of 2 repartition lines are needed (on base and one tax). 2) When matching taxes, if not match, do not add `False` to the list. --- account_chart_update/wizard/wizard_chart_update.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/account_chart_update/wizard/wizard_chart_update.py b/account_chart_update/wizard/wizard_chart_update.py index f9f6678ed..63d739ddb 100644 --- a/account_chart_update/wizard/wizard_chart_update.py +++ b/account_chart_update/wizard/wizard_chart_update.py @@ -403,7 +403,9 @@ class WizardUpdateChartsAccounts(models.TransientModel): def find_taxes_by_templates(self, templates): tax_ids = [] for tax in templates: - tax_ids.append(self.find_tax_by_templates(tax)) + tax_id = self.find_tax_by_templates(tax) + if tax_id: + tax_ids.append(tax_id) return self.env["account.tax"].browse(tax_ids) @tools.ormcache("templates") @@ -503,9 +505,10 @@ class WizardUpdateChartsAccounts(models.TransientModel): ) ) - # Mark to be removed the lines not found - remove_ids = [x for x in current_repartition.ids if x not in existing_ids] - result += [(2, x) for x in remove_ids] + if tax.amount_type != "group": + # Mark to be removed the lines not found + remove_ids = [x for x in current_repartition.ids if x not in existing_ids] + result += [(2, x) for x in remove_ids] return result @api.model From 92c0fa27e1884ba373fd0396c14e93d862e38d7f Mon Sep 17 00:00:00 2001 From: Christopher Ormaza Date: Tue, 12 Apr 2022 12:17:10 -0500 Subject: [PATCH 005/197] [ADD] use same wizard on payment records --- .../test_account_move_transfer_partner.py | 36 ++++-- .../wizard_account_move_transfer_partner.py | 108 +++++++++++++----- ...ard_account_move_transfer_partner_view.xml | 12 ++ 3 files changed, 116 insertions(+), 40 deletions(-) diff --git a/account_move_transfer_partner/tests/test_account_move_transfer_partner.py b/account_move_transfer_partner/tests/test_account_move_transfer_partner.py index fcc3d253e..3c52ece1f 100644 --- a/account_move_transfer_partner/tests/test_account_move_transfer_partner.py +++ b/account_move_transfer_partner/tests/test_account_move_transfer_partner.py @@ -181,7 +181,9 @@ class TestAccountMoveTransferPartner(TransactionCase): all_invoices = self.invoice_1 + self.invoice_2 + self.invoice_3 total_residual = sum(i.amount_residual for i in all_invoices) wizard_form = Form( - self.wizard_model.with_context({"active_ids": self.invoice_1.ids}) + self.wizard_model.with_context( + {"active_ids": self.invoice_1.ids, "active_model": "account.move"} + ) ) self.assertTrue(wizard_form.allow_edit_amount_to_transfer) wizard_form.amount_to_transfer = self.invoice_1.amount_residual + 1 @@ -197,7 +199,9 @@ class TestAccountMoveTransferPartner(TransactionCase): with self.assertRaises(ValidationError): wizard.action_create_move() wizard_form = Form( - self.wizard_model.with_context({"active_ids": all_invoices.ids}) + self.wizard_model.with_context( + {"active_ids": all_invoices.ids, "active_model": "account.move"} + ) ) with self.assertRaises(AssertionError): wizard_form.amount_to_transfer = total_residual + 1 @@ -220,7 +224,9 @@ class TestAccountMoveTransferPartner(TransactionCase): def test_02_account_move_transfer_partner(self): wizard_form = Form( - self.wizard_model.with_context({"active_ids": self.in_invoice.ids}) + self.wizard_model.with_context( + {"active_ids": self.in_invoice.ids, "active_model": "account.move"} + ) ) self.assertTrue(wizard_form.allow_edit_amount_to_transfer) current_residual_amount = self.invoice_1.amount_residual @@ -245,7 +251,9 @@ class TestAccountMoveTransferPartner(TransactionCase): def test_03_account_move_transfer_partner(self): wizard_form = Form( - self.wizard_model.with_context({"active_ids": self.out_refund.ids}) + self.wizard_model.with_context( + {"active_ids": self.out_refund.ids, "active_model": "account.move"} + ) ) self.assertTrue(wizard_form.allow_edit_amount_to_transfer) wizard_form.destination_partner_id = self.partner_3 @@ -255,7 +263,9 @@ class TestAccountMoveTransferPartner(TransactionCase): def test_04_account_move_transfer_partner(self): wizard_form = Form( - self.wizard_model.with_context({"active_ids": self.in_refund.ids}) + self.wizard_model.with_context( + {"active_ids": self.in_refund.ids, "active_model": "account.move"} + ) ) self.assertTrue(wizard_form.allow_edit_amount_to_transfer) wizard_form.destination_partner_id = self.partner_3 @@ -265,9 +275,11 @@ class TestAccountMoveTransferPartner(TransactionCase): def test_05_account_move_transfer_partner(self): wizard_form = Form( - self.wizard_model.with_context({"active_ids": self.general_move.ids}) + self.wizard_model.with_context( + {"active_ids": self.general_move.ids, "active_model": "account.move"} + ) ) - self.assertTrue(wizard_form.allow_edit_amount_to_transfer) + self.assertFalse(wizard_form.allow_edit_amount_to_transfer) self.assertTrue(wizard_form.no_invoice_documents) wizard_form.destination_partner_id = self.partner_3 wizard = wizard_form.save() @@ -288,7 +300,10 @@ class TestAccountMoveTransferPartner(TransactionCase): self.invoice_with_payment_term.action_post() wizard_form = Form( self.wizard_model.with_context( - {"active_ids": self.invoice_with_payment_term.ids} + { + "active_ids": self.invoice_with_payment_term.ids, + "active_model": "account.move", + } ) ) self.assertTrue(wizard_form.allow_edit_amount_to_transfer) @@ -320,7 +335,10 @@ class TestAccountMoveTransferPartner(TransactionCase): self.invoice_with_payment_term.action_post() wizard_form = Form( self.wizard_model.with_context( - {"active_ids": self.invoice_with_payment_term.ids} + { + "active_ids": self.invoice_with_payment_term.ids, + "active_model": "account.move", + } ) ) self.assertTrue(wizard_form.allow_edit_amount_to_transfer) diff --git a/account_move_transfer_partner/wizard/wizard_account_move_transfer_partner.py b/account_move_transfer_partner/wizard/wizard_account_move_transfer_partner.py index 4e1ba189c..ea95b8758 100644 --- a/account_move_transfer_partner/wizard/wizard_account_move_transfer_partner.py +++ b/account_move_transfer_partner/wizard/wizard_account_move_transfer_partner.py @@ -58,32 +58,53 @@ class WizardAccountMoveTransferPartner(models.TransientModel): total_amount_due = 0 for move in self.move_ids: if move._origin: - invoice = move._origin + real_move = move._origin else: - invoice = move - total_amount_due += invoice.currency_id.with_context( + real_move = move + total_amount_due += real_move.currency_id.with_context( date=self.account_date or fields.Date.today() - ).compute(invoice.amount_residual, self.currency_id) + ).compute(real_move.amount_residual, self.currency_id) + if real_move.payment_id: + ( + liquidity_lines, + counterpart_lines, + writeoff_lines, + ) = real_move.payment_id._seek_for_lines() + payment_amount_due = abs( + sum(counterpart_lines.mapped("amount_residual")) + ) + total_amount_due += real_move.payment_id.currency_id.with_context( + date=self.account_date or fields.Date.today() + ).compute(payment_amount_due, self.currency_id) self.total_amount_due = total_amount_due self.amount_to_transfer = self.total_amount_due @api.model def default_get(self, fields_list): values = super(WizardAccountMoveTransferPartner, self).default_get(fields_list) - moves = ( - self.env["account.move"] - .browse(self.env.context.get("active_ids")) - .filtered(lambda x: x.state == "posted") - ) + current_model = self.env.context.get("active_model") + moves = self.env["account.move"].browse() + records = self.env[current_model].browse(self.env.context.get("active_ids")) + if current_model == "account.payment": + moves = records.mapped("move_id") + elif current_model == "account.move": + moves = records + moves = moves.filtered(lambda x: x.state == "posted") + allowed_moves = moves.filtered(lambda x: x.is_invoice() or x.payment_id) values["origin_partner_ids"] = moves.mapped("partner_id").ids - values["move_ids"] = moves.filtered(lambda x: x.is_invoice()).ids - values["no_invoice_documents"] = ( - len(moves.filtered(lambda x: not x.is_invoice())) >= 1 - ) - due_amount = abs(sum(moves.mapped("amount_residual"))) + values["move_ids"] = allowed_moves.ids + values["no_invoice_documents"] = len(moves - allowed_moves) >= 1 + due_amount = abs(sum(allowed_moves.mapped("amount_residual"))) + for payment in allowed_moves.mapped("payment_id"): + ( + liquidity_lines, + counterpart_lines, + writeoff_lines, + ) = payment._seek_for_lines() + due_amount += abs(sum(counterpart_lines.mapped("amount_residual"))) values["total_amount_due"] = due_amount values["amount_to_transfer"] = due_amount - values["allow_edit_amount_to_transfer"] = len(moves) == 1 + values["allow_edit_amount_to_transfer"] = len(allowed_moves) == 1 return values def action_create_move(self): @@ -121,10 +142,18 @@ class WizardAccountMoveTransferPartner(models.TransientModel): reconcilable_account = move.line_ids.mapped("account_id").filtered( lambda x: x.user_type_id.type in ("receivable", "payable") ) - lines = move.line_ids.filtered( - lambda line: line.account_id == reconcilable_account - and not line.reconciled - ) + if move.payment_id: + ( + liquidity_lines, + counterpart_lines, + writeoff_lines, + ) = move.payment_id._seek_for_lines() + lines = counterpart_lines.filtered(lambda x: not x.reconciled) + else: + lines = move.line_ids.filtered( + lambda line: line.account_id == reconcilable_account + and not line.reconciled + ) common_data = { "account_id": reconcilable_account.id, "move_id": new_move.id, @@ -149,11 +178,18 @@ class WizardAccountMoveTransferPartner(models.TransientModel): ) credit_line_data = common_data.copy() debit_line_data = common_data.copy() + is_inbound = ( + move.is_inbound() or move.payment_id.partner_type == "supplier" + ) + is_outbound = ( + move.is_outbound() or move.payment_id.partner_type == "customer" + ) + partner = line.move_id.payment_id.partner_id or line.move_id.partner_id credit_line_data.update( { - "partner_id": move.is_inbound() - and move.partner_id.id - or move.is_outbound() + "partner_id": is_inbound + and partner.id + or is_outbound and self.destination_partner_id.id, "credit": amount, "debit": 0.0, @@ -163,10 +199,10 @@ class WizardAccountMoveTransferPartner(models.TransientModel): ) debit_line_data.update( { - "partner_id": move.is_inbound() + "partner_id": is_inbound and self.destination_partner_id.id - or move.is_outbound() - and move.partner_id.id, + or is_outbound + and partner.id, "credit": 0.0, "debit": amount, "amount_currency": amount_currency, @@ -176,12 +212,22 @@ class WizardAccountMoveTransferPartner(models.TransientModel): credit_aml += aml_model.create(credit_line_data) debit_aml += aml_model.create(debit_line_data) new_move.action_post() - if move.is_inbound(): - for aml in credit_aml: - move.js_assign_outstanding_line(aml.id) - if move.is_outbound(): - for aml in debit_aml: - move.js_assign_outstanding_line(aml.id) + if is_inbound: + if move.payment_id: + lines_not_reconciled = lines.filtered(lambda x: not x.reconciled) + lines_not_reconciled |= credit_aml + lines_not_reconciled.reconcile() + else: + for aml in credit_aml: + move.js_assign_outstanding_line(aml.id) + if is_outbound: + if move.payment_id: + lines_not_reconciled = lines.filtered(lambda x: not x.reconciled) + lines_not_reconciled |= debit_aml + lines_not_reconciled.reconcile() + else: + for aml in debit_aml: + move.js_assign_outstanding_line(aml.id) new_moves |= new_move action = self.env.ref("account.action_move_journal_line").read()[0] action.update({"domain": [("id", "in", new_moves.ids)]}) diff --git a/account_move_transfer_partner/wizard/wizard_account_move_transfer_partner_view.xml b/account_move_transfer_partner/wizard/wizard_account_move_transfer_partner_view.xml index a9462e891..5ed76a599 100644 --- a/account_move_transfer_partner/wizard/wizard_account_move_transfer_partner_view.xml +++ b/account_move_transfer_partner/wizard/wizard_account_move_transfer_partner_view.xml @@ -94,5 +94,17 @@ + + Transfer amount due to another partner + ir.actions.act_window + wizard.account.move.transfer.partner + form + new + + + From ccc5d0b27ea5a9abe2f1f9f19336b32c9c733040 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 29 Nov 2022 23:30:33 +0100 Subject: [PATCH 006/197] [IMP] account_asset_management: big code cleanup Use fields.Monetary() instead of fields.Float() Use odoo's compare and round methods for floats Add check_company=True where relevant Stop using "Account" decimal precision... it doesn't exist any more! Replace compute() by _convert() for currency conversion --- .../models/account_account.py | 3 +- .../models/account_asset.py | 68 +++++++++++-------- .../models/account_asset_line.py | 39 ++++++----- .../models/account_move.py | 14 ++-- .../views/account_account.xml | 12 +++- .../views/account_asset.xml | 28 +++----- .../wizard/account_asset_remove.py | 45 +++++++----- 7 files changed, 117 insertions(+), 92 deletions(-) diff --git a/account_asset_management/models/account_account.py b/account_asset_management/models/account_account.py index 6e1b0f360..3e7104c9c 100644 --- a/account_asset_management/models/account_account.py +++ b/account_asset_management/models/account_account.py @@ -11,7 +11,8 @@ class AccountAccount(models.Model): asset_profile_id = fields.Many2one( comodel_name="account.asset.profile", string="Asset Profile", - help="Default Asset Profile when creating invoice lines " "with this account.", + check_company=True, + help="Default Asset Profile when creating invoice lines with this account.", ) @api.constrains("asset_profile_id") diff --git a/account_asset_management/models/account_asset.py b/account_asset_management/models/account_asset.py index f6abd6436..6cb600ea7 100644 --- a/account_asset_management/models/account_asset.py +++ b/account_asset_management/models/account_asset.py @@ -58,40 +58,41 @@ class AccountAsset(models.Model): size=32, states=READONLY_STATES, ) - purchase_value = fields.Float( + purchase_value = fields.Monetary( string="Purchase Value", required=True, states=READONLY_STATES, + currency_field="company_currency_id", help="This amount represent the initial value of the asset." "\nThe Depreciation Base is calculated as follows:" "\nPurchase Value - Salvage Value.", ) - salvage_value = fields.Float( + salvage_value = fields.Monetary( string="Salvage Value", - digits="Account", states=READONLY_STATES, + currency_field="company_currency_id", help="The estimated value that an asset will realize upon " "its sale at the end of its useful life.\n" "This value is used to determine the depreciation amounts.", ) - depreciation_base = fields.Float( + depreciation_base = fields.Monetary( compute="_compute_depreciation_base", - digits="Account", string="Depreciation Base", store=True, + currency_field="company_currency_id", help="This amount represent the depreciation base " "of the asset (Purchase Value - Salvage Value).", ) - value_residual = fields.Float( + value_residual = fields.Monetary( compute="_compute_depreciation", - digits="Account", string="Residual Value", + currency_field="company_currency_id", store=True, ) - value_depreciated = fields.Float( + value_depreciated = fields.Monetary( compute="_compute_depreciation", - digits="Account", string="Depreciated Value", + currency_field="company_currency_id", store=True, ) note = fields.Text("Note") @@ -270,7 +271,6 @@ class AccountAsset(models.Model): related="company_id.currency_id", string="Company Currency", store=True, - readonly=True, ) account_analytic_id = fields.Many2one( comodel_name="account.analytic.account", @@ -611,8 +611,8 @@ class AccountAsset(models.Model): last_line, posted_lines, ): - digits = self.env["decimal.precision"].precision_get("Account") company = self.company_id + currency = company.currency_id fiscalyear_lock_date = company.fiscalyear_lock_date or fields.Date.to_date( "1901-01-01" ) @@ -643,14 +643,14 @@ class AccountAsset(models.Model): if amount or self.carry_forward_missed_depreciations: vals = { "previous_id": depr_line.id, - "amount": round(amount, digits), + "amount": currency.round(amount), "asset_id": self.id, "name": name, "line_date": line["date"], "line_days": line["days"], "init_entry": fiscalyear_lock_date >= line["date"], } - depreciated_value += round(amount, digits) + depreciated_value += currency.round(amount) depr_line = self.env["account.asset.line"].create(vals) else: seq -= 1 @@ -659,10 +659,10 @@ class AccountAsset(models.Model): def compute_depreciation_board(self): line_obj = self.env["account.asset.line"] - digits = self.env["decimal.precision"].precision_get("Account") for asset in self: - if asset.value_residual == 0.0: + currency = asset.company_id.currency_id + if currency.is_zero(asset.value_residual): continue domain = [ ("asset_id", "=", asset.id), @@ -744,7 +744,7 @@ class AccountAsset(models.Model): [posted_line.amount for posted_line in posted_lines] ) residual_amount = asset.depreciation_base - depreciated_value - amount_diff = round(residual_amount_table - residual_amount, digits) + amount_diff = currency.round(residual_amount_table - residual_amount) if amount_diff: # We will auto-create a new line because the number of lines in # the tables are the same as the posted depreciations and there @@ -991,7 +991,8 @@ class AccountAsset(models.Model): def _compute_depreciation_amount_per_fiscal_year( self, table, line_dates, depreciation_start_date, depreciation_stop_date ): - digits = self.env["decimal.precision"].precision_get("Account") + self.ensure_one() + currency = self.company_id.currency_id fy_residual_amount = self.depreciation_base i_max = len(table) - 1 asset_sign = self.depreciation_base >= 0 and 1 or -1 @@ -1023,17 +1024,22 @@ class AccountAsset(models.Model): firstyear = i == 0 and True or False fy_factor = self._get_fy_duration_factor(entry, firstyear) fy_amount = year_amount * fy_factor - if asset_sign * (fy_amount - fy_residual_amount) > 0: + if ( + currency.compare_amounts( + asset_sign * (fy_amount - fy_residual_amount), 0 + ) + > 0 + ): fy_amount = fy_residual_amount - period_amount = round(period_amount, digits) - fy_amount = round(fy_amount, digits) + period_amount = currency.round(period_amount) + fy_amount = currency.round(fy_amount) else: fy_amount = False if self.method_time == "number": number = self.method_number else: number = len(line_dates) - period_amount = round(self.depreciation_base / number, digits) + period_amount = currency.round(self.depreciation_base / number) entry.update( { "period_amount": period_amount, @@ -1043,7 +1049,7 @@ class AccountAsset(models.Model): ) if self.method_time == "year": fy_residual_amount -= fy_amount - if round(fy_residual_amount, digits) == 0: + if currency.is_zero(fy_residual_amount): break i_max = i table = table[: i_max + 1] @@ -1053,7 +1059,8 @@ class AccountAsset(models.Model): self, table, depreciation_start_date, depreciation_stop_date, line_dates ): - digits = self.env["decimal.precision"].precision_get("Account") + self.ensure_one() + currency = self.company_id.currency_id asset_sign = 1 if self.depreciation_base >= 0 else -1 i_max = len(table) - 1 remaining_value = self.depreciation_base @@ -1072,7 +1079,7 @@ class AccountAsset(models.Model): prev_date = max(entry["date_start"], depreciation_start_date) for li, line_date in enumerate(line_dates): line_days = (line_date - prev_date).days + 1 - if round(remaining_value, digits) == 0.0: + if currency.is_zero(remaining_value): break if line_date > min(entry["date_stop"], depreciation_stop_date) and not ( @@ -1085,20 +1092,23 @@ class AccountAsset(models.Model): if ( self.method == "degr-linear" - and asset_sign * (fy_amount - fy_amount_check) < 0 + and currency.compare_amounts( + asset_sign * (fy_amount - fy_amount_check), 0 + ) + < 0 ): break if i == 0 and li == 0: - if entry.get("day_amount") > 0.0: + if currency.compare_amounts(entry.get("day_amount"), 0) > 0: amount = line_days * entry.get("day_amount") else: amount = self._get_first_period_amount( table, entry, depreciation_start_date, line_dates ) - amount = round(amount, digits) + amount = currency.round(amount) else: - if entry.get("day_amount") > 0.0: + if currency.compare_amounts(entry.get("day_amount"), 0) > 0: amount = line_days * entry.get("day_amount") else: amount = entry.get("period_amount") @@ -1131,7 +1141,7 @@ class AccountAsset(models.Model): # The code has now been simplified with compensation # always in last FT depreciation line. if self.method_time == "year" and not entry.get("day_amount"): - if round(fy_amount_check - fy_amount, digits) != 0: + if not currency.is_zero(fy_amount_check - fy_amount): diff = fy_amount_check - fy_amount amount = amount - diff remaining_value += diff diff --git a/account_asset_management/models/account_asset_line.py b/account_asset_management/models/account_asset_line.py index e51e6a35f..94e7e8a54 100644 --- a/account_asset_management/models/account_asset_line.py +++ b/account_asset_management/models/account_asset_line.py @@ -26,23 +26,28 @@ class AccountAssetLine(models.Model): readonly=True, ) parent_state = fields.Selection( - related="asset_id.state", string="State of Asset", readonly=True + related="asset_id.state", + string="State of Asset", ) - depreciation_base = fields.Float( - related="asset_id.depreciation_base", string="Depreciation Base", readonly=True + depreciation_base = fields.Monetary( + related="asset_id.depreciation_base", + string="Depreciation Base", + currency_field="company_currency_id", ) - amount = fields.Float(string="Amount", digits="Account", required=True) - remaining_value = fields.Float( + amount = fields.Monetary( + string="Amount", required=True, currency_field="company_currency_id" + ) + remaining_value = fields.Monetary( compute="_compute_values", - digits="Account", string="Next Period Depreciation", store=True, + currency_field="company_currency_id", ) - depreciated_value = fields.Float( + depreciated_value = fields.Monetary( compute="_compute_values", - digits="Account", string="Amount Already Depreciated", store=True, + currency_field="company_currency_id", ) line_date = fields.Date(string="Date", required=True) line_days = fields.Integer(string="Days", readonly=True) @@ -69,11 +74,9 @@ class AccountAssetLine(models.Model): help="Set this flag for entries of previous fiscal years " "for which Odoo has not generated accounting entries.", ) - company_id = fields.Many2one( - "res.company", - store=True, - readonly=True, - related="asset_id.company_id", + company_id = fields.Many2one(related="asset_id.company_id", store=True) + company_currency_id = fields.Many2one( + related="asset_id.company_id.currency_id", store=True, string="Company Currency" ) @api.depends("amount", "previous_id", "type") @@ -228,15 +231,17 @@ class AccountAssetLine(models.Model): def _setup_move_line_data(self, depreciation_date, account, ml_type, move): """Prepare data to be propagated to account.move.line""" asset = self.asset_id + currency = asset.company_id.currency_id amount = self.amount + amount_comp = currency.compare_amounts(amount, 0) analytic_id = False analytic_tags = self.env["account.analytic.tag"] if ml_type == "depreciation": - debit = amount < 0 and -amount or 0.0 - credit = amount > 0 and amount or 0.0 + debit = amount_comp < 0 and -amount or 0.0 + credit = amount_comp > 0 and amount or 0.0 elif ml_type == "expense": - debit = amount > 0 and amount or 0.0 - credit = amount < 0 and -amount or 0.0 + debit = amount_comp > 0 and amount or 0.0 + credit = amount_comp < 0 and -amount or 0.0 analytic_id = asset.account_analytic_id.id analytic_tags = asset.analytic_tag_ids move_line_data = { diff --git a/account_asset_management/models/account_move.py b/account_asset_management/models/account_move.py index 6d2b6a741..92b17c95a 100644 --- a/account_asset_management/models/account_move.py +++ b/account_asset_management/models/account_move.py @@ -33,13 +33,12 @@ class AccountMove(models.Model): asset_count = fields.Integer(compute="_compute_asset_count") def _compute_asset_count(self): - for rec in self: - assets = ( - self.env["account.asset.line"] - .search([("move_id", "=", rec.id)]) - .mapped("asset_id") - ) - rec.asset_count = len(assets) + rg_res = self.env["account.asset.line"].read_group( + [("move_id", "in", self.ids)], ["move_id"], ["move_id"] + ) + mapped_data = {x["move_id"][0]: x["move_id_count"] for x in rg_res} + for move in self: + move.asset_count = mapped_data.get(move.id, 0) def unlink(self): # for move in self: @@ -180,6 +179,7 @@ class AccountMoveLine(models.Model): comodel_name="account.asset", string="Asset", ondelete="restrict", + check_company=True, ) @api.depends("account_id", "asset_id") diff --git a/account_asset_management/views/account_account.xml b/account_asset_management/views/account_account.xml index dec4cc503..8fabfc60d 100644 --- a/account_asset_management/views/account_account.xml +++ b/account_asset_management/views/account_account.xml @@ -5,8 +5,18 @@ account.account - + + + + + + + account.account + + + + diff --git a/account_asset_management/views/account_asset.xml b/account_asset_management/views/account_asset.xml index 99f6e26cd..ff7adb716 100644 --- a/account_asset_management/views/account_asset.xml +++ b/account_asset_management/views/account_asset.xml @@ -70,21 +70,9 @@ /> - - - + + + @@ -93,8 +81,6 @@ @@ -187,6 +171,7 @@ +