Files
account-tools/account_move_last_payment/models/account_move.py
maud-buchwalter c704459de7
Some checks failed
tests / test (pull_request) Failing after 3m17s
pre-commit / pre-commit (pull_request) Failing after 1m58s
[ADD] account_move_last_payment display last date and payment method on invoice
2026-10-22 11:26:37 +02:00

85 lines
3.6 KiB
Python

from odoo import api, fields, models
class AccountMove(models.Model):
_inherit = "account.move"
last_payment_date = fields.Date(
compute="_compute_last_payment_info",
store=True,
copy=False,
help="Date of the most recent payment reconciled with this "
"invoice, whatever the reconciliation path (explicit payment "
"or direct bank statement line reconciliation).",
)
last_payment_method = fields.Char(
compute="_compute_last_payment_info",
store=True,
copy=False,
help="Payment method of the most recent payment reconciled with this invoice.",
)
@api.depends(
"line_ids.matched_debit_ids.debit_move_id.date",
"line_ids.matched_debit_ids.debit_move_id.move_id.origin_payment_id.payment_method_line_id.name",
"line_ids.matched_debit_ids.debit_move_id.move_id.statement_line_id",
"line_ids.matched_credit_ids.credit_move_id.date",
"line_ids.matched_credit_ids.credit_move_id.move_id.origin_payment_id.payment_method_line_id.name",
"line_ids.matched_credit_ids.credit_move_id.move_id.statement_line_id",
)
def _compute_last_payment_info(self):
"""Compute the last real payment date and method.
Only customer and vendor invoices carry the information; every
other move type gets both fields cleared. Candidates are the
reconciliation counterpart lines of the receivable/payable
lines (exchange difference moves excluded). The most recent
candidate wins; on equal dates the first candidate found wins.
"""
for move in self:
if move.move_type not in ("in_invoice", "out_invoice"):
move.last_payment_date = False
move.last_payment_method = False
continue
candidates = move._get_last_payment_candidates()
if not candidates:
move.last_payment_date = False
move.last_payment_method = False
else:
latest_date, latest_method = max(
candidates, key=lambda candidate: candidate[0]
)
move.last_payment_date = latest_date
move.last_payment_method = latest_method
def _get_last_payment_candidates(self):
"""Collect the candidate real payments for this invoice.
A candidate is a ``(date, method)`` tuple built from each
reconciliation counterpart line:
- counterpart move with ``origin_payment_id``: explicit
payment, dated with the payment date, method from the
payment method line;
- counterpart move with ``statement_line_id``: direct bank
statement reconciliation, dated with the counterpart move
date, method fixed to the bank transfer label;
- any other manual reconciliation is ignored.
:return: list of ``(date, method)`` tuples, possibly empty.
"""
self.ensure_one()
invoice_partials, exchange_diff_moves = self._get_reconciled_invoices_partials()
candidates = []
for _partial, _amount, counterpart_line in invoice_partials:
if counterpart_line.move_id.id in exchange_diff_moves:
continue
payment = counterpart_line.move_id.origin_payment_id
if payment:
candidates.append((payment.date, payment.payment_method_line_id.name))
elif counterpart_line.move_id.statement_line_id:
candidates.append(
(counterpart_line.move_id.date, self.env._("Bank Transfer"))
)
return candidates