Skip to content

Commit

Permalink
[IMP] account_check_report: payment amount should consider current pa…
Browse files Browse the repository at this point in the history
…yment only

not other payments

[IMP] account_check_report: show amount residual at the payment date, not on todays date

[IMP] account_check_report: show direct refund applied on invoices paid by the check
e.g. 2 invoices and a refund for those 2 invoices should appear, even if the refund
includes separate invoices

[IMP]account_check_report: button invoices show all related invoices + direct refunds

[IMP] account_check_report: add test coverage

[IMP] account_check_report: do not print more than one line by invoice. Currently,
if there are several receivable/payable lines, for example, due to payment terms
the invoice appears multiple times
  • Loading branch information
AaronHForgeFlow committed Aug 30, 2024
1 parent 1f43470 commit fbd26bc
Show file tree
Hide file tree
Showing 7 changed files with 521 additions and 39 deletions.
1 change: 1 addition & 0 deletions account_check_report/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import report
from . import models
1 change: 1 addition & 0 deletions account_check_report/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import account_payment
92 changes: 92 additions & 0 deletions account_check_report/models/account_payment.py
Original file line number Diff line number Diff line change
@@ -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

Check warning on line 60 in account_check_report/models/account_payment.py

View check run for this annotation

Codecov / codecov/patch

account_check_report/models/account_payment.py#L60

Added line #L60 was not covered by tests
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 []

Check warning on line 68 in account_check_report/models/account_payment.py

View check run for this annotation

Codecov / codecov/patch

account_check_report/models/account_payment.py#L68

Added line #L68 was not covered by tests

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
4 changes: 2 additions & 2 deletions account_check_report/report/account_check_report.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@
<tr style="text-align:left;border-top: 0px;">
<td style="padding-top:2mm;">
<span
t-esc="_format_date_to_partner_lang(line.date_maturity, o.partner_id.id)"
t-esc="_format_date_to_partner_lang(line[0].date, o.partner_id.id)"
/>
</td>
<td
style="max-width: 55mm;"
t-esc="line.display_name"
t-esc="line[0].display_name"
/>
<td>
<span
Expand Down
181 changes: 144 additions & 37 deletions account_check_report/report/report_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,25 @@ def _get_paid_lines(self, payment):
amls = rec_lines.mapped("matched_credit_ids.credit_move_id") + rec_lines.mapped(
"matched_debit_ids.debit_move_id"
)
# Include direct refunds:
if payment.partner_type == "customer":
amls += rec_lines.mapped(
"matched_debit_ids.debit_move_id.matched_credit_ids."
"credit_move_id.full_reconcile_id.reconciled_line_ids"
).filtered(
lambda line: line.id
not in rec_lines.mapped("matched_debit_ids.debit_move_id.id")
and line.move_id.date <= payment.date
)
else:
amls += rec_lines.mapped(
"matched_credit_ids.credit_move_id.matched_debit_ids."
"debit_move_id.full_reconcile_id.reconciled_line_ids"
).filtered(
lambda line: line.id
not in rec_lines.mapped("matched_credit_ids.credit_move_id.id")
and line.move_id.date <= payment.date
)
amls -= rec_lines
# Here we need to handle a nasty corner case.
# Sometimes we match a payment with invoices and refunds. Internally
Expand All @@ -36,54 +55,142 @@ def _get_paid_lines(self, payment):
# refunds. In order to solve that, we will just include all the move
# lines associated to the invoices that the user intended to pay,
# including refunds.
invoice_amls = payment.reconciled_invoice_ids.line_ids.filtered(
lambda x: x.account_id.reconcile
and x.account_id == payment.destination_account_id
and x.partner_id == payment.partner_id
)
if payment.partner_type == "customer":
invoice_amls = payment.reconciled_invoice_ids.line_ids.filtered(
lambda x: x.account_id.reconcile
and x.account_id == payment.destination_account_id
and x.partner_id == payment.partner_id
)
else:
invoice_amls = payment.reconciled_bill_ids.line_ids.filtered(
lambda x: x.account_id.reconcile
and x.account_id == payment.destination_account_id
and x.partner_id == payment.partner_id
)
amls |= invoice_amls
return amls
res = []
invoices_checked = []
# Another nasty corner case:
# avoid printing more than one line for invoice where the
# payable/receivable line is split. That happens when using
# payment terms with several lines
# I group the lines by invoice below
for aml in amls:
invoice = aml.move_id
if invoice in invoices_checked:
# prevent duplicated lines
continue

Check warning on line 82 in account_check_report/report/report_helper.py

View check run for this annotation

Codecov / codecov/patch

account_check_report/report/report_helper.py#L82

Added line #L82 was not covered by tests
if invoice:
invoices_checked.append(invoice)
amls_inv = amls.filtered(lambda line: line.move_id == invoice)
res.append([line for line in amls_inv])
else:
res.append([aml])

Check warning on line 88 in account_check_report/report/report_helper.py

View check run for this annotation

Codecov / codecov/patch

account_check_report/report/report_helper.py#L88

Added line #L88 was not covered by tests
return res

def _get_residual_amount(self, payment, line):
amt = line.amount_residual
def _get_residual_amount(self, payment, lines):
amt = abs(lines[0].move_id.amount_total) - abs(
self._get_paid_amount(payment, lines)
)
if amt < 0.0:
amt *= -1
amt = payment.company_id.currency_id.with_context(date=payment.date).compute(
amt, payment.currency_id
)
return amt

def _get_paid_amount(self, payment, line):
amount = 0.0
total_amount_to_show = 0.0
# We pay out
if line.matched_credit_ids:
amount = -1 * sum(p.amount for p in line.matched_credit_ids)
# We receive payment
elif line.matched_debit_ids:
amount = sum(p.amount for p in line.matched_debit_ids)
def _get_paid_amount_this_payment(self, payment, lines):
"Get the paid amount for the payment at payment date"
agg_amt = 0
for line in lines:
amount = 0.0
total_paid_at_date = 0.0
payment.mapped("reconciled_invoice_ids")
# Considering the dates of the partial reconcile
if line.matched_credit_ids:
amount = -1 * sum(
p.amount
for p in line.matched_credit_ids.filtered(
lambda line: line.credit_move_id.date <= payment.date
and (
line.credit_move_id.payment_id == payment
or not line.credit_move_id.payment_id
)
)
)
# We receive payment
elif line.matched_debit_ids:
amount = sum(
p.amount
for p in line.matched_debit_ids.filtered(
lambda line: line.debit_move_id.date <= payment.date
and (
line.debit_move_id.payment_id == payment
or not line.debit_move_id.payment_id
)
)
)

# In case of customer payment, we reverse the amounts
if payment.partner_type == "customer":
amount *= -1
# In case of customer payment, we reverse the amounts
if payment.partner_type == "customer":
amount *= -1

Check warning on line 136 in account_check_report/report/report_helper.py

View check run for this annotation

Codecov / codecov/patch

account_check_report/report/report_helper.py#L136

Added line #L136 was not covered by tests
amount_to_show = payment.company_id.currency_id.with_context(
date=payment.date
).compute(amount, payment.currency_id)
if not float_is_zero(
amount_to_show, precision_rounding=payment.currency_id.rounding
):
total_paid_at_date = amount_to_show
agg_amt += total_paid_at_date
return agg_amt

amount_to_show = payment.company_id.currency_id.with_context(
date=payment.date
).compute(amount, payment.currency_id)
if not float_is_zero(
amount_to_show, precision_rounding=payment.currency_id.rounding
):
total_amount_to_show = amount_to_show
return total_amount_to_show
def _get_paid_amount(self, payment, lines):
"Get the total paid amount for all payments at the payment date"
agg_amt = 0.0
for line in lines:
amount = 0.0
total_amount_to_show = 0.0
# Considering the dates of the partial reconcile
if line.matched_credit_ids:
amount = -1 * sum(
p.amount
for p in line.matched_credit_ids.filtered(
lambda line: line.credit_move_id.date <= payment.date
)
)
# We receive payment
elif line.matched_debit_ids:
amount = sum(
p.amount
for p in line.matched_debit_ids.filtered(
lambda line: line.debit_move_id.date <= payment.date
)
)

def _get_total_amount(self, payment, line):
amt = line.balance
if amt < 0.0:
amt *= -1
amt = payment.company_id.currency_id.with_context(date=payment.date).compute(
amt, payment.currency_id
)
return amt
# In case of customer payment, we reverse the amounts
if payment.partner_type == "customer":
amount *= -1

Check warning on line 172 in account_check_report/report/report_helper.py

View check run for this annotation

Codecov / codecov/patch

account_check_report/report/report_helper.py#L172

Added line #L172 was not covered by tests
amount_to_show = payment.company_id.currency_id.with_context(
date=payment.date
).compute(amount, payment.currency_id)
if not float_is_zero(
amount_to_show, precision_rounding=payment.currency_id.rounding
):
total_amount_to_show = amount_to_show
agg_amt += total_amount_to_show
return agg_amt

def _get_total_amount(self, payment, lines):
agg_amt = 0
for line in lines:
amt = line.balance
if amt < 0.0 or line.move_id.move_type in ("in_refund", "out_refund"):
amt *= -1
amt = payment.company_id.currency_id.with_context(
date=payment.date
).compute(amt, payment.currency_id)
agg_amt += amt
return agg_amt

@api.model
def _get_report_values(self, docids, data=None):
Expand All @@ -96,7 +203,7 @@ def _get_report_values(self, docids, data=None):
"total_amount": self._get_total_amount,
"paid_lines": self._get_paid_lines,
"residual_amount": self._get_residual_amount,
"paid_amount": self._get_paid_amount,
"paid_amount": self._get_paid_amount_this_payment,
"_format_date_to_partner_lang": self._format_date_to_partner_lang,
}
return docargs
1 change: 1 addition & 0 deletions account_check_report/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_account_check_report
Loading

0 comments on commit fbd26bc

Please sign in to comment.