Add payment reference to statement rule
This commit is contained in:
@@ -167,7 +167,8 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
states={
|
||||
'invisible': Eval('type', 'out') != 'out',
|
||||
}),
|
||||
'get_customer_payment_reference')
|
||||
'get_customer_payment_reference',
|
||||
searcher='search_customer_payment_reference')
|
||||
sequence = fields.Integer("Sequence", readonly=True)
|
||||
sequence_type_cache = fields.Selection([
|
||||
(None, ""),
|
||||
@@ -656,6 +657,19 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
|
||||
return self.format_customer_payment_reference(
|
||||
self._customer_payment_reference_type())
|
||||
|
||||
@classmethod
|
||||
def search_customer_payment_reference(cls, name, clause):
|
||||
_, operator, value = clause[:3]
|
||||
if operator == '=':
|
||||
return ['OR', *cls._search_customer_payment_reference(value)]
|
||||
else:
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def _search_customer_payment_reference(cls, value):
|
||||
if iso11649.is_valid(value):
|
||||
yield ('number_alnum', '=', value[4:])
|
||||
|
||||
def _customer_payment_reference_type(self):
|
||||
return 'creditor_reference'
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
Add payment reference to statement rule
|
||||
|
||||
Version 7.6.0 - 2025-04-28
|
||||
--------------------------
|
||||
|
||||
@@ -6,6 +6,7 @@ import re
|
||||
from decimal import Decimal
|
||||
|
||||
from simpleeval import simple_eval
|
||||
from stdnum import iso11649
|
||||
|
||||
from trytond.model import ModelSQL, ModelView, fields, sequence_ordered
|
||||
from trytond.modules.currency.fields import Monetary
|
||||
@@ -95,7 +96,8 @@ class StatementRule(sequence_ordered(), ModelSQL, ModelView):
|
||||
"It may define the groups named:\n"
|
||||
"'party'\n"
|
||||
"'bank_account'\n"
|
||||
"'invoice'")
|
||||
"'invoice'\n"
|
||||
"'payment_reference'")
|
||||
information_rules = fields.One2Many(
|
||||
'account.statement.rule.information', 'rule', "Information Rules")
|
||||
|
||||
@@ -322,7 +324,7 @@ class StatementRuleLine(sequence_ordered(), ModelSQL, ModelView):
|
||||
|
||||
currency = origin.statement.journal.currency
|
||||
amount = currency.round(simple_eval(decistmt(self.amount), **context))
|
||||
party = self._get_party(origin, keywords)
|
||||
party = self._get_party(origin, keywords, amount=amount)
|
||||
related_to = list(
|
||||
filter(None, self._get_related_to(
|
||||
origin, keywords, party=party, amount=amount)))
|
||||
@@ -370,15 +372,21 @@ class StatementRuleLine(sequence_ordered(), ModelSQL, ModelView):
|
||||
line.related_to = related_to
|
||||
return line
|
||||
|
||||
def _get_party(self, origin, keywords):
|
||||
def _get_party(self, origin, keywords, amount=0):
|
||||
pool = Pool()
|
||||
Party = pool.get('party.party')
|
||||
Line = pool.get('account.statement.line')
|
||||
Configuration = pool.get('account.configuration')
|
||||
try:
|
||||
AccountNumber = pool.get('bank.account.number')
|
||||
except KeyError:
|
||||
AccountNumber = None
|
||||
|
||||
configuration = Configuration(1)
|
||||
customer_payment_reference_number = configuration.get_multivalue(
|
||||
'customer_payment_reference_number',
|
||||
company=origin.company.id)
|
||||
|
||||
party = self.party
|
||||
if not party:
|
||||
if keywords.get('bank_account') and AccountNumber:
|
||||
@@ -402,15 +410,29 @@ class StatementRuleLine(sequence_ordered(), ModelSQL, ModelView):
|
||||
if lines:
|
||||
line, = lines
|
||||
party = line.party
|
||||
elif keywords.get('party'):
|
||||
parties = Party.search(
|
||||
[('rec_name', 'ilike', keywords['party'])])
|
||||
elif (keywords.get('party')
|
||||
or (keywords.get('payment_reference')
|
||||
and amount > 0
|
||||
and customer_payment_reference_number == 'party')):
|
||||
domain = []
|
||||
if party_rec_name := keywords.get('party'):
|
||||
domain.append(('rec_name', 'ilike', party_rec_name))
|
||||
if ((payment_reference := keywords.get('payment_reference'))
|
||||
and amount > 0
|
||||
and customer_payment_reference_number == 'party'):
|
||||
domain.extend(
|
||||
self._party_payment_reference(payment_reference))
|
||||
if domain:
|
||||
domain.insert(0, 'OR')
|
||||
parties = Party.search(domain)
|
||||
else:
|
||||
parties = []
|
||||
if len(parties) == 1:
|
||||
party, = parties
|
||||
else:
|
||||
elif party_rec_name:
|
||||
lines = Line.search([
|
||||
('statement.state', 'in', ['validated', 'posted']),
|
||||
('origin.keywords.party', '=', keywords['party']),
|
||||
('origin.keywords.party', '=', party_rec_name),
|
||||
('party', '!=', None),
|
||||
],
|
||||
order=[('date', 'DESC')], limit=1)
|
||||
@@ -419,8 +441,14 @@ class StatementRuleLine(sequence_ordered(), ModelSQL, ModelView):
|
||||
party = line.party
|
||||
return party
|
||||
|
||||
@classmethod
|
||||
def _party_payment_reference(cls, value):
|
||||
if iso11649.is_valid(value):
|
||||
yield ('code_alnum', '=', value[4:])
|
||||
|
||||
def _get_related_to(self, origin, keywords, party=None, amount=0):
|
||||
return {self._get_invoice(origin, keywords, party=party)}
|
||||
return {
|
||||
self._get_invoice(origin, keywords, party=party, amount=amount)}
|
||||
|
||||
def _get_party_from(self, related_to):
|
||||
pool = Pool()
|
||||
@@ -434,16 +462,48 @@ class StatementRuleLine(sequence_ordered(), ModelSQL, ModelView):
|
||||
if isinstance(related_to, Invoice):
|
||||
return related_to.account
|
||||
|
||||
def _get_invoice(self, origin, keywords, party=None):
|
||||
def _get_invoice(self, origin, keywords, party=None, amount=0):
|
||||
pool = Pool()
|
||||
Invoice = pool.get('account.invoice')
|
||||
if keywords.get('invoice'):
|
||||
Configuration = pool.get('account.configuration')
|
||||
|
||||
configuration = Configuration(1)
|
||||
customer_payment_reference_number = configuration.get_multivalue(
|
||||
'customer_payment_reference_number',
|
||||
company=origin.company.id)
|
||||
|
||||
if keywords.get('invoice') or keywords.get('payment_reference'):
|
||||
kdomain = ['OR']
|
||||
if invoice_rec_name := keywords.get('invoice'):
|
||||
kdomain.append(('rec_name', '=', invoice_rec_name))
|
||||
if payment_reference := keywords.get('payment_reference'):
|
||||
if (amount > 0
|
||||
and customer_payment_reference_number == 'invoice'):
|
||||
kdomain.append(
|
||||
('customer_payment_reference', '=', payment_reference))
|
||||
elif amount < 0:
|
||||
kdomain.append(
|
||||
('supplier_payment_reference', '=', payment_reference))
|
||||
domain = [
|
||||
('rec_name', '=', keywords['invoice']),
|
||||
kdomain,
|
||||
('company', '=', origin.company.id),
|
||||
('currency', '=', origin.currency.id),
|
||||
('state', '=', 'posted'),
|
||||
]
|
||||
if amount > 0:
|
||||
domain.append(['OR',
|
||||
[('type', '=', 'out'),
|
||||
('total_amount', '>=', 0)],
|
||||
[('type', '=', 'in'),
|
||||
('total_amount', '<=', 0)],
|
||||
])
|
||||
elif amount < 0:
|
||||
domain.append(['OR',
|
||||
[('type', '=', 'out'),
|
||||
('total_amount', '<=', 0)],
|
||||
[('type', '=', 'in'),
|
||||
('total_amount', '>=', 0)],
|
||||
])
|
||||
if party:
|
||||
domain.append(['OR',
|
||||
('party', '=', party.id),
|
||||
|
||||
@@ -44,7 +44,7 @@ else:
|
||||
download_url = 'http://downloads.tryton.org/%s.%s/' % (
|
||||
major_version, minor_version)
|
||||
|
||||
requires = ['simpleeval']
|
||||
requires = ['simpleeval', 'python-stdnum']
|
||||
for dep in info.get('depends', []):
|
||||
if not re.match(r'(ir|res)(\W|$)', dep):
|
||||
requires.append(get_require_version('trytond_%s' % dep))
|
||||
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
=========================================================
|
||||
Account Statement Rule Keyword Payment Reference Scenario
|
||||
==========================================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> 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
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules(
|
||||
... ['account_statement_rule'],
|
||||
... create_company, create_chart)
|
||||
|
||||
>>> AccountConfiguration = Model.get('account.configuration')
|
||||
>>> AccountJournal = Model.get('account.journal')
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> Rule = Model.get('account.statement.rule')
|
||||
>>> Statement = Model.get('account.statement')
|
||||
>>> StatementJournal = Model.get('account.statement.journal')
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear(today=today))
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
>>> account_configuration = AccountConfiguration(1)
|
||||
>>> account_configuration.customer_payment_reference_number = 'invoice'
|
||||
>>> account_configuration.save()
|
||||
|
||||
Create a party::
|
||||
|
||||
>>> customer = Party(name="Customer", code="CUST-001")
|
||||
>>> customer.save()
|
||||
|
||||
Create an invoice::
|
||||
|
||||
>>> invoice = Invoice(party=customer)
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal('40.0000')
|
||||
>>> line.account = accounts['revenue']
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
|
||||
Create statement rules::
|
||||
|
||||
>>> rule = Rule(name="Payment Reference Rule")
|
||||
>>> rule.description = r"Ref: *(?P<payment_reference>.*)"
|
||||
>>> line = rule.lines.new()
|
||||
>>> line.amount = "pending"
|
||||
>>> rule.save()
|
||||
|
||||
Create a statement::
|
||||
|
||||
>>> account_journal, = AccountJournal.find([('code', '=', 'STA')], limit=1)
|
||||
>>> journal_number = StatementJournal(
|
||||
... name="Number",
|
||||
... journal=account_journal,
|
||||
... account=accounts['cash'],
|
||||
... validation='number_of_lines')
|
||||
>>> journal_number.save()
|
||||
|
||||
>>> statement = Statement(name="Test")
|
||||
>>> statement.journal = journal_number
|
||||
>>> statement.number_of_lines = 1
|
||||
>>> origin = statement.origins.new()
|
||||
>>> origin.date = today
|
||||
>>> origin.amount = Decimal('50.00')
|
||||
>>> origin.description = "Ref: " + invoice.customer_payment_reference
|
||||
>>> statement.click('apply_rules')
|
||||
>>> line, = statement.lines
|
||||
>>> assertEqual(line.related_to, invoice)
|
||||
>>> assertEqual(line.party, customer)
|
||||
>>> assertEqual(line.account, invoice.account)
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
==================================================================
|
||||
Account Statement Rule Keyword Payment Reference Customer Scenario
|
||||
==================================================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import create_chart, get_accounts
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules(
|
||||
... ['account_statement_rule'],
|
||||
... create_company, create_chart)
|
||||
|
||||
>>> AccountConfiguration = Model.get('account.configuration')
|
||||
>>> AccountJournal = Model.get('account.journal')
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> Rule = Model.get('account.statement.rule')
|
||||
>>> Statement = Model.get('account.statement')
|
||||
>>> StatementJournal = Model.get('account.statement.journal')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
>>> account_configuration = AccountConfiguration(1)
|
||||
>>> account_configuration.customer_payment_reference_number = 'party'
|
||||
>>> account_configuration.save()
|
||||
|
||||
Create a party::
|
||||
|
||||
>>> customer = Party(name="Customer", code="CUST-001")
|
||||
>>> customer.save()
|
||||
|
||||
Create statement rules::
|
||||
|
||||
>>> rule = Rule(name="Payment Reference Rule")
|
||||
>>> rule.description = r"Ref: *(?P<payment_reference>.*)"
|
||||
>>> line = rule.lines.new()
|
||||
>>> line.amount = "pending"
|
||||
>>> rule.save()
|
||||
|
||||
Create a statement::
|
||||
|
||||
>>> account_journal, = AccountJournal.find([('code', '=', 'STA')], limit=1)
|
||||
>>> journal_number = StatementJournal(
|
||||
... name="Number",
|
||||
... journal=account_journal,
|
||||
... account=accounts['cash'],
|
||||
... validation='number_of_lines')
|
||||
>>> journal_number.save()
|
||||
|
||||
>>> statement = Statement(name="Test")
|
||||
>>> statement.journal = journal_number
|
||||
>>> statement.number_of_lines = 1
|
||||
>>> origin = statement.origins.new()
|
||||
>>> origin.date = today
|
||||
>>> origin.amount = Decimal('50.00')
|
||||
>>> origin.description = "Ref: RF92CUST001"
|
||||
>>> statement.click('apply_rules')
|
||||
>>> line, = statement.lines
|
||||
>>> assertEqual(line.party, customer)
|
||||
Reference in New Issue
Block a user