#4 fix payment registration

This commit is contained in:
Luis 2026-09-21 16:43:45 +02:00
parent 5b2202f7d4
commit 76dc355c6c
2 changed files with 60 additions and 4 deletions

View file

@ -53,14 +53,24 @@ class AccountMove(models.Model):
return res return res
def _get_membership_payment_date(self, invoice): def _get_membership_payment_date(self, invoice):
"""Get the date of the most recent reconciled payment. """Get the date of the most recent payment reconciled with the invoice.
If there are no payments (e.g. zero-amount invoice), fallback to today. Uses the ``_get_reconciled_payments()`` helper instead of the
``reconciled_payment_ids`` field: that field was dropped in 18.0 and
only reintroduced on 2025-10-13 (odoo/odoo d8e7798d2c), so it is
missing on older 18.0 builds. ``matched_payment_ids`` is not an option
either: the register wizard links it *after* reconciling, so it is
still empty when ``_invoice_paid_hook`` runs.
If nothing is reconciled (e.g. zero-amount invoice), fallback to today.
""" """
self.ensure_one() payments = invoice._get_reconciled_payments()
payments = invoice.reconciled_payment_ids
if payments: if payments:
return max(payments.mapped("date")) return max(payments.mapped("date"))
# Counterparts that are not payments: credit notes, manual entries...
reconciled_amls = invoice._get_reconciled_amls()
if reconciled_amls:
return max(reconciled_amls.mapped("date"))
return fields.Date.today() return fields.Date.today()
def _compute_membership_dates( def _compute_membership_dates(

View file

@ -110,6 +110,15 @@ class TestMembershipPaymentDate(common.TransactionCase):
} }
) )
cls.product_variable = template_variable.product_variant_id cls.product_variable = template_variable.product_variant_id
# Plain service, not a membership
template_service = cls.env["product.template"].create(
{
"name": "Consulting Service",
"type": "service",
"list_price": 50.0,
}
)
cls.product_service = template_service.product_variant_id
def _create_invoice(self, product, invoice_date, quantity=1.0): def _create_invoice(self, product, invoice_date, quantity=1.0):
"""Create and return a draft out_invoice with the given product.""" """Create and return a draft out_invoice with the given product."""
@ -311,3 +320,40 @@ class TestMembershipPaymentDate(common.TransactionCase):
self._register_payment(invoice, payment_date=date(2026, 5, 10)) self._register_payment(invoice, payment_date=date(2026, 5, 10))
self.assertEqual(mline.date_from, date(2026, 5, 10)) self.assertEqual(mline.date_from, date(2026, 5, 10))
self.assertEqual(mline.date_to, date(2026, 12, 31)) self.assertEqual(mline.date_to, date(2026, 12, 31))
def test_09_single_payment_for_several_invoices(self):
"""One payment settling several invoices at once.
The reconciliation calls _invoice_paid_hook with a multi-record
recordset, which used to raise "Expected singleton".
"""
membership_invoice = self._create_invoice(
self.product_fixed, invoice_date=date(2026, 5, 1)
)
mline = self._post_invoice(membership_invoice)[0]
other_invoice = self._create_invoice(
self.product_service, invoice_date=date(2026, 5, 1)
)
other_invoice.action_post()
invoices = membership_invoice | other_invoice
payment_register = (
self.env["account.payment.register"]
.with_context(
active_model="account.move",
active_ids=invoices.ids,
)
.create(
{
"payment_date": date(2026, 5, 10),
"journal_id": self.bank_journal.id,
"group_payment": True,
}
)
)
payment_register._create_payments()
self.assertTrue(
all(inv.payment_state in ("paid", "in_payment") for inv in invoices)
)
# The membership line still gets the payment date, not today's date
self.assertEqual(mline.date_from, date(2026, 5, 10))
self.assertEqual(mline.date_to, date(2026, 12, 31))