Skip to content

Commit c3e0b99

Browse files
CristianoMafraJuniorfelipemotter
authored andcommitted
[IMP] account_invoice_overdue_warn: filter by delay lines
1 parent d2e5b54 commit c3e0b99

File tree

3 files changed

+347
-17
lines changed

3 files changed

+347
-17
lines changed

account_invoice_overdue_warn/models/res_partner.py

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Copyright 2021 Akretion France (http://www.akretion.com/)
22
# @author: Alexis de Lattre <[email protected]>
3+
# Copyright 2025 Engenere.one
34
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
45

56
from odoo import fields, models
@@ -32,43 +33,60 @@ def _compute_overdue_invoice_count_amount(self):
3233
partner.overdue_invoice_count = count
3334
partner.overdue_invoice_amount = amount_company_currency
3435

36+
def _get_overdue_move_lines(self, company_id):
37+
domain = self._prepare_overdue_move_lines_domain(company_id)
38+
overdue_move_lines = self.env["account.move.line"].search(domain)
39+
return overdue_move_lines
40+
3541
def _prepare_overdue_invoice_count_amount(self, company_id):
3642
# This method is also called by the module
3743
# account_invoice_overdue_warn_sale where the company_id arg is used
3844
self.ensure_one()
39-
domain = self._prepare_overdue_invoice_domain(company_id)
40-
# amount_residual_signed is in company currency
41-
rg_res = self.env["account.move"].read_group(
42-
domain, ["amount_residual_signed"], []
45+
# amount_residual is in company currency
46+
overdue_move_lines = self._get_overdue_move_lines(company_id)
47+
overdue_invoice_amount = self._compute_overdue_move_lines_total(
48+
overdue_move_lines
4349
)
44-
count = 0
45-
overdue_invoice_amount = 0.0
46-
if rg_res:
47-
count = rg_res[0]["__count"]
48-
overdue_invoice_amount = rg_res[0]["amount_residual_signed"]
50+
count = self._count_unique_invoices(overdue_move_lines)
4951
return (count, overdue_invoice_amount)
5052

51-
def _prepare_overdue_invoice_domain(self, company_id):
53+
def _prepare_overdue_move_lines_domain(self, company_id):
5254
# The use of commercial_partner_id is in this method
5355
self.ensure_one()
5456
today = fields.Date.context_today(self)
5557
if company_id is None:
5658
company_id = self.env.company.id
5759
domain = [
58-
("move_type", "=", "out_invoice"),
59-
("company_id", "=", company_id),
60-
("commercial_partner_id", "=", self.commercial_partner_id.id),
61-
("invoice_date_due", "<", today),
62-
("state", "=", "posted"),
63-
("payment_state", "in", ("not_paid", "partial")),
60+
("move_id.company_id", "=", company_id),
61+
("move_id.commercial_partner_id", "=", self.commercial_partner_id.id),
62+
("date_maturity", "<", today),
63+
("move_id.state", "=", "posted"),
64+
("reconciled", "=", False),
65+
("account_internal_type", "=", "receivable"),
66+
("amount_residual", "!=", 0.0),
6467
]
6568
return domain
6669

70+
def _compute_overdue_move_lines_total(self, overdue_move_lines):
71+
return sum(overdue_move_lines.mapped("amount_residual"))
72+
73+
def _get_unique_invoices_id_list(self, overdue_move_lines):
74+
return overdue_move_lines.mapped("move_id").ids
75+
76+
def _count_unique_invoices(self, overdue_move_lines):
77+
unique_invoices = self._get_unique_invoices_id_list(overdue_move_lines)
78+
return len(unique_invoices)
79+
80+
def _prepare_invoice_domain(self, invoice_ids):
81+
return [("id", "in", invoice_ids)]
82+
6783
def _prepare_jump_to_overdue_invoices(self, company_id):
6884
action = self.env["ir.actions.actions"]._for_xml_id(
6985
"account.action_move_out_invoice_type"
7086
)
71-
action["domain"] = self._prepare_overdue_invoice_domain(company_id)
87+
overdue_move_lines = self._get_overdue_move_lines(company_id)
88+
unique_invoice_ids = self._get_unique_invoices_id_list(overdue_move_lines)
89+
action["domain"] = self._prepare_invoice_domain(unique_invoice_ids)
7290
action["context"] = {
7391
"journal_type": "sale",
7492
"move_type": "out_invoice",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
from . import test_overdue_warn
2+
from . import test_overdue_warn_installment
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
# Copyright 2025 Engenere.one
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
from datetime import datetime, timedelta
4+
5+
from odoo.tests import Form, tagged
6+
from odoo.tests.common import TransactionCase
7+
8+
9+
@tagged("post_install", "-at_install")
10+
class TestOverdueWarn(TransactionCase):
11+
def setUp(self):
12+
super().setUp()
13+
self.company = self.env.ref("base.main_company")
14+
self.partner = self.env["res.partner"].create(
15+
{
16+
"name": "Test Partner",
17+
"country_id": self.env.ref("base.br").id,
18+
"company_id": self.company.id,
19+
}
20+
)
21+
self.today = datetime.now().date()
22+
self.revenue_acc = self.env["account.account"].search(
23+
[
24+
("company_id", "=", self.company.id),
25+
(
26+
"user_type_id",
27+
"=",
28+
self.env.ref("account.data_account_type_revenue").id,
29+
),
30+
],
31+
limit=1,
32+
)
33+
34+
self.payment_term = self.env["account.payment.term"].create(
35+
{
36+
"name": "Immediate payment",
37+
"line_ids": [
38+
(
39+
0,
40+
0,
41+
{
42+
"value": "balance",
43+
"days": 0,
44+
},
45+
),
46+
],
47+
}
48+
)
49+
50+
self.payment_term_installments_a = self.env["account.payment.term"].create(
51+
{
52+
"name": "Pay in 3 installments",
53+
"line_ids": [
54+
(
55+
0,
56+
0,
57+
{
58+
"value": "percent",
59+
"value_amount": 33.33,
60+
"days": 0,
61+
},
62+
),
63+
(
64+
0,
65+
0,
66+
{
67+
"value": "percent",
68+
"value_amount": 33.33,
69+
"days": 0,
70+
},
71+
),
72+
(
73+
0,
74+
0,
75+
{
76+
"value": "balance",
77+
"days": 0,
78+
},
79+
),
80+
],
81+
}
82+
)
83+
84+
self.payment_term_installments_b = self.env["account.payment.term"].create(
85+
{
86+
"name": "Pay in 3 installments",
87+
"line_ids": [
88+
(
89+
0,
90+
0,
91+
{
92+
"value": "percent",
93+
"value_amount": 33.33,
94+
"days": 0,
95+
},
96+
),
97+
(
98+
0,
99+
0,
100+
{
101+
"value": "percent",
102+
"value_amount": 33.33,
103+
"days": 0,
104+
},
105+
),
106+
(
107+
0,
108+
0,
109+
{
110+
"value": "balance",
111+
"days": 14,
112+
},
113+
),
114+
],
115+
}
116+
)
117+
118+
def test_out_invoice_draft(self):
119+
out_invoice_draft = self.env["account.move"].create(
120+
{
121+
"partner_id": self.partner.id,
122+
"move_type": "out_invoice",
123+
"company_id": self.company.id,
124+
"currency_id": self.company.currency_id.id,
125+
"invoice_date": self.today - timedelta(days=9),
126+
"invoice_payment_term_id": self.payment_term.id,
127+
"invoice_line_ids": [
128+
(
129+
0,
130+
0,
131+
{
132+
"name": "Product Test",
133+
"price_unit": 500,
134+
"quantity": 1,
135+
"account_id": self.revenue_acc.id,
136+
},
137+
)
138+
],
139+
}
140+
)
141+
self.assertEqual(
142+
out_invoice_draft.invoice_date_due,
143+
out_invoice_draft.line_ids[1].date_maturity,
144+
)
145+
self.assertEqual(out_invoice_draft.state, "draft")
146+
self.assertEqual(self.partner.overdue_invoice_count, 0)
147+
self.assertEqual(self.partner.overdue_invoice_amount, 0.0)
148+
149+
def test_confirmed_supplier_invoice(self):
150+
out_invoice_supplier = self.env["account.move"].create(
151+
{
152+
"partner_id": self.partner.id,
153+
"move_type": "in_invoice",
154+
"company_id": self.company.id,
155+
"currency_id": self.company.currency_id.id,
156+
"invoice_date": self.today - timedelta(days=9),
157+
"invoice_payment_term_id": self.payment_term.id,
158+
"invoice_line_ids": [
159+
(
160+
0,
161+
0,
162+
{
163+
"name": "Product Test",
164+
"price_unit": 500,
165+
"quantity": 1,
166+
"account_id": self.revenue_acc.id,
167+
},
168+
)
169+
],
170+
}
171+
)
172+
out_invoice_supplier.action_post()
173+
self.assertEqual(
174+
out_invoice_supplier.invoice_date_due,
175+
out_invoice_supplier.line_ids[1].date_maturity,
176+
)
177+
self.assertEqual(self.partner.overdue_invoice_count, 0)
178+
self.assertEqual(self.partner.overdue_invoice_amount, 0.0)
179+
180+
def test_mixed_case_with_two_invoices(self):
181+
182+
out_invoice_a = self.env["account.move"].create(
183+
{
184+
"partner_id": self.partner.id,
185+
"move_type": "out_invoice",
186+
"company_id": self.company.id,
187+
"currency_id": self.company.currency_id.id,
188+
"invoice_date": self.today - timedelta(days=10),
189+
"invoice_payment_term_id": self.payment_term_installments_a.id,
190+
"invoice_line_ids": [
191+
(
192+
0,
193+
0,
194+
{
195+
"name": "Product Test",
196+
"price_unit": 900,
197+
"quantity": 1,
198+
"account_id": self.revenue_acc.id,
199+
},
200+
),
201+
],
202+
}
203+
)
204+
out_invoice_a.action_post()
205+
206+
out_invoice_b = self.env["account.move"].create(
207+
{
208+
"partner_id": self.partner.id,
209+
"move_type": "out_invoice",
210+
"company_id": self.company.id,
211+
"currency_id": self.company.currency_id.id,
212+
"invoice_date": self.today - timedelta(days=10),
213+
"invoice_payment_term_id": self.payment_term_installments_b.id,
214+
"invoice_line_ids": [
215+
(
216+
0,
217+
0,
218+
{
219+
"name": "Product Test",
220+
"price_unit": 900,
221+
"quantity": 1,
222+
"account_id": self.revenue_acc.id,
223+
},
224+
),
225+
],
226+
}
227+
)
228+
out_invoice_b.action_post()
229+
230+
action_data_a = out_invoice_a.action_register_payment()
231+
wizard_a = Form(
232+
self.env["account.payment.register"].with_context(
233+
**action_data_a["context"]
234+
)
235+
).save()
236+
wizard_a.amount = 450.0
237+
wizard_a.action_create_payments()
238+
239+
self.assertEqual(
240+
out_invoice_b.invoice_date_due,
241+
out_invoice_b.line_ids[3].date_maturity,
242+
)
243+
self.assertEqual(self.partner.overdue_invoice_count, 2)
244+
self.assertEqual(self.partner.overdue_invoice_amount, 1049.94)
245+
246+
def test_confirmed_invoice_with_past_date(self):
247+
out_invoice_past_paid = self.env["account.move"].create(
248+
{
249+
"partner_id": self.partner.id,
250+
"move_type": "out_invoice",
251+
"company_id": self.company.id,
252+
"currency_id": self.company.currency_id.id,
253+
"invoice_date": self.today - timedelta(days=5),
254+
"invoice_payment_term_id": self.payment_term.id,
255+
"invoice_line_ids": [
256+
(
257+
0,
258+
0,
259+
{
260+
"name": "Product Test",
261+
"price_unit": 500.0,
262+
"quantity": 1,
263+
"account_id": self.revenue_acc.id,
264+
},
265+
)
266+
],
267+
}
268+
)
269+
out_invoice_past_paid.action_post()
270+
action_data = out_invoice_past_paid.action_register_payment()
271+
wizard = Form(
272+
self.env["account.payment.register"].with_context(**action_data["context"])
273+
).save()
274+
wizard.action_create_payments()
275+
self.assertEqual(
276+
out_invoice_past_paid.invoice_date_due,
277+
out_invoice_past_paid.line_ids[1].date_maturity,
278+
)
279+
self.assertEqual(self.partner.overdue_invoice_count, 0)
280+
self.assertEqual(self.partner.overdue_invoice_amount, 0)
281+
282+
def test_confirmed_invoice_with_future_date_unpaid(self):
283+
out_invoice_future = self.env["account.move"].create(
284+
{
285+
"partner_id": self.partner.id,
286+
"move_type": "out_invoice",
287+
"company_id": self.company.id,
288+
"currency_id": self.company.currency_id.id,
289+
"invoice_date": self.today + timedelta(days=5),
290+
"invoice_payment_term_id": self.payment_term.id,
291+
"invoice_line_ids": [
292+
(
293+
0,
294+
0,
295+
{
296+
"name": "Product Test",
297+
"price_unit": 500,
298+
"quantity": 1,
299+
"account_id": self.revenue_acc.id,
300+
},
301+
)
302+
],
303+
}
304+
)
305+
out_invoice_future.action_post()
306+
self.assertEqual(
307+
out_invoice_future.invoice_date_due,
308+
out_invoice_future.line_ids[1].date_maturity,
309+
)
310+
self.assertEqual(self.partner.overdue_invoice_count, 0)
311+
self.assertEqual(self.partner.overdue_invoice_amount, 0)

0 commit comments

Comments
 (0)