From c875a412b3a770089f6bdefebdcf99261c97b1c8 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 | 52 ++- .../tests/__init__.py | 1 + .../tests/test_overdue_warn_installment.py | 309 ++++++++++++++++++ 3 files changed, 345 insertions(+), 17 deletions(-) create mode 100644 account_invoice_overdue_warn/tests/test_overdue_warn_installment.py diff --git a/account_invoice_overdue_warn/models/res_partner.py b/account_invoice_overdue_warn/models/res_partner.py index af9f00d0d..122afb294 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,60 @@ def _compute_overdue_invoice_count_amount(self): partner.overdue_invoice_count = count partner.overdue_invoice_amount = amount_company_currency + def _get_overdue_move_lines(self, company_id): + domain = self._prepare_overdue_move_lines_domain(company_id) + overdue_move_lines = self.env["account.move.line"].search(domain) + return overdue_move_lines + 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"], [] + # amount_residual is in company currency + overdue_move_lines = self._get_overdue_move_lines(company_id) + overdue_invoice_amount = self._compute_overdue_move_lines_total( + overdue_move_lines ) - count = 0 - overdue_invoice_amount = 0.0 - if rg_res: - count = rg_res[0]["__count"] - overdue_invoice_amount = rg_res[0]["amount_residual_signed"] + count = self._count_unique_invoices(overdue_move_lines) return (count, overdue_invoice_amount) - def _prepare_overdue_invoice_domain(self, company_id): + def _prepare_overdue_move_lines_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")), + ("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"), + ("amount_residual", "!=", 0.0), ] return domain + def _compute_overdue_move_lines_total(self, overdue_move_lines): + return sum(overdue_move_lines.mapped("amount_residual")) + + def _get_unique_invoices_id_list(self, overdue_move_lines): + return overdue_move_lines.mapped("move_id").ids + + def _count_unique_invoices(self, overdue_move_lines): + unique_invoices = self._get_unique_invoices_id_list(overdue_move_lines) + return len(unique_invoices) + + def _prepare_invoice_domain(self, invoice_ids): + return [("id", "in", invoice_ids)] + 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_move_lines = self._get_overdue_move_lines(company_id) + unique_invoice_ids = self._get_unique_invoices_id_list(overdue_move_lines) + action["domain"] = self._prepare_invoice_domain(unique_invoice_ids) 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..8e1338ed9 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_installment diff --git a/account_invoice_overdue_warn/tests/test_overdue_warn_installment.py b/account_invoice_overdue_warn/tests/test_overdue_warn_installment.py new file mode 100644 index 000000000..4b7d68533 --- /dev/null +++ b/account_invoice_overdue_warn/tests/test_overdue_warn_installment.py @@ -0,0 +1,309 @@ +# 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": "Test 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, + ) + + self.payment_term = self.env["account.payment.term"].create( + { + "name": "Immediate payment", + "line_ids": [ + ( + 0, + 0, + { + "value": "balance", + "days": 0, + }, + ), + ], + } + ) + + self.payment_term_installments_a = self.env["account.payment.term"].create( + { + "name": "Pay in 3 installments", + "line_ids": [ + ( + 0, + 0, + { + "value": "percent", + "value_amount": 33.33, + "days": 0, + }, + ), + ( + 0, + 0, + { + "value": "percent", + "value_amount": 33.33, + "days": 0, + }, + ), + ( + 0, + 0, + { + "value": "balance", + "days": 0, + }, + ), + ], + } + ) + + self.payment_term_installments_b = self.env["account.payment.term"].create( + { + "name": "Pay in 3 installments", + "line_ids": [ + ( + 0, + 0, + { + "value": "percent", + "value_amount": 33.33, + "days": 0, + }, + ), + ( + 0, + 0, + { + "value": "percent", + "value_amount": 33.33, + "days": 0, + }, + ), + ( + 0, + 0, + { + "value": "balance", + "days": 14, + }, + ), + ], + } + ) + + def test_out_invoice_draft(self): + out_invoice_draft = 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=9), + "invoice_payment_term_id": self.payment_term.id, + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "Product Test", + "price_unit": 500, + "quantity": 1, + "account_id": self.revenue_acc.id, + }, + ) + ], + } + ) + self.assertEqual( + out_invoice_draft.invoice_date_due, + out_invoice_draft.line_ids[1].date_maturity, + ) + self.assertEqual(out_invoice_draft.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=9), + "invoice_payment_term_id": self.payment_term.id, + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "Product Test", + "price_unit": 500, + "quantity": 1, + "account_id": self.revenue_acc.id, + }, + ) + ], + } + ) + out_invoice_supplier.action_post() + self.assertEqual( + out_invoice_supplier.invoice_date_due, + out_invoice_supplier.line_ids[1].date_maturity, + ) + 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_payment_term_id": self.payment_term_installments_a.id, + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "Product Test", + "price_unit": 900, + "quantity": 1, + "account_id": self.revenue_acc.id, + }, + ), + ], + } + ) + 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_payment_term_id": self.payment_term_installments_b.id, + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "Product Test", + "price_unit": 900, + "quantity": 1, + "account_id": self.revenue_acc.id, + }, + ), + ], + } + ) + 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() + + self.assertEqual( + out_invoice_b.invoice_date_due, + out_invoice_b.line_ids[3].date_maturity, + ) + self.assertEqual(self.partner.overdue_invoice_count, 2) + self.assertEqual(self.partner.overdue_invoice_amount, 1049.94) + + 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_payment_term_id": self.payment_term.id, + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "Product Test", + "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( + out_invoice_past_paid.invoice_date_due, + out_invoice_past_paid.line_ids[1].date_maturity, + ) + 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_payment_term_id": self.payment_term.id, + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "Product Test", + "price_unit": 500, + "quantity": 1, + "account_id": self.revenue_acc.id, + }, + ) + ], + } + ) + out_invoice_future.action_post() + self.assertEqual( + out_invoice_future.invoice_date_due, + out_invoice_future.line_ids[1].date_maturity, + ) + self.assertEqual(self.partner.overdue_invoice_count, 0) + self.assertEqual(self.partner.overdue_invoice_amount, 0)