Add direct debit as instrument for invoice payment means
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -820,6 +820,52 @@ class Invoice(metaclass=PoolMeta):
|
||||
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'
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -124,6 +124,13 @@ class PartyReceptionDirectDebit(
|
||||
'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')
|
||||
|
||||
@@ -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])
|
||||
@@ -51,6 +51,7 @@ model:
|
||||
[register account_invoice]
|
||||
model:
|
||||
account.Invoice
|
||||
account.InvoicePaymentMean
|
||||
payment.Payment_Invoice
|
||||
|
||||
[register account_statement]
|
||||
|
||||
@@ -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 @@ class ConfigurationSepaMandateSequence(ModelSQL, CompanyValueMixin):
|
||||
'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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -50,7 +50,10 @@ for dep in info.get('depends', []):
|
||||
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,
|
||||
|
||||
@@ -154,6 +154,7 @@ class AccountPaymentSepaTestCase(
|
||||
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 @@ class AccountPaymentSepaTestCase(
|
||||
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),
|
||||
|
||||
@@ -8,6 +8,7 @@ depends:
|
||||
bank
|
||||
party
|
||||
extras_depend:
|
||||
account_invoice
|
||||
account_payment_clearing
|
||||
xml:
|
||||
payment.xml
|
||||
@@ -32,3 +33,7 @@ wizard:
|
||||
report:
|
||||
payment.MandateReport
|
||||
payment.MessageReport
|
||||
|
||||
[register account_invoice]
|
||||
model:
|
||||
account.InvoicePaymentMean
|
||||
|
||||
Reference in New Issue
Block a user