details: https://code.tryton.org/tryton/commit/7feb597bd64a
branch: default
user: Cédric Krier <[email protected]>
date: Mon Feb 16 12:01:18 2026 +0100
description:
Add direct debit as instrument for invoice payment means
diffstat:
modules/account_payment/CHANGELOG
| 1 +
modules/account_payment/account.py
| 46 +++++++
modules/account_payment/message.xml
| 3 +
modules/account_payment/party.py
| 7 +
modules/account_payment/tests/scenario_account_payment_invoice_payment_means.rst
| 63 ++++++++++
modules/account_payment/tryton.cfg
| 1 +
modules/account_payment_sepa/account.py
| 18 ++
modules/account_payment_sepa/message.xml
| 3 +
modules/account_payment_sepa/setup.py
| 5 +-
modules/account_payment_sepa/tests/test_module.py
| 3 +-
modules/account_payment_sepa/tryton.cfg
| 5 +
11 files changed, 153 insertions(+), 2 deletions(-)
diffs (270 lines):
diff -r f2c5d67fe779 -r 7feb597bd64a modules/account_payment/CHANGELOG
--- a/modules/account_payment/CHANGELOG Mon Feb 16 12:32:35 2026 +0100
+++ b/modules/account_payment/CHANGELOG Mon Feb 16 12:01:18 2026 +0100
@@ -1,3 +1,4 @@
+* Add direct debit as instrument for invoice payment means
* Warn against creating an overpayment
Version 7.8.0 - 2025-12-15
diff -r f2c5d67fe779 -r 7feb597bd64a modules/account_payment/account.py
--- a/modules/account_payment/account.py Mon Feb 16 12:32:35 2026 +0100
+++ b/modules/account_payment/account.py Mon Feb 16 12:01:18 2026 +0100
@@ -820,6 +820,52 @@
amounts[invoice.id] -= payment_amount
return amounts
+ @classmethod
+ def set_payment_means(cls, invoices):
+ pool = Pool()
+ PaymentMean = pool.get('account.invoice.payment.mean')
+ Reception = pool.get('party.party.reception_direct_debit')
+ payment_means = []
+ for invoice in invoices:
+ if invoice.type == 'out' and not invoice.payment_means:
+ pattern = Reception.get_pattern_for_invoice(invoice)
+ for reception in invoice.party.reception_direct_debits:
+ if reception.match(pattern):
+ payment_mean = PaymentMean.from_reception_direct_debit(
+ reception)
+ payment_mean.invoice = invoice
+ payment_means.append(payment_mean)
+ PaymentMean.save(payment_means)
+ super().set_payment_means(invoices)
+
+
+class InvoicePaymentMean(metaclass=PoolMeta):
+ __name__ = 'account.invoice.payment.mean'
+
+ @classmethod
+ def __setup__(cls):
+ super().__setup__()
+ cls.instrument.domain['party.party.reception_direct_debit'] = [
+ ('party', 'in', Eval('payers', [])),
+ ('company', '=', Eval('company', -1)),
+ ]
+
+ @classmethod
+ def _get_instruments(cls):
+ yield from super()._get_instruments()
+ yield 'party.party.reception_direct_debit'
+
+ @classmethod
+ def from_reception_direct_debit(cls, reception):
+ return cls(instrument=reception)
+
+ def get_rec_name(self, name):
+ name = super().get_rec_name(name)
+ if self.instrument.__name__ == 'party.party.reception_direct_debit':
+ name = gettext(
+ 'account_payment.msg_invoice_payment_mean_direct_debit')
+ return name
+
class Statement(metaclass=PoolMeta):
__name__ = 'account.statement'
diff -r f2c5d67fe779 -r 7feb597bd64a modules/account_payment/message.xml
--- a/modules/account_payment/message.xml Mon Feb 16 12:32:35 2026 +0100
+++ b/modules/account_payment/message.xml Mon Feb 16 12:01:18 2026 +0100
@@ -3,6 +3,9 @@
this repository contains the full copyright notices and license terms. -->
<tryton>
<data grouped="1">
+ <record model="ir.message" id="msg_invoice_payment_mean_direct_debit">
+ <field name="text">Direct debit</field>
+ </record>
<record model="ir.message" id="msg_payment_delete_draft">
<field name="text">To delete payment "%(payment)s" you must reset
it to draft state.</field>
</record>
diff -r f2c5d67fe779 -r 7feb597bd64a modules/account_payment/party.py
--- a/modules/account_payment/party.py Mon Feb 16 12:32:35 2026 +0100
+++ b/modules/account_payment/party.py Mon Feb 16 12:01:18 2026 +0100
@@ -124,6 +124,13 @@
'type': type,
}
+ @classmethod
+ def get_pattern_for_invoice(cls, invoice):
+ return {
+ 'company': invoice.company.id,
+ 'currency': invoice.currency.id,
+ }
+
def get_payments(self, line=None, amount=None, date=None):
pool = Pool()
Date = pool.get('ir.date')
diff -r f2c5d67fe779 -r 7feb597bd64a
modules/account_payment/tests/scenario_account_payment_invoice_payment_means.rst
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++
b/modules/account_payment/tests/scenario_account_payment_invoice_payment_means.rst
Mon Feb 16 12:01:18 2026 +0100
@@ -0,0 +1,63 @@
+==============================
+Invoice Payment Means Scenario
+==============================
+
+Imports::
+
+ >>> from proteus import Model
+ >>> from trytond.modules.account.tests.tools import (
+ ... create_chart, create_fiscalyear, get_accounts)
+ >>> from trytond.modules.account_invoice.tests.tools import (
+ ... set_fiscalyear_invoice_sequences)
+ >>> from trytond.modules.company.tests.tools import create_company,
get_company
+ >>> from trytond.tests.tools import activate_modules, assertEqual,
assertFalse
+
+Activate modules::
+
+ >>> config = activate_modules(
+ ... ['account_payment', 'account_invoice'], create_company,
create_chart)
+
+ >>> Invoice = Model.get('account.invoice')
+ >>> Party = Model.get('party.party')
+ >>> PaymentJournal = Model.get('account.payment.journal')
+
+ >>> company = get_company()
+
+Create fiscal year::
+
+ >>> fiscalyear = set_fiscalyear_invoice_sequences(create_fiscalyear())
+ >>> fiscalyear.click('create_period')
+
+Get accounts::
+
+ >>> accounts = get_accounts()
+
+Create payment journal::
+
+ >>> payment_journal = PaymentJournal(name="Manual",
process_method='manual')
+ >>> payment_journal.save()
+
+Create party::
+
+ >>> customer = Party(name="Customer")
+ >>> _ = customer.reception_direct_debits.new(journal=payment_journal)
+ >>> customer.save()
+
+Create invoice without payment means::
+
+ >>> invoice = Invoice(type='out')
+ >>> invoice.party = customer
+ >>> assertFalse(invoice.payment_means)
+ >>> invoice.click('validate_invoice')
+ >>> payment_mean, = invoice.payment_means
+ >>> assertEqual(payment_mean.instrument,
customer.reception_direct_debits[0])
+
+Create invoice with payment means::
+
+ >>> invoice = Invoice(type='out')
+ >>> invoice.party = customer
+ >>> payment_mean = invoice.payment_means.new()
+ >>> payment_mean.instrument = customer.reception_direct_debits[0]
+ >>> invoice.click('post')
+ >>> payment_mean, = invoice.payment_means
+ >>> assertEqual(payment_mean.instrument,
customer.reception_direct_debits[0])
diff -r f2c5d67fe779 -r 7feb597bd64a modules/account_payment/tryton.cfg
--- a/modules/account_payment/tryton.cfg Mon Feb 16 12:32:35 2026 +0100
+++ b/modules/account_payment/tryton.cfg Mon Feb 16 12:01:18 2026 +0100
@@ -51,6 +51,7 @@
[register account_invoice]
model:
account.Invoice
+ account.InvoicePaymentMean
payment.Payment_Invoice
[register account_statement]
diff -r f2c5d67fe779 -r 7feb597bd64a modules/account_payment_sepa/account.py
--- a/modules/account_payment_sepa/account.py Mon Feb 16 12:32:35 2026 +0100
+++ b/modules/account_payment_sepa/account.py Mon Feb 16 12:01:18 2026 +0100
@@ -1,5 +1,7 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
+
+from trytond.i18n import gettext
from trytond.model import ModelSQL, fields
from trytond.modules.company.model import CompanyValueMixin
from trytond.pool import PoolMeta
@@ -27,3 +29,19 @@
'account_payment_sepa', 'sequence_type_mandate')),
('company', 'in', [Eval('company', -1), None]),
])
+
+
+class InvoicePaymentMean(metaclass=PoolMeta):
+ __name__ = 'account.invoice.payment.mean'
+
+ def get_rec_name(self, name):
+ name = super().get_rec_name(name)
+ if (self.instrument.__name__ == 'party.party.reception_direct_debit'
+ and self.instrument.journal.process_method == 'sepa'
+ and self.instrument.sepa_mandate):
+ mandate = self.instrument.sepa_mandate
+ name = gettext(
+ 'account_payment_sepa.msg_invoice_payment_mean_direct_debit',
+ mandate=mandate.identification,
+ account_number=mandate.account_number.number)
+ return name
diff -r f2c5d67fe779 -r 7feb597bd64a modules/account_payment_sepa/message.xml
--- a/modules/account_payment_sepa/message.xml Mon Feb 16 12:32:35 2026 +0100
+++ b/modules/account_payment_sepa/message.xml Mon Feb 16 12:01:18 2026 +0100
@@ -3,6 +3,9 @@
this repository contains the full copyright notices and license terms. -->
<tryton>
<data grouped="1">
+ <record model="ir.message" id="msg_invoice_payment_mean_direct_debit">
+ <field name="text">SEPA direct debit using mandate %(mandate)s on
%(account_number)s</field>
+ </record>
<record model="ir.message" id="msg_payment_process_no_mandate">
<field name="text">To process payment "%(payment)s" you must
define a SEPA mandate for it.</field>
</record>
diff -r f2c5d67fe779 -r 7feb597bd64a modules/account_payment_sepa/setup.py
--- a/modules/account_payment_sepa/setup.py Mon Feb 16 12:32:35 2026 +0100
+++ b/modules/account_payment_sepa/setup.py Mon Feb 16 12:01:18 2026 +0100
@@ -50,7 +50,10 @@
requires.append(get_require_version('trytond_%s' % dep))
requires.append(get_require_version('trytond'))
-tests_require = [get_require_version('proteus')]
+tests_require = [
+ get_require_version('proteus'),
+ get_require_version('trytond_account_invoice'),
+ ]
setup(name=name,
version=version,
diff -r f2c5d67fe779 -r 7feb597bd64a
modules/account_payment_sepa/tests/test_module.py
--- a/modules/account_payment_sepa/tests/test_module.py Mon Feb 16 12:32:35
2026 +0100
+++ b/modules/account_payment_sepa/tests/test_module.py Mon Feb 16 12:01:18
2026 +0100
@@ -154,6 +154,7 @@
PartyCheckReplaceMixin, CompanyTestMixin, ModuleTestCase):
'Test Account Payment SEPA module'
module = 'account_payment_sepa'
+ extras = ['account_invoice']
@with_transaction()
def test_pain001_001_03(self):
@@ -279,7 +280,7 @@
party = Party(
bank_accounts_used=[bank_account])
payment = Payment(
- kind='payable', party=party,
+ kind='payable', party=party, line=None,
sepa_payable_bank_account_number=None)
self.assertEqual(id(payment.sepa_bank_account_number),
diff -r f2c5d67fe779 -r 7feb597bd64a modules/account_payment_sepa/tryton.cfg
--- a/modules/account_payment_sepa/tryton.cfg Mon Feb 16 12:32:35 2026 +0100
+++ b/modules/account_payment_sepa/tryton.cfg Mon Feb 16 12:01:18 2026 +0100
@@ -8,6 +8,7 @@
bank
party
extras_depend:
+ account_invoice
account_payment_clearing
xml:
payment.xml
@@ -32,3 +33,7 @@
report:
payment.MandateReport
payment.MessageReport
+
+[register account_invoice]
+model:
+ account.InvoicePaymentMean