From 0fa08b14c4f85e78ca3161a5bcd6437af67c2952 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Sat, 30 Apr 2016 01:46:34 +0200 Subject: [PATCH] Start to port bank-payment to v9 (with a lot of improvements) during the Sorrento Code sprint 2016 Improvements include: - full re-organisation of modules and big re-organisation of the code - simplification of the code related to the fact that support for direct debit is now in t he base module, not added by an optional module account_direct_debit (module was removed) - new design of the wizard to select move lines to pay - support for non-SEPA file transfer- - support for German direct debit SEPA files (fixes bug #129) - remove workflow of payment.order This port to v9 is not finished... there is still a lot of work: - finish the code of account_payment_order/wizard/account_payment_line_create.py - port account_banking_payment_transfer and integrate it inside account_payment_order - fix bugs - clean-up code, remove dead code - test in several complex scenarios --- account_banking_mandate/__openerp__.py | 11 ++-- .../data/mandate_reference_sequence.xml | 5 -- account_banking_mandate/models/__init__.py | 3 +- .../models/account_banking_mandate.py | 63 +++++++------------ .../models/account_invoice.py | 17 ++++- .../models/account_move_line.py | 23 +++++++ ...ayment_line.py => account_payment_line.py} | 34 +++++----- .../models/bank_payment_line.py | 2 +- .../security/ir.model.access.csv | 6 +- .../views/account_banking_mandate_view.xml | 26 +------- .../views/account_invoice_view.xml | 6 +- .../views/account_move_line.xml | 26 ++++++++ .../views/account_payment_line.xml | 37 +++++++++++ .../views/account_payment_view.xml | 30 --------- .../views/bank_payment_line_view.xml | 14 ++--- .../views/res_partner_bank_view.xml | 21 ++----- 16 files changed, 171 insertions(+), 153 deletions(-) create mode 100644 account_banking_mandate/models/account_move_line.py rename account_banking_mandate/models/{payment_line.py => account_payment_line.py} (65%) create mode 100644 account_banking_mandate/views/account_move_line.xml create mode 100644 account_banking_mandate/views/account_payment_line.xml delete mode 100644 account_banking_mandate/views/account_payment_view.xml diff --git a/account_banking_mandate/__openerp__.py b/account_banking_mandate/__openerp__.py index cd91e8219..1fff79f66 100644 --- a/account_banking_mandate/__openerp__.py +++ b/account_banking_mandate/__openerp__.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- # © 2014 Compassion CH - Cyril Sester # © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza -# © 2015 Akretion - Alexis de Lattre +# © 2015-2016 Akretion - Alexis de Lattre # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { 'name': 'Account Banking Mandate', 'summary': 'Banking mandates', - 'version': '8.0.0.2.0', + 'version': '9.0.0.2.0', 'license': 'AGPL-3', 'author': "Compassion CH, " "Serv. Tecnol. Avanzados - Pedro M. Baeza, " @@ -16,19 +16,20 @@ 'website': 'https://github.com/OCA/bank-payment', 'category': 'Banking addons', 'depends': [ - 'account_banking_payment_export', + 'account_payment_order', ], 'data': [ 'views/account_banking_mandate_view.xml', 'views/account_invoice_view.xml', - 'views/account_payment_view.xml', + 'views/account_payment_line.xml', 'views/res_partner_bank_view.xml', 'views/bank_payment_line_view.xml', + 'views/account_move_line.xml', 'data/mandate_reference_sequence.xml', 'security/mandate_security.xml', 'security/ir.model.access.csv', ], 'demo': [], 'test': ['test/banking_mandate.yml'], - 'installable': False, + 'installable': True, } diff --git a/account_banking_mandate/data/mandate_reference_sequence.xml b/account_banking_mandate/data/mandate_reference_sequence.xml index 9a51db944..e3df03051 100644 --- a/account_banking_mandate/data/mandate_reference_sequence.xml +++ b/account_banking_mandate/data/mandate_reference_sequence.xml @@ -3,11 +3,6 @@ - - DD Mandate Reference - account.banking.mandate - - DD Mandate Reference account.banking.mandate diff --git a/account_banking_mandate/models/__init__.py b/account_banking_mandate/models/__init__.py index cb0da2fd5..646eec280 100644 --- a/account_banking_mandate/models/__init__.py +++ b/account_banking_mandate/models/__init__.py @@ -6,5 +6,6 @@ from . import account_banking_mandate from . import account_invoice from . import res_partner_bank -from . import payment_line +from . import account_payment_line from . import bank_payment_line +from . import account_move_line diff --git a/account_banking_mandate/models/account_banking_mandate.py b/account_banking_mandate/models/account_banking_mandate.py index 6c969ed00..dccd38e4d 100644 --- a/account_banking_mandate/models/account_banking_mandate.py +++ b/account_banking_mandate/models/account_banking_mandate.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- # © 2014 Compassion CH - Cyril Sester # © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza -# © 2015 Akretion - Alexis de Lattre +# © 2015-2016 Akretion - Alexis de Lattre # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp import models, fields, exceptions, api, _ +from openerp import models, fields, api, _ +from openerp.exceptions import UserError, ValidationError class AccountBankingMandate(models.Model): @@ -17,30 +18,10 @@ class AccountBankingMandate(models.Model): _rec_name = 'unique_mandate_reference' _inherit = ['mail.thread'] _order = 'signature_date desc' - _track = { - 'state': { - 'account_banking_mandate.mandate_valid': ( - lambda self, cr, uid, obj, ctx=None: obj['state'] == 'valid'), - 'account_banking_mandate.mandate_expired': ( - lambda self, cr, uid, obj, ctx=None: - obj['state'] == 'expired'), - 'account_banking_mandate.mandate_cancel': ( - lambda self, cr, uid, obj, ctx=None: obj['state'] == 'cancel'), - }, - } - - def _get_states(self): - return [('draft', 'Draft'), - ('valid', 'Valid'), - ('expired', 'Expired'), - ('cancel', 'Cancelled')] format = fields.Selection( - [('basic', _('Basic Mandate'))], - default='basic', - required=True, - string='Mandate Format', - ) + [('basic', 'Basic Mandate')], default='basic', required=True, + string='Mandate Format', track_visibility='onchange') partner_bank_id = fields.Many2one( comodel_name='res.partner.bank', string='Bank Account', track_visibility='onchange') @@ -52,19 +33,22 @@ class AccountBankingMandate(models.Model): default=lambda self: self.env['res.company']._company_default_get( 'account.banking.mandate')) unique_mandate_reference = fields.Char( - string='Unique Mandate Reference', track_visibility='always', - default='/') + string='Unique Mandate Reference', track_visibility='onchange') signature_date = fields.Date(string='Date of Signature of the Mandate', track_visibility='onchange') scan = fields.Binary(string='Scan of the Mandate') last_debit_date = fields.Date(string='Date of the Last Debit', readonly=True) - state = fields.Selection( - _get_states, string='Status', default='draft', + state = fields.Selection([ + ('draft', 'Draft'), + ('valid', 'Valid'), + ('expired', 'Expired'), + ('cancel', 'Cancelled'), + ], string='Status', default='draft', track_visibility='onchange', help="Only valid mandates can be used in a payment line. A cancelled " - "mandate is a mandate that has been cancelled by the customer.") + "mandate is a mandate that has been cancelled by the customer.") payment_line_ids = fields.One2many( - comodel_name='payment.line', inverse_name='mandate_id', + comodel_name='account.payment.line', inverse_name='mandate_id', string="Related Payment Lines") _sql_constraints = [( @@ -79,13 +63,13 @@ class AccountBankingMandate(models.Model): if (mandate.signature_date and mandate.signature_date > fields.Date.context_today( mandate)): - raise exceptions.Warning( + raise ValidationError( _("The date of signature of mandate '%s' " "is in the future !") % mandate.unique_mandate_reference) if (mandate.signature_date and mandate.last_debit_date and mandate.signature_date > mandate.last_debit_date): - raise exceptions.Warning( + raise ValidationError( _("The mandate '%s' can't have a date of last debit " "before the date of signature." ) % mandate.unique_mandate_reference) @@ -96,20 +80,21 @@ class AccountBankingMandate(models.Model): for mandate in self: if mandate.state == 'valid': if not mandate.signature_date: - raise exceptions.Warning( + raise ValidationError( _("Cannot validate the mandate '%s' without a date of " "signature.") % mandate.unique_mandate_reference) if not mandate.partner_bank_id: - raise exceptions.Warning( + raise ValidationError( _("Cannot validate the mandate '%s' because it is not " "attached to a bank account.") % mandate.unique_mandate_reference) @api.model def create(self, vals=None): - if vals.get('unique_mandate_reference', '/') == '/': + if vals.get('unique_mandate_reference', 'New') == 'New': vals['unique_mandate_reference'] = \ - self.env['ir.sequence'].next_by_code('account.banking.mandate') + self.env['ir.sequence'].next_by_code('account.banking.mandate')\ + or 'New' return super(AccountBankingMandate, self).create(vals) @api.multi @@ -122,7 +107,7 @@ class AccountBankingMandate(models.Model): def validate(self): for mandate in self: if mandate.state != 'draft': - raise exceptions.Warning( + raise UserError( _('Mandate should be in draft state')) self.write({'state': 'valid'}) return True @@ -131,7 +116,7 @@ class AccountBankingMandate(models.Model): def cancel(self): for mandate in self: if mandate.state not in ('draft', 'valid'): - raise exceptions.Warning( + raise UserError( _('Mandate should be in draft or valid state')) self.write({'state': 'cancel'}) return True @@ -143,7 +128,7 @@ class AccountBankingMandate(models.Model): """ for mandate in self: if mandate.state != 'cancel': - raise exceptions.Warning( + raise UserError( _('Mandate should be in cancel state')) self.write({'state': 'draft'}) return True diff --git a/account_banking_mandate/models/account_invoice.py b/account_banking_mandate/models/account_invoice.py index f5eb7f287..cd6e17776 100644 --- a/account_banking_mandate/models/account_invoice.py +++ b/account_banking_mandate/models/account_invoice.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- # © 2014 Compassion CH - Cyril Sester # © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza +# © 2016 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp import models, fields +from openerp import models, fields, api class AccountInvoice(models.Model): @@ -12,5 +13,15 @@ class AccountInvoice(models.Model): mandate_id = fields.Many2one( 'account.banking.mandate', string='Direct Debit Mandate', - domain=[('state', '=', 'valid')], readonly=True, - states={'draft': [('readonly', False)]}) + ondelete='restrict', + readonly=True, states={'draft': [('readonly', False)]}) + + @api.model + def line_get_convert(self, line, part): + """Copy mandate from invoice to account move line""" + res = super(AccountInvoice, self).line_get_convert(line, part) + if line.get('type') == 'dest' and line.get('invoice_id'): + invoice = self.browse(line['invoice_id']) + if invoice.type in ('out_invoice', 'out_refund'): + res['mandate_id'] = invoice.mandate_id.id or False + return res diff --git a/account_banking_mandate/models/account_move_line.py b/account_banking_mandate/models/account_move_line.py new file mode 100644 index 000000000..142bebf4e --- /dev/null +++ b/account_banking_mandate/models/account_move_line.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# © 2016 Akretion (http://www.akretion.com/) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp import models, fields, api + + +class AccountMoveLine(models.Model): + _inherit = 'account.move.line' + + mandate_id = fields.Many2one( + 'account.banking.mandate', string='Direct Debit Mandate', + ondelete='restrict') + + @api.multi + def _prepare_payment_line_vals(self, payment_order): + vals = super(AccountMoveLine, self)._prepare_payment_line_vals( + payment_order) + # TODO : test on the view field "mandate required ?" + if payment_order.payment_type == 'inbound' and self.mandate_id: + vals['mandate_id'] = self.mandate_id.id or False + vals['partner_bank_id'] = self.mandate_id.partner_bank_id.id or False + return vals diff --git a/account_banking_mandate/models/payment_line.py b/account_banking_mandate/models/account_payment_line.py similarity index 65% rename from account_banking_mandate/models/payment_line.py rename to account_banking_mandate/models/account_payment_line.py index 96bd73f41..7083d3352 100644 --- a/account_banking_mandate/models/payment_line.py +++ b/account_banking_mandate/models/account_payment_line.py @@ -1,19 +1,21 @@ # -*- coding: utf-8 -*- # © 2014 Compassion CH - Cyril Sester # © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza -# © 2015 Akretion - Alexis de Lattre +# © 2015-2016 Akretion - Alexis de Lattre # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp import models, fields, api, exceptions, _ +from openerp import models, fields, api, _ +from openerp.exceptions import ValidationError -class PaymentLine(models.Model): - _inherit = 'payment.line' +class AccountPaymentLine(models.Model): + _inherit = 'account.payment.line' mandate_id = fields.Many2one( comodel_name='account.banking.mandate', string='Direct Debit Mandate', domain=[('state', '=', 'valid')]) + # TODO : remove this @api.model def create(self, vals=None): """If the customer invoice has a mandate, take it @@ -21,7 +23,7 @@ class PaymentLine(models.Model): """ if vals is None: vals = {} - partner_bank_id = vals.get('bank_id') + partner_bank_id = vals.get('partner_bank_id') move_line_id = vals.get('move_line_id') if (self.env.context.get('search_payment_order_type') == 'debit' and 'mandate_id' not in vals): @@ -31,7 +33,7 @@ class PaymentLine(models.Model): line.invoice.mandate_id): vals.update({ 'mandate_id': line.invoice.mandate_id.id, - 'bank_id': line.invoice.mandate_id.partner_bank_id.id, + 'partner_bank_id': line.invoice.mandate_id.partner_bank_id.id, }) if partner_bank_id and 'mandate_id' not in vals: mandates = self.env['account.banking.mandate'].search( @@ -39,21 +41,23 @@ class PaymentLine(models.Model): ('state', '=', 'valid')]) if mandates: vals['mandate_id'] = mandates[0].id - return super(PaymentLine, self).create(vals) + return super(AccountPaymentLine, self).create(vals) @api.one - @api.constrains('mandate_id', 'bank_id') + @api.constrains('mandate_id', 'partner_bank_id') def _check_mandate_bank_link(self): - if (self.mandate_id and self.bank_id and + if (self.mandate_id and self.partner_bank_id and self.mandate_id.partner_bank_id.id != - self.bank_id.id): - raise exceptions.Warning( + self.partner_bank_id.id): + raise ValidationError( _("The payment line with reference '%s' has the bank account " "'%s' which is not attached to the mandate '%s' (this " "mandate is attached to the bank account '%s').") % (self.name, - self.env['res.partner.bank'].name_get( - [self.bank_id.id])[0][1], + self.partner_bank_id.name_get()[0][1], self.mandate_id.unique_mandate_reference, - self.env['res.partner.bank'].name_get( - [self.mandate_id.partner_bank_id.id])[0][1])) + self.mandate_id.partner_bank_id.name_get()[0][1])) + +# @api.multi +# def check_payment_line(self): +# TODO : i would like to block here is mandate is missing... but how do you know it's required ? => create option on payment order ? diff --git a/account_banking_mandate/models/bank_payment_line.py b/account_banking_mandate/models/bank_payment_line.py index 4952c17a0..8dd2cbdc8 100644 --- a/account_banking_mandate/models/bank_payment_line.py +++ b/account_banking_mandate/models/bank_payment_line.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # © 2014 Compassion CH - Cyril Sester # © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza -# © 2015 Akretion - Alexis de Lattre +# © 2015-2016 Akretion - Alexis de Lattre # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from openerp import models, fields, api diff --git a/account_banking_mandate/security/ir.model.access.csv b/account_banking_mandate/security/ir.model.access.csv index 07f07a3da..065258d42 100644 --- a/account_banking_mandate/security/ir.model.access.csv +++ b/account_banking_mandate/security/ir.model.access.csv @@ -1,3 +1,3 @@ -"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" -"access_account_banking_mandate","Full access on account.banking.mandate","model_account_banking_mandate","account_payment.group_account_payment",1,1,1,1 -"access_account_banking_mandate_read","Read access on account.banking.mandate","model_account_banking_mandate","base.group_user",1,0,0,0 \ No newline at end of file +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_account_banking_mandate,Full access on account.banking.mandate,model_account_banking_mandate,account_payment_order.group_account_payment,1,1,1,1 +access_account_banking_mandate_read,Read access on account.banking.mandate,model_account_banking_mandate,base.group_user,1,0,0,0 diff --git a/account_banking_mandate/views/account_banking_mandate_view.xml b/account_banking_mandate/views/account_banking_mandate_view.xml index f91c35bd3..a7112cc0f 100644 --- a/account_banking_mandate/views/account_banking_mandate_view.xml +++ b/account_banking_mandate/views/account_banking_mandate_view.xml @@ -101,33 +101,11 @@ - - - Mandate Validated - account.banking.mandate - - Banking Mandate Validated - - - - Mandate Expired - account.banking.mandate - - Banking Mandate has Expired - - - - Mandate Cancelled - account.banking.mandate - - Banking Mandate Cancelled - - diff --git a/account_banking_mandate/views/account_invoice_view.xml b/account_banking_mandate/views/account_invoice_view.xml index a21e83857..e562c8118 100644 --- a/account_banking_mandate/views/account_invoice_view.xml +++ b/account_banking_mandate/views/account_invoice_view.xml @@ -1,6 +1,6 @@ @@ -10,10 +10,10 @@ add.mandate.on.customer.invoice.form account.invoice - + - + diff --git a/account_banking_mandate/views/account_move_line.xml b/account_banking_mandate/views/account_move_line.xml new file mode 100644 index 000000000..69d51471d --- /dev/null +++ b/account_banking_mandate/views/account_move_line.xml @@ -0,0 +1,26 @@ + + + + + + + + + account_banking_mandate.move_line_form + account.move.line + + + + + + + + + + + diff --git a/account_banking_mandate/views/account_payment_line.xml b/account_banking_mandate/views/account_payment_line.xml new file mode 100644 index 000000000..48e6a753f --- /dev/null +++ b/account_banking_mandate/views/account_payment_line.xml @@ -0,0 +1,37 @@ + + + + + + + account_banking_mandate.account.payment.line.form + account.payment.line + + + + + + + + + + account_banking_mandate.account.payment.line.tree + account.payment.line + + + + + + + + + + diff --git a/account_banking_mandate/views/account_payment_view.xml b/account_banking_mandate/views/account_payment_view.xml deleted file mode 100644 index 9d2ced2cf..000000000 --- a/account_banking_mandate/views/account_payment_view.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - mandate.payment.order.form - payment.order - - - - - - - - - - - - - - diff --git a/account_banking_mandate/views/bank_payment_line_view.xml b/account_banking_mandate/views/bank_payment_line_view.xml index 25c8656e5..98ec669a1 100644 --- a/account_banking_mandate/views/bank_payment_line_view.xml +++ b/account_banking_mandate/views/bank_payment_line_view.xml @@ -1,6 +1,6 @@ @@ -10,11 +10,11 @@ banking.mandate.bank.payment.line.form bank.payment.line - + - + + invisible="context.get('default_payment_type')!='inbound'"/> @@ -22,11 +22,11 @@ banking.mandate.bank.payment.line.tree bank.payment.line - + - + + invisible="context.get('default_payment_type')!='inbound'"/> diff --git a/account_banking_mandate/views/res_partner_bank_view.xml b/account_banking_mandate/views/res_partner_bank_view.xml index 572fa766e..60f9cbce1 100644 --- a/account_banking_mandate/views/res_partner_bank_view.xml +++ b/account_banking_mandate/views/res_partner_bank_view.xml @@ -1,6 +1,6 @@ @@ -12,8 +12,8 @@ res.partner.bank - - + + @@ -26,23 +26,10 @@ - + - - - mandate.partner.form - res.partner - - - - - - - -