From ea724b8a22791abab3f8425fcefe7a88fa3dafb3 Mon Sep 17 00:00:00 2001 From: Sergi Almacellas Abellana Date: Sat, 1 Sep 2018 11:21:06 +0200 Subject: [PATCH] Add invoice payment method issue7450 review45561002 --- CHANGELOG | 1 + __init__.py | 1 + doc/index.rst | 14 +++ invoice.py | 87 ++++++++++++------- invoice.xml | 44 ++++++++++ tests/scenario_invoice.rst | 40 +++++---- tests/scenario_invoice_alternate_currency.rst | 34 +++++--- view/pay_ask_form.xml | 4 +- view/pay_start_form.xml | 4 +- view/payment_method_form.xml | 17 ++++ view/payment_method_tree.xml | 7 ++ 11 files changed, 190 insertions(+), 63 deletions(-) create mode 100644 view/payment_method_form.xml create mode 100644 view/payment_method_tree.xml diff --git a/CHANGELOG b/CHANGELOG index 460c53def5..f2fb50c070 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Add invoice payment method * Show number and reference in invoice rec_name * Remove support for Python 2.7 * Use today as fallback for invoice date report diff --git a/__init__.py b/__init__.py index 422837c506..aa806d4b1c 100644 --- a/__init__.py +++ b/__init__.py @@ -35,6 +35,7 @@ def register(): Move, MoveLine, Reconciliation, + PaymentMethod, module='account_invoice', type_='model') Pool.register( TestPaymentTerm, diff --git a/doc/index.rst b/doc/index.rst index d8a4ec55bc..bb72e16758 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -127,6 +127,20 @@ computed terms base on an amount and a date. .. note:: The last line of payment term must be a remainder. +Payment Method +************** + +The payment options available when paying an invoice. It has the following +fields: + +- Name +- Company +- Journal: Will be used for creating the payment move +- Credit Account and Debit Account: The accounts used for the payment move + depending on the amount sign. +- Active: A checkbox that allow to disable the payment method. + + Fiscal Year Sequences ********************* diff --git a/invoice.py b/invoice.py index cad9b7ba59..ebf43f9184 100644 --- a/invoice.py +++ b/invoice.py @@ -10,7 +10,7 @@ from sql.conditionals import Coalesce, Case from sql.functions import Round from trytond.model import Workflow, ModelView, ModelSQL, fields, Check, \ - sequence_ordered, Unique + sequence_ordered, Unique, DeactivableMixin from trytond.report import Report from trytond.wizard import Wizard, StateView, StateTransition, StateAction, \ Button @@ -26,7 +26,7 @@ from trytond.modules.account.tax import TaxableMixin from trytond.modules.product import price_digits __all__ = ['Invoice', 'InvoicePaymentLine', 'InvoiceLine', - 'InvoiceLineTax', 'InvoiceTax', + 'InvoiceLineTax', 'InvoiceTax', 'PaymentMethod', 'InvoiceReport', 'PayInvoiceStart', 'PayInvoiceAsk', 'PayInvoice', 'CreditInvoiceStart', 'CreditInvoice'] @@ -239,16 +239,6 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): 'invoice "%(invoice)s" on fiscalyear "%(fiscalyear)s".'), 'modify_invoice': ('You can not modify invoice "%s" because ' 'it is posted, paid or cancelled.'), - 'same_debit_account': ('The debit account on journal ' - '"%(journal)s" is the same as invoice "%(invoice)s"\'s ' - 'account.'), - 'missing_debit_account': ('The debit account on journal "%s" ' - 'is missing.'), - 'same_credit_account': ('The credit account on journal ' - '"%(journal)s" is the same as invoice "%(invoice)s"\'s ' - 'account.'), - 'missing_credit_account': ('The credit account on journal ' - '"%s" is missing.'), 'same_account_on_line': ('Invoice "%(invoice)s" uses the same ' 'account "%(account)s" for the invoice and in line ' '"%(line)s".'), @@ -1190,7 +1180,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): best = result return best - def pay_invoice(self, amount, journal, date, description, + def pay_invoice(self, amount, payment_method, date, description, amount_second_currency=None, second_currency=None): ''' Adds a payment of amount to an invoice using the journal, date and @@ -1219,18 +1209,10 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): line2.debit, line2.credit = line1.credit, line1.debit if line2.debit: - account_journal = 'debit_account' + payment_acccount = 'debit_account' else: - account_journal = 'credit_account' - line2.account = getattr(journal, account_journal) - if self.account == line2.account: - self.raise_user_error('same_%s' % account_journal, { - 'journal': journal.rec_name, - 'invoice': self.rec_name, - }) - if not line2.account: - self.raise_user_error('missing_%s' % account_journal, - (journal.rec_name,)) + payment_acccount = 'credit_account' + line2.account = getattr(payment_method, payment_acccount) for line in lines: if line.account.party_required: @@ -1242,7 +1224,8 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): period_id = Period.find(self.company.id, date=date) - move = Move(journal=journal, period=period_id, date=date, + move = Move( + journal=payment_method.journal, period=period_id, date=date, company=self.company, lines=lines) move.save() Move.post([move]) @@ -2378,6 +2361,33 @@ class InvoiceTax(sequence_ordered(), ModelSQL, ModelView): return line +class PaymentMethod(DeactivableMixin, ModelSQL, ModelView): + 'Payment Method' + __name__ = 'account.invoice.payment.method' + company = fields.Many2One('company.company', "Company", required=True) + name = fields.Char("Name", required=True, translate=True) + journal = fields.Many2One('account.journal', "Journal", required=True, + domain=[('type', '=', 'cash')]) + credit_account = fields.Many2One('account.account', "Credit Account", + required=True, + domain=[ + ('kind', '!=', 'view'), + ('company', '=', Eval('company')), + ], + depends=['company']) + debit_account = fields.Many2One('account.account', "Debit Account", + required=True, + domain=[ + ('kind', '!=', 'view'), + ('company', '=', Eval('company')), + ], + depends=['company']) + + @classmethod + def default_company(cls): + return Transaction().context.get('company') + + class InvoiceReport(Report): __name__ = 'account.invoice' @@ -2441,8 +2451,17 @@ class PayInvoiceStart(ModelView): currency = fields.Many2One('currency.currency', 'Currency', required=True) currency_digits = fields.Integer('Currency Digits', readonly=True) description = fields.Char('Description', size=None) - journal = fields.Many2One('account.journal', 'Journal', required=True, - domain=[('type', '=', 'cash')]) + company = fields.Many2One('company.company', "Company", readonly=True) + invoice_account = fields.Many2One( + 'account.account', "Invoice Account", readonly=True) + payment_method = fields.Many2One( + 'account.invoice.payment.method', "Payment Method", required=True, + domain=[ + ('company', '=', Eval('company')), + ('debit_account', '!=', Eval('invoice_account')), + ('credit_account', '!=', Eval('invoice_account')), + ], + depends=['company', 'amount', 'invoice_account']) date = fields.Date('Date', required=True) @staticmethod @@ -2468,14 +2487,16 @@ class PayInvoiceAsk(ModelView): ('writeoff', 'Write-Off'), ('partial', 'Partial Payment'), ], 'Type', required=True) - journal_writeoff = fields.Many2One('account.journal', 'Write-Off Journal', + writeoff = fields.Many2One( + 'account.move.reconcile.write_off', "Write Off", domain=[ - ('type', '=', 'write-off'), + ('company', '=', Eval('company')), ], states={ 'invisible': Eval('type') != 'writeoff', 'required': Eval('type') == 'writeoff', - }, depends=['type']) + }, + depends=['company', 'type']) amount = fields.Numeric('Payment Amount', digits=(16, Eval('currency_digits', 2)), readonly=True, depends=['currency_digits']) @@ -2577,11 +2598,13 @@ class PayInvoice(Wizard): Invoice = Pool().get('account.invoice') default = {} invoice = Invoice(Transaction().context['active_id']) + default['company'] = invoice.company.id default['currency'] = invoice.currency.id default['currency_digits'] = invoice.currency.digits default['amount'] = (invoice.amount_to_pay_today or invoice.amount_to_pay) default['description'] = invoice.number + default['invoice_account'] = invoice.account.id return default def transition_choice(self): @@ -2675,7 +2698,7 @@ class PayInvoice(Wizard): line = None if not invoice.company.currency.is_zero(amount): line = invoice.pay_invoice(amount, - self.start.journal, self.start.date, + self.start.payment_method, self.start.date, self.start.description, amount_second_currency, second_currency) @@ -2690,7 +2713,7 @@ class PayInvoice(Wizard): lines += [line] if lines: MoveLine.reconcile(lines, - journal=self.ask.journal_writeoff, + writeoff=self.ask.writeoff, date=self.start.date) else: if line: diff --git a/invoice.xml b/invoice.xml index 544e00366f..63b1ad9dec 100644 --- a/invoice.xml +++ b/invoice.xml @@ -315,6 +315,50 @@ this repository contains the full copyright notices and license terms. --> pay_ask_form + + account.invoice.payment.method + form + payment_method_form + + + account.invoice.payment.method + tree + payment_method_tree + + + Invoice Payment Methods + account.invoice.payment.method + + + + + + + + + + + + + + + + + + + + + + + + + + + + + account.invoice.credit.start form diff --git a/tests/scenario_invoice.rst b/tests/scenario_invoice.rst index eabb4bc7b0..8ae0ce0d45 100644 --- a/tests/scenario_invoice.rst +++ b/tests/scenario_invoice.rst @@ -62,22 +62,32 @@ Create tax:: >>> credit_note_tax_code = create_tax_code(tax, 'tax', 'credit') >>> credit_note_tax_code.save() -Set Cash journal:: +Create payment method:: >>> Journal = Model.get('account.journal') - >>> journal_cash, = Journal.find([('type', '=', 'cash')]) - >>> journal_cash.credit_account = account_cash - >>> journal_cash.debit_account = account_cash - >>> journal_cash.save() - -Create Write-Off journal:: - + >>> PaymentMethod = Model.get('account.invoice.payment.method') >>> Sequence = Model.get('ir.sequence') + >>> journal_cash, = Journal.find([('type', '=', 'cash')]) + >>> payment_method = PaymentMethod() + >>> payment_method.name = 'Cash' + >>> payment_method.journal = journal_cash + >>> payment_method.credit_account = account_cash + >>> payment_method.debit_account = account_cash + >>> payment_method.save() + +Create Write Off method:: + + >>> WriteOff = Model.get('account.move.reconcile.write_off') >>> sequence_journal, = Sequence.find([('code', '=', 'account.journal')]) >>> journal_writeoff = Journal(name='Write-Off', type='write-off', - ... sequence=sequence_journal, - ... credit_account=revenue, debit_account=expense) + ... sequence=sequence_journal) >>> journal_writeoff.save() + >>> writeoff_method = WriteOff() + >>> writeoff_method.name = 'Rate loss' + >>> writeoff_method.journal = journal_writeoff + >>> writeoff_method.credit_account = expense + >>> writeoff_method.debit_account = expense + >>> writeoff_method.save() Create party:: @@ -250,7 +260,7 @@ Pay invoice:: >>> pay.form.amount Decimal('240.00') >>> pay.form.amount = Decimal('120.00') - >>> pay.form.journal = journal_cash + >>> pay.form.payment_method = payment_method >>> pay.execute('choice') >>> pay.state 'end' @@ -259,7 +269,7 @@ Pay invoice:: >>> pay.form.amount Decimal('120.00') >>> pay.form.amount = Decimal('20.00') - >>> pay.form.journal = journal_cash + >>> pay.form.payment_method = payment_method >>> pay.execute('choice') >>> pay.form.type = 'partial' >>> pay.form.amount @@ -278,10 +288,10 @@ Pay invoice:: >>> pay.form.amount Decimal('-20.00') >>> pay.form.amount = Decimal('99.00') - >>> pay.form.journal = journal_cash + >>> pay.form.payment_method = payment_method >>> pay.execute('choice') >>> pay.form.type = 'writeoff' - >>> pay.form.journal_writeoff = journal_writeoff + >>> pay.form.writeoff = writeoff_method >>> pay.form.amount Decimal('99.00') >>> len(pay.form.lines_to_pay) @@ -349,7 +359,7 @@ Create a paid invoice:: >>> line.unit_price = Decimal('40') >>> invoice.click('post') >>> pay = Wizard('account.invoice.pay', [invoice]) - >>> pay.form.journal = journal_cash + >>> pay.form.payment_method = payment_method >>> pay.execute('choice') >>> pay.state 'end' diff --git a/tests/scenario_invoice_alternate_currency.rst b/tests/scenario_invoice_alternate_currency.rst index e42a6d151e..7136eb7d21 100644 --- a/tests/scenario_invoice_alternate_currency.rst +++ b/tests/scenario_invoice_alternate_currency.rst @@ -61,22 +61,32 @@ Create tax:: >>> tax = create_tax(Decimal('.10')) >>> tax.save() -Set Cash journal:: +Create payment method:: >>> Journal = Model.get('account.journal') - >>> journal_cash, = Journal.find([('type', '=', 'cash')]) - >>> journal_cash.credit_account = account_cash - >>> journal_cash.debit_account = account_cash - >>> journal_cash.save() - -Create Write-Off journal:: - + >>> PaymentMethod = Model.get('account.invoice.payment.method') >>> Sequence = Model.get('ir.sequence') + >>> journal_cash, = Journal.find([('type', '=', 'cash')]) + >>> payment_method = PaymentMethod() + >>> payment_method.name = 'Cash' + >>> payment_method.journal = journal_cash + >>> payment_method.credit_account = account_cash + >>> payment_method.debit_account = account_cash + >>> payment_method.save() + +Create writeoff method:: + + >>> WriteOff = Model.get('account.move.reconcile.write_off') >>> sequence_journal, = Sequence.find([('code', '=', 'account.journal')]) >>> journal_writeoff = Journal(name='Write-Off', type='write-off', - ... sequence=sequence_journal, - ... credit_account=revenue, debit_account=expense) + ... sequence=sequence_journal) >>> journal_writeoff.save() + >>> writeoff = WriteOff() + >>> writeoff.name = 'Rate loss' + >>> writeoff.journal = journal_writeoff + >>> writeoff.credit_account = expense + >>> writeoff.debit_account = expense + >>> writeoff.save() Create party:: @@ -148,11 +158,11 @@ Pay the invoice with rate change:: >>> pay = Wizard('account.invoice.pay', [invoice]) >>> pay.form.amount Decimal('460.00') - >>> pay.form.journal = journal_cash + >>> pay.form.payment_method = payment_method >>> pay.form.date = tomorrow >>> pay.execute('choice') >>> pay.form.type = 'writeoff' - >>> pay.form.journal_writeoff = journal_writeoff + >>> pay.form.writeoff = writeoff >>> pay.form.amount Decimal('460.00') >>> pay.form.currency == eur diff --git a/view/pay_ask_form.xml b/view/pay_ask_form.xml index 91e8517620..5b8393e409 100644 --- a/view/pay_ask_form.xml +++ b/view/pay_ask_form.xml @@ -12,8 +12,8 @@ this repository contains the full copyright notices and license terms. -->