From c9cb8ba71ca4a80541dc68d0111d46e4168ae0bd Mon Sep 17 00:00:00 2001 From: CristianoMafraJunior Date: Mon, 25 Mar 2024 10:08:55 -0300 Subject: [PATCH] [IMP] account_invoice_overdue_warn: filter by delay lines --- .../models/res_partner.py | 64 +++-- .../tests/__init__.py | 1 + .../tests/test_overdue_warn_portion.py | 258 ++++++++++++++++++ 3 files changed, 297 insertions(+), 26 deletions(-) create mode 100644 account_invoice_overdue_warn/tests/test_overdue_warn_portion.py diff --git a/account_invoice_overdue_warn/models/res_partner.py b/account_invoice_overdue_warn/models/res_partner.py index af9f00d0d..6e57a0d00 100644 --- a/account_invoice_overdue_warn/models/res_partner.py +++ b/account_invoice_overdue_warn/models/res_partner.py @@ -1,5 +1,6 @@ # Copyright 2021 Akretion France (http://www.akretion.com/) # @author: Alexis de Lattre +# Copyright 2024 Engenere.one # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import fields, models @@ -32,43 +33,54 @@ def _compute_overdue_invoice_count_amount(self): partner.overdue_invoice_count = count partner.overdue_invoice_amount = amount_company_currency - def _prepare_overdue_invoice_count_amount(self, company_id): - # This method is also called by the module - # account_invoice_overdue_warn_sale where the company_id arg is used - self.ensure_one() - domain = self._prepare_overdue_invoice_domain(company_id) - # amount_residual_signed is in company currency - rg_res = self.env["account.move"].read_group( - domain, ["amount_residual_signed"], [] - ) - count = 0 - overdue_invoice_amount = 0.0 - if rg_res: - count = rg_res[0]["__count"] - overdue_invoice_amount = rg_res[0]["amount_residual_signed"] - return (count, overdue_invoice_amount) - def _prepare_overdue_invoice_domain(self, company_id): - # The use of commercial_partner_id is in this method self.ensure_one() today = fields.Date.context_today(self) if company_id is None: company_id = self.env.company.id - domain = [ - ("move_type", "=", "out_invoice"), - ("company_id", "=", company_id), - ("commercial_partner_id", "=", self.commercial_partner_id.id), - ("invoice_date_due", "<", today), - ("state", "=", "posted"), - ("payment_state", "in", ("not_paid", "partial")), + overdue_move_lines_domain = [ + ("move_id.company_id", "=", company_id), + ("move_id.commercial_partner_id", "=", self.commercial_partner_id.id), + ("date_maturity", "<", today), + ("move_id.state", "=", "posted"), + ("reconciled", "=", False), + ("account_internal_type", "=", "receivable"), ] - return domain + overdue_move_lines = self.env["account.move.line"].search( + overdue_move_lines_domain + ) + overdue_invoice_ids = overdue_move_lines.mapped("move_id").ids + overdue_invoice_domain = [("id", "in", overdue_invoice_ids)] + return overdue_invoice_domain + + def _prepare_overdue_invoice_count_amount(self, company_id): + self.ensure_one() + overdue_invoice_domain = self._prepare_overdue_invoice_domain(company_id) + overdue_invoices = self.env["account.move"].search(overdue_invoice_domain) + + overdue_invoice_ids = set() + overdue_invoice_amount = 0 + + for invoice in overdue_invoices: + for line in invoice.line_ids: + if ( + line.date_maturity + and line.date_maturity < fields.Date.today() + and not line.reconciled + ): + overdue_invoice_ids.add(invoice.id) + overdue_invoice_amount += line.amount_residual + + count = len(overdue_invoice_ids) + + return count, overdue_invoice_amount def _prepare_jump_to_overdue_invoices(self, company_id): action = self.env["ir.actions.actions"]._for_xml_id( "account.action_move_out_invoice_type" ) - action["domain"] = self._prepare_overdue_invoice_domain(company_id) + overdue_invoice_domain = self._prepare_overdue_invoice_domain(company_id) + action["domain"] = overdue_invoice_domain action["context"] = { "journal_type": "sale", "move_type": "out_invoice", diff --git a/account_invoice_overdue_warn/tests/__init__.py b/account_invoice_overdue_warn/tests/__init__.py index 365cd2464..25e615461 100644 --- a/account_invoice_overdue_warn/tests/__init__.py +++ b/account_invoice_overdue_warn/tests/__init__.py @@ -1 +1,2 @@ from . import test_overdue_warn +from . import test_overdue_warn_portion diff --git a/account_invoice_overdue_warn/tests/test_overdue_warn_portion.py b/account_invoice_overdue_warn/tests/test_overdue_warn_portion.py new file mode 100644 index 000000000..aba2ae209 --- /dev/null +++ b/account_invoice_overdue_warn/tests/test_overdue_warn_portion.py @@ -0,0 +1,258 @@ +# Copyright 2024 Engenere.one +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from datetime import datetime, timedelta + +from odoo.tests import Form, tagged +from odoo.tests.common import TransactionCase + + +@tagged("post_install", "-at_install") +class TestOverdueWarn(TransactionCase): + def setUp(self): + super().setUp() + self.company = self.env.ref("base.main_company") + self.partner = self.env["res.partner"].create( + { + "name": "Teste Partner", + "country_id": self.env.ref("base.br").id, + "company_id": self.company.id, + } + ) + self.today = datetime.now().date() + self.revenue_acc = self.env["account.account"].search( + [ + ("company_id", "=", self.company.id), + ( + "user_type_id", + "=", + self.env.ref("account.data_account_type_revenue").id, + ), + ], + limit=1, + ) + + def test_late_draft_invoice(self): + out_invoice_sketch = self.env["account.move"].create( + { + "partner_id": self.partner.id, + "move_type": "out_invoice", + "company_id": self.company.id, + "currency_id": self.company.currency_id.id, + "invoice_date": self.today - timedelta(days=5), + "invoice_date_due": self.today - timedelta(days=5), + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "test line", + "price_unit": 500, + "quantity": 1, + "account_id": self.revenue_acc.id, + }, + ) + ], + } + ) + self.assertEqual(out_invoice_sketch.state, "draft") + self.assertEqual(self.partner.overdue_invoice_count, 0) + self.assertEqual(self.partner.overdue_invoice_amount, 0.0) + + def test_confirmed_supplier_invoice(self): + out_invoice_supplier = self.env["account.move"].create( + { + "partner_id": self.partner.id, + "move_type": "in_invoice", + "company_id": self.company.id, + "currency_id": self.company.currency_id.id, + "invoice_date": self.today - timedelta(days=5), + "invoice_date_due": self.today - timedelta(days=5), + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "test line", + "price_unit": 500, + "quantity": 1, + "account_id": self.revenue_acc.id, + }, + ) + ], + } + ) + out_invoice_supplier.action_post() + self.assertEqual(self.partner.overdue_invoice_count, 0) + self.assertEqual(self.partner.overdue_invoice_amount, 0.0) + + def test_mixed_case_with_two_invoices(self): + + out_invoice_a = self.env["account.move"].create( + { + "partner_id": self.partner.id, + "move_type": "out_invoice", + "company_id": self.company.id, + "currency_id": self.company.currency_id.id, + "invoice_date": self.today - timedelta(days=10), + "invoice_date_due": self.today - timedelta(days=10), + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "Portion 1", + "price_unit": 300, + "quantity": 1, + "account_id": self.revenue_acc.id, + "date_maturity": self.today - timedelta(days=9), + }, + ), + ( + 0, + 0, + { + "name": "Portion 2", + "price_unit": 300, + "quantity": 1, + "account_id": self.revenue_acc.id, + "date_maturity": self.today - timedelta(days=9), + }, + ), + ( + 0, + 0, + { + "name": "Portion 3", + "price_unit": 300, + "quantity": 1, + "account_id": self.revenue_acc.id, + "date_maturity": self.today - timedelta(days=9), + }, + ), + ], + } + ) + out_invoice_a.action_post() + + out_invoice_b = self.env["account.move"].create( + { + "partner_id": self.partner.id, + "move_type": "out_invoice", + "company_id": self.company.id, + "currency_id": self.company.currency_id.id, + "invoice_date": self.today - timedelta(days=10), + "invoice_date_due": self.today - timedelta(days=10), + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "Portion 1", + "price_unit": 300, + "quantity": 1, + "account_id": self.revenue_acc.id, + "date_maturity": self.today - timedelta(days=9), + }, + ), + ( + 0, + 0, + { + "name": "Portion 2", + "price_unit": 300, + "quantity": 1, + "account_id": self.revenue_acc.id, + "date_maturity": self.today - timedelta(days=9), + }, + ), + ( + 0, + 0, + { + "name": "Portion 3", + "price_unit": 300, + "quantity": 1, + "account_id": self.revenue_acc.id, + "date_maturity": self.today + timedelta(days=20), + }, + ), + ], + } + ) + out_invoice_b.action_post() + + action_data_a = out_invoice_a.action_register_payment() + wizard_a = Form( + self.env["account.payment.register"].with_context(action_data_a["context"]) + ).save() + wizard_a.amount = 450.0 + wizard_a.action_create_payments() + + action_data_b = out_invoice_a.action_register_payment() + wizard_b = Form( + self.env["account.payment.register"].with_context(action_data_b["context"]) + ).save() + wizard_b.amount = 0 + wizard_b.action_create_payments() + + self.assertEqual(self.partner.overdue_invoice_count, 2) + self.assertEqual(self.partner.overdue_invoice_amount, 1050.0) + + def test_confirmed_invoice_with_past_date(self): + out_invoice_past_paid = self.env["account.move"].create( + { + "partner_id": self.partner.id, + "move_type": "out_invoice", + "company_id": self.company.id, + "currency_id": self.company.currency_id.id, + "invoice_date": self.today - timedelta(days=5), + "invoice_date_due": self.today - timedelta(days=5), + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "test line", + "price_unit": 500.0, + "quantity": 1, + "account_id": self.revenue_acc.id, + }, + ) + ], + } + ) + out_invoice_past_paid.action_post() + action_data = out_invoice_past_paid.action_register_payment() + wizard = Form( + self.env["account.payment.register"].with_context(action_data["context"]) + ).save() + wizard.action_create_payments() + self.assertEqual(self.partner.overdue_invoice_count, 0) + self.assertEqual(self.partner.overdue_invoice_amount, 0) + + def test_confirmed_invoice_with_future_date_unpaid(self): + out_invoice_future = self.env["account.move"].create( + { + "partner_id": self.partner.id, + "move_type": "out_invoice", + "company_id": self.company.id, + "currency_id": self.company.currency_id.id, + "invoice_date": self.today + timedelta(days=5), + "invoice_date_due": self.today + timedelta(days=10), + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "test line", + "price_unit": 500, + "quantity": 1, + "account_id": self.revenue_acc.id, + }, + ) + ], + } + ) + out_invoice_future.action_post() + self.assertEqual(self.partner.overdue_invoice_count, 0) + self.assertEqual(self.partner.overdue_invoice_amount, 0)