diff --git a/account_check_report/__init__.py b/account_check_report/__init__.py
index 4c4f242fa03b..59b4cb501816 100644
--- a/account_check_report/__init__.py
+++ b/account_check_report/__init__.py
@@ -1 +1,2 @@
from . import report
+from . import models
diff --git a/account_check_report/models/__init__.py b/account_check_report/models/__init__.py
new file mode 100644
index 000000000000..ab350b87bb10
--- /dev/null
+++ b/account_check_report/models/__init__.py
@@ -0,0 +1 @@
+from . import account_payment
diff --git a/account_check_report/models/account_payment.py b/account_check_report/models/account_payment.py
new file mode 100644
index 000000000000..d2883a87b4b1
--- /dev/null
+++ b/account_check_report/models/account_payment.py
@@ -0,0 +1,92 @@
+# 2016-2024 ForgeFlow S.L.
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+from odoo import api, models
+from odoo.osv import expression
+
+
+class AccountPayment(models.Model):
+ _inherit = "account.payment"
+
+ @api.depends(
+ "move_id.line_ids.matched_debit_ids", "move_id.line_ids.matched_credit_ids"
+ )
+ def _compute_stat_buttons_from_reconciliation(self):
+ res = super()._compute_stat_buttons_from_reconciliation()
+ for rec in self:
+ if rec.payment_type == "outbound":
+ open_action = rec.button_open_bills()
+ result_domain = open_action.get("domain", False)
+ if not result_domain:
+ return res
+ rec.reconciled_bills_count = len(
+ self.env["account.move"].search(result_domain)
+ )
+ else:
+ open_action = rec.button_open_invoices()
+ result_domain = open_action.get("domain", False)
+ if not result_domain:
+ return res
+ rec.reconciled_invoices_count = len(
+ self.env["account.move"].search(result_domain)
+ )
+ return res
+
+ def get_direct_refunds(self):
+ if self.payment_type == "outbound":
+ move_lines = self.reconciled_bill_ids.mapped("line_ids")
+ else:
+ move_lines = self.reconciled_invoice_ids.mapped("line_ids")
+ rec_lines = move_lines.filtered(
+ lambda x: x.account_id.reconcile
+ and x.account_id == self.destination_account_id
+ and x.partner_id == self.partner_id
+ )
+ # include direct refunds
+ if self.partner_type == "customer":
+ invoice_ids = rec_lines.mapped(
+ "matched_credit_ids.credit_move_id.full_reconcile_id."
+ "reconciled_line_ids.move_id"
+ ).filtered(lambda i: i.date <= self.date)
+ elif self.partner_type == "supplier":
+ invoice_ids = rec_lines.mapped(
+ "matched_debit_ids.debit_move_id.full_reconcile_id."
+ "reconciled_line_ids.move_id"
+ ).filtered(lambda i: i.date <= self.date)
+ # include other invoices where the payment was applied
+ invoice_ids += rec_lines.mapped(
+ "matched_credit_ids.credit_move_id.move_id"
+ ) + rec_lines.mapped("matched_debit_ids.debit_move_id.move_id")
+ if not invoice_ids:
+ return False
+ invoice_ids -= self.move_id
+ return invoice_ids
+
+ def get_direct_refunds_domain(self):
+ invoices = self.get_direct_refunds()
+ if invoices:
+ return [("id", "in", invoices.ids)]
+ return []
+
+ def button_open_invoices(self):
+ res = super(AccountPayment, self).button_open_invoices()
+ if not res.get("domain", False):
+ return res
+ result_domain = res.get("domain", False)
+ direct_refunds_domain = self.get_direct_refunds_domain()
+ if direct_refunds_domain:
+ res["domain"] = expression.OR([result_domain, direct_refunds_domain])
+ return res
+
+ def button_open_bills(self):
+ """
+ Include direct refunds, those are not linked to the payments
+ directly
+ """
+ res = super(AccountPayment, self).button_open_bills()
+ if not res.get("domain", False):
+ return res
+ result_domain = res.get("domain", False)
+ direct_refunds_domain = self.get_direct_refunds_domain()
+ if direct_refunds_domain:
+ res["domain"] = expression.OR([result_domain, direct_refunds_domain])
+ return res
diff --git a/account_check_report/report/account_check_report.xml b/account_check_report/report/account_check_report.xml
index 7f80668ef3f7..ab51c2ec6555 100644
--- a/account_check_report/report/account_check_report.xml
+++ b/account_check_report/report/account_check_report.xml
@@ -21,11 +21,13 @@
style="padding-right:100mm;float:right;"
t-esc="'Date: {}'.format(_format_date_to_partner_lang(o.date, o.partner_id.id))"
/>
-
+
0 + ) + # Test button invoices + res = payment.button_open_bills() + self.assertTrue(vendor_bill.id in res["domain"][1][2]) + + def test_00_several_payments(self): + """check the amount is correct on partial payments + and the line is not repeated if several payments involved""" + vendor_bill = self._create_vendor_bill(self.acc_expense) + vendor_bill.action_post() + vendor_bill2 = self._create_vendor_bill(self.acc_expense) + vendor_bill2.action_post() + payment = self._create_payment(amount=1) + payment2 = self._create_payment(amount=4) + payment_ml = payment.line_ids.filtered( + lambda l: l.account_id == self.acc_payable + ) + payment2_ml = payment2.line_ids.filtered( + lambda l: l.account_id == self.acc_payable + ) + vendor_bill_ml = vendor_bill.line_ids.filtered( + lambda l: l.account_id == self.acc_payable + ) + vendor_bill2_ml = vendor_bill2.line_ids.filtered( + lambda l: l.account_id == self.acc_payable + ) + # reconcile fully the first bill with 1st payment + # the rest with the 2nd payment 1 + 1.99 + (payment_ml + payment2_ml + vendor_bill_ml).reconcile() + # reconcile partially the 2nd payments with the second invoice ($ 2.01 from payment) + (payment2_ml + vendor_bill2_ml).reconcile() + # PAYMENT CHECK REPORT PAYMENT 1 + report_lines = self.env[ + "report.account_check_report.check_report" + ]._get_paid_lines(payment) + amls_in_report = reduce(lambda x, y: x + y, report_lines) + aml_list = [item[0] for item in amls_in_report] + # check only the first bill appears + self.assertIn( + vendor_bill.line_ids.filtered(lambda l: l.account_id == self.acc_payable), + aml_list, + ) + self.assertNotIn( + vendor_bill2.line_ids.filtered(lambda l: l.account_id == self.acc_payable), + aml_list, + ) + # check the values of the line in the report + paid_amount = self.env[ + "report.account_check_report.check_report" + ]._get_paid_amount_this_payment(payment, amls_in_report) + self.assertEqual(paid_amount, 1.0) + residual = self.env[ + "report.account_check_report.check_report" + ]._get_residual_amount(payment, amls_in_report) + self.assertEqual(residual, 0.0) + total_amount = self.env[ + "report.account_check_report.check_report" + ]._get_total_amount(payment, amls_in_report) + self.assertEqual(total_amount, 2.99) + # PAYMENT CHECK REPORT PAYMENT 2 + report_lines = self.env[ + "report.account_check_report.check_report" + ]._get_paid_lines(payment2) + amls_in_report = reduce(lambda x, y: x + y, report_lines) + aml_list = [item[0] for item in amls_in_report] + # check both bill appears + self.assertIn( + vendor_bill.line_ids.filtered(lambda l: l.account_id == self.acc_payable), + aml_list, + ) + self.assertIn( + vendor_bill2.line_ids.filtered(lambda l: l.account_id == self.acc_payable), + aml_list, + ) + # check the values of the line in the report + for aml in amls_in_report: + paid_amount = self.env[ + "report.account_check_report.check_report" + ]._get_paid_amount_this_payment(payment2, aml) + if aml == vendor_bill_ml: + self.assertAlmostEqual(paid_amount, 1.99, places=2) + else: + self.assertAlmostEqual(paid_amount, 2.01, places=2) + residual = self.env[ + "report.account_check_report.check_report" + ]._get_residual_amount(payment2, aml) + if aml == vendor_bill_ml: + self.assertEqual(residual, 0.0) + else: + self.assertAlmostEqual(residual, 0.98, places=2) + total_amount = self.env[ + "report.account_check_report.check_report" + ]._get_total_amount(payment2, aml) + self.assertAlmostEqual(total_amount, 2.99, places=2) |