From e58228fe28389bd31a549e7fa88f5d0ea2470b7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 23 Jan 2022 13:12:40 +0100 Subject: [PATCH] Do not create dunning for line with pending payment issue11101 review395231003 --- CHANGELOG | 1 + __init__.py | 3 + account.py | 23 ++++ setup.py | 5 +- tests/scenario_account_payment_dunning.rst | 134 +++++++++++++++++++++ tests/test_account_payment.py | 5 + tryton.cfg | 1 + 7 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 tests/scenario_account_payment_dunning.rst diff --git a/CHANGELOG b/CHANGELOG index 833616ebe3..919f7b2b98 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Do not create dunning for line with pending payment * Add support for Python 3.10 * Remove support for Python 3.6 * Add amount_line_paid property on payments diff --git a/__init__.py b/__init__.py index a6aea7a07f..1ec5b704ea 100644 --- a/__init__.py +++ b/__init__.py @@ -24,6 +24,9 @@ def register(): Pool.register( account.Invoice, module='account_payment', type_='model', depends=['account_invoice']) + Pool.register( + account.Dunning, + module='account_payment', type_='model', depends=['account_dunning']) Pool.register( payment.ProcessPayment, account.CreateDirectDebit, diff --git a/account.py b/account.py index 27a7e441ff..b310d8e2a8 100644 --- a/account.py +++ b/account.py @@ -567,3 +567,26 @@ class Invoice(metaclass=PoolMeta): invoice.currency) amounts[invoice.id] -= payment_amount return amounts + + +class Dunning(metaclass=PoolMeta): + __name__ = 'account.dunning' + + def get_active(self, name): + return super().get_active(name) and self.line.payment_amount > 0 + + @classmethod + def search_active(cls, name, clause): + if tuple(clause[1:]) in {('=', True), ('!=', False)}: + domain = ('line.payment_amount', '>', 0) + elif tuple(clause[1:]) in {('=', False), ('!=', True)}: + domain = ('line.payment_amount', '<=', 0) + else: + domain = [] + return [super().search_active(name, clause), domain] + + @classmethod + def _overdue_line_domain(cls, date): + return [super()._overdue_line_domain(date), + ('payment_amount', '>', 0), + ] diff --git a/setup.py b/setup.py index 424f11e484..fb60bec6e3 100755 --- a/setup.py +++ b/setup.py @@ -64,7 +64,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_dunning'), + ] for dep in ['account_invoice']: tests_require.append(get_require_version('trytond_%s' % dep)) dependency_links = [] diff --git a/tests/scenario_account_payment_dunning.rst b/tests/scenario_account_payment_dunning.rst new file mode 100644 index 0000000000..ac48fcd914 --- /dev/null +++ b/tests/scenario_account_payment_dunning.rst @@ -0,0 +1,134 @@ +================================ +Account Payment Dunning Scenario +================================ + +Imports:: + + >>> import datetime as dt + >>> from decimal import Decimal + + >>> from proteus import Model, Wizard + >>> from trytond.tests.tools import activate_modules, set_user + >>> from trytond.modules.company.tests.tools import ( + ... create_company, get_company) + >>> from trytond.modules.account.tests.tools import ( + ... create_fiscalyear, create_chart, get_accounts) + + >>> today = dt.date.today() + +Activate modules:: + + >>> config = activate_modules(['account_payment', 'account_dunning']) + + >>> Dunning = Model.get('account.dunning') + >>> Journal = Model.get('account.journal') + >>> Move = Model.get('account.move') + >>> Party = Model.get('party.party') + >>> PaymentJournal = Model.get('account.payment.journal') + >>> Procedure = Model.get('account.dunning.procedure') + +Create company:: + + >>> _ = create_company() + >>> company = get_company() + +Create chart of accounts:: + + >>> _ = create_chart(company) + >>> accounts = get_accounts(company) + + >>> expense_journal, = Journal.find([('code', '=', 'EXP')]) + +Create fiscal year:: + + >>> fiscalyear = create_fiscalyear(company) + >>> fiscalyear.click('create_period') + +Create dunning procedure:: + + >>> procedure = Procedure(name='Procedure') + >>> level = procedure.levels.new(overdue=dt.timedelta(0)) + >>> procedure.save() + +Create payment journal:: + + >>> payment_journal = PaymentJournal( + ... name='Manual', + ... process_method='manual') + >>> payment_journal.save() + +Create parties:: + + >>> customer = Party(name='Customer') + >>> customer.dunning_procedure = procedure + >>> customer.save() + +Create payable move:: + + >>> move = Move() + >>> move.journal = expense_journal + >>> line = move.lines.new() + >>> line.party = customer + >>> line.account = accounts['receivable'] + >>> line.debit = Decimal('50.00') + >>> line.maturity_date = today + >>> line = move.lines.new() + >>> line.account = accounts['revenue'] + >>> line.credit = Decimal('50.00') + >>> move.click('post') + +Make a payment:: + + >>> line, = [l for l in move.lines if l.account == accounts['receivable']] + >>> line.payment_amount + Decimal('50.00') + >>> pay_line = Wizard('account.move.line.pay', [line]) + >>> pay_line.execute('next_') + >>> pay_line.execute('next_') + >>> payment, = line.payments + >>> line.payment_amount + Decimal('0.00') + +Create no dunning:: + + >>> create_dunning = Wizard('account.dunning.create') + >>> create_dunning.execute('create_') + >>> Dunning.find([]) + [] + +Fail the payment:: + + >>> payment.click('approve') + >>> process_payment = Wizard('account.payment.process', [payment]) + >>> process_payment.execute('process') + >>> payment.click('fail') + >>> payment.state + 'failed' + >>> line.reload() + >>> line.payment_amount + Decimal('50.00') + +Create dunning:: + + >>> create_dunning = Wizard('account.dunning.create') + >>> create_dunning.execute('create_') + >>> dunning, = Dunning.find([]) + >>> dunning.line == line + True + +Recreate a payment:: + + >>> pay_line = Wizard('account.move.line.pay', [line]) + >>> pay_line.execute('next_') + >>> pay_line.execute('next_') + >>> _, payment = line.payments + >>> payment.state + 'draft' + +Dunning is inactive:: + + >>> dunning.reload() + >>> dunning.active + False + >>> Dunning.find([]) + [] diff --git a/tests/test_account_payment.py b/tests/test_account_payment.py index 02e3c7d841..bbe1369897 100644 --- a/tests/test_account_payment.py +++ b/tests/test_account_payment.py @@ -39,4 +39,9 @@ def suite(): tearDown=doctest_teardown, encoding='utf-8', checker=doctest_checker, optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)) + suite.addTests(doctest.DocFileSuite( + 'scenario_account_payment_dunning.rst', + tearDown=doctest_teardown, encoding='utf-8', + checker=doctest_checker, + optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)) return suite diff --git a/tryton.cfg b/tryton.cfg index dde04ed818..f279c80c5e 100644 --- a/tryton.cfg +++ b/tryton.cfg @@ -8,6 +8,7 @@ depends: party res extras_depend: + account_dunning account_invoice xml: payment.xml