diff --git a/account_banking_mandate/__init__.py b/account_banking_mandate/__init__.py index bd2b7ef7b..cde864bae 100644 --- a/account_banking_mandate/__init__.py +++ b/account_banking_mandate/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# © 2014 Compassion CH - Cyril Sester -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from . import models +# -*- coding: utf-8 -*- + +from . import models diff --git a/account_banking_mandate/models/account_banking_mandate.py b/account_banking_mandate/models/account_banking_mandate.py index 08df4799c..fbecc4ca8 100644 --- a/account_banking_mandate/models/account_banking_mandate.py +++ b/account_banking_mandate/models/account_banking_mandate.py @@ -1,134 +1,134 @@ -# -*- coding: utf-8 -*- -# © 2014 Compassion CH - Cyril Sester -# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza -# © 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, _ -from openerp.exceptions import UserError, ValidationError - - -class AccountBankingMandate(models.Model): - """The banking mandate is attached to a bank account and represents an - authorization that the bank account owner gives to a company for a - specific operation (such as direct debit) - """ - _name = 'account.banking.mandate' - _description = "A generic banking mandate" - _rec_name = 'unique_mandate_reference' - _inherit = ['mail.thread'] - _order = 'signature_date desc' - - format = fields.Selection( - [('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') - partner_id = fields.Many2one( - comodel_name='res.partner', related='partner_bank_id.partner_id', - string='Partner', store=True) - company_id = fields.Many2one( - comodel_name='res.company', string='Company', required=True, - default=lambda self: self.env['res.company']._company_default_get( - 'account.banking.mandate')) - unique_mandate_reference = fields.Char( - 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([ - ('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.") - payment_line_ids = fields.One2many( - comodel_name='account.payment.line', inverse_name='mandate_id', - string="Related Payment Lines") - - _sql_constraints = [( - 'mandate_ref_company_uniq', - 'unique(unique_mandate_reference, company_id)', - 'A Mandate with the same reference already exists for this company !')] - - @api.multi - @api.constrains('signature_date', 'last_debit_date') - def _check_dates(self): - for mandate in self: - if (mandate.signature_date and - mandate.signature_date > fields.Date.context_today( - mandate)): - 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 ValidationError( - _("The mandate '%s' can't have a date of last debit " - "before the date of signature." - ) % mandate.unique_mandate_reference) - - @api.multi - @api.constrains('state', 'partner_bank_id') - def _check_valid_state(self): - for mandate in self: - if mandate.state == 'valid': - if not mandate.signature_date: - raise ValidationError( - _("Cannot validate the mandate '%s' without a date of " - "signature.") % mandate.unique_mandate_reference) - if not mandate.partner_bank_id: - 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', 'New') == 'New': - vals['unique_mandate_reference'] = \ - self.env['ir.sequence'].next_by_code( - 'account.banking.mandate') or 'New' - return super(AccountBankingMandate, self).create(vals) - - @api.multi - @api.onchange('partner_bank_id') - def mandate_partner_bank_change(self): - for mandate in self: - mandate.partner_id = mandate.partner_bank_id.partner_id - - @api.multi - def validate(self): - for mandate in self: - if mandate.state != 'draft': - raise UserError( - _('Mandate should be in draft state')) - self.write({'state': 'valid'}) - return True - - @api.multi - def cancel(self): - for mandate in self: - if mandate.state not in ('draft', 'valid'): - raise UserError( - _('Mandate should be in draft or valid state')) - self.write({'state': 'cancel'}) - return True - - @api.multi - def back2draft(self): - """Allows to set the mandate back to the draft state. - This is for mandates cancelled by mistake. - """ - for mandate in self: - if mandate.state != 'cancel': - raise UserError( - _('Mandate should be in cancel state')) - self.write({'state': 'draft'}) - return True +# -*- coding: utf-8 -*- +# © 2014 Compassion CH - Cyril Sester +# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza +# © 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, _ +from openerp.exceptions import UserError, ValidationError + + +class AccountBankingMandate(models.Model): + """The banking mandate is attached to a bank account and represents an + authorization that the bank account owner gives to a company for a + specific operation (such as direct debit) + """ + _name = 'account.banking.mandate' + _description = "A generic banking mandate" + _rec_name = 'unique_mandate_reference' + _inherit = ['mail.thread'] + _order = 'signature_date desc' + + format = fields.Selection( + [('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') + partner_id = fields.Many2one( + comodel_name='res.partner', related='partner_bank_id.partner_id', + string='Partner', store=True) + company_id = fields.Many2one( + comodel_name='res.company', string='Company', required=True, + default=lambda self: self.env['res.company']._company_default_get( + 'account.banking.mandate')) + unique_mandate_reference = fields.Char( + 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([ + ('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.") + payment_line_ids = fields.One2many( + comodel_name='account.payment.line', inverse_name='mandate_id', + string="Related Payment Lines") + + _sql_constraints = [( + 'mandate_ref_company_uniq', + 'unique(unique_mandate_reference, company_id)', + 'A Mandate with the same reference already exists for this company !')] + + @api.multi + @api.constrains('signature_date', 'last_debit_date') + def _check_dates(self): + for mandate in self: + if (mandate.signature_date and + mandate.signature_date > fields.Date.context_today( + mandate)): + 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 ValidationError( + _("The mandate '%s' can't have a date of last debit " + "before the date of signature." + ) % mandate.unique_mandate_reference) + + @api.multi + @api.constrains('state', 'partner_bank_id') + def _check_valid_state(self): + for mandate in self: + if mandate.state == 'valid': + if not mandate.signature_date: + raise ValidationError( + _("Cannot validate the mandate '%s' without a date of " + "signature.") % mandate.unique_mandate_reference) + if not mandate.partner_bank_id: + 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', 'New') == 'New': + vals['unique_mandate_reference'] = \ + self.env['ir.sequence'].next_by_code( + 'account.banking.mandate') or 'New' + return super(AccountBankingMandate, self).create(vals) + + @api.multi + @api.onchange('partner_bank_id') + def mandate_partner_bank_change(self): + for mandate in self: + mandate.partner_id = mandate.partner_bank_id.partner_id + + @api.multi + def validate(self): + for mandate in self: + if mandate.state != 'draft': + raise UserError( + _('Mandate should be in draft state')) + self.write({'state': 'valid'}) + return True + + @api.multi + def cancel(self): + for mandate in self: + if mandate.state not in ('draft', 'valid'): + raise UserError( + _('Mandate should be in draft or valid state')) + self.write({'state': 'cancel'}) + return True + + @api.multi + def back2draft(self): + """Allows to set the mandate back to the draft state. + This is for mandates cancelled by mistake. + """ + for mandate in self: + if mandate.state != 'cancel': + 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 771150c2d..e1152569c 100644 --- a/account_banking_mandate/models/account_invoice.py +++ b/account_banking_mandate/models/account_invoice.py @@ -1,27 +1,42 @@ -# -*- 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, api - - -class AccountInvoice(models.Model): - _inherit = 'account.invoice' - - mandate_id = fields.Many2one( - 'account.banking.mandate', string='Direct Debit Mandate', - 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 +# -*- 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, api + + +class AccountInvoice(models.Model): + _inherit = 'account.invoice' + + mandate_id = fields.Many2one( + 'account.banking.mandate', string='Direct Debit Mandate', + 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 + + # If a customer pays via direct debit, it's refunds should + # be deducted form the next debit by default. The module + # account_payment_partner copies payment_mode_id from invoice + # to refund, and we also need to copy mandate from invoice to refund + @api.model + def _prepare_refund( + self, invoice, date_invoice=None, date=None, description=None, + journal_id=None): + vals = super(AccountInvoice, self)._prepare_refund( + invoice, date_invoice=date_invoice, date=date, + description=description, journal_id=journal_id) + if invoice.type == 'out_invoice': + vals['mandate_id'] = invoice.mandate_id.id + return vals diff --git a/account_banking_mandate/models/res_partner_bank.py b/account_banking_mandate/models/res_partner_bank.py index 51b8fa14f..119d3b4ff 100644 --- a/account_banking_mandate/models/res_partner_bank.py +++ b/account_banking_mandate/models/res_partner_bank.py @@ -1,16 +1,16 @@ -# -*- coding: utf-8 -*- -# © 2014 Compassion CH - Cyril Sester -# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from openerp import models, fields - - -class ResPartnerBank(models.Model): - _inherit = 'res.partner.bank' - - mandate_ids = fields.One2many( - comodel_name='account.banking.mandate', inverse_name='partner_bank_id', - string='Direct Debit Mandates', - help='Banking mandates represents an authorization that the bank ' - 'account owner gives to a company for a specific operation') +# -*- coding: utf-8 -*- +# © 2014 Compassion CH - Cyril Sester +# © 2014 Serv. Tecnol. Avanzados - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp import models, fields + + +class ResPartnerBank(models.Model): + _inherit = 'res.partner.bank' + + mandate_ids = fields.One2many( + comodel_name='account.banking.mandate', inverse_name='partner_bank_id', + string='Direct Debit Mandates', + help='Banking mandates represents an authorization that the bank ' + 'account owner gives to a company for a specific operation') diff --git a/account_banking_mandate/views/account_invoice_view.xml b/account_banking_mandate/views/account_invoice_view.xml index e562c8118..41fcc966c 100644 --- a/account_banking_mandate/views/account_invoice_view.xml +++ b/account_banking_mandate/views/account_invoice_view.xml @@ -13,7 +13,8 @@ - + diff --git a/account_payment_order/models/account_payment_order.py b/account_payment_order/models/account_payment_order.py index 67bb0c790..fe214b8d2 100644 --- a/account_payment_order/models/account_payment_order.py +++ b/account_payment_order/models/account_payment_order.py @@ -119,10 +119,11 @@ class AccountPaymentOrder(models.Model): for order in self: allowed_journal_ids = False if order.payment_mode_id: - if order.payment_mode_id.bank_account_link == 'fixed': - allowed_journal_ids = order.payment_mode_id.fixed_journal_id + mode = order.payment_mode_id + if mode.bank_account_link == 'fixed': + allowed_journal_ids = mode.fixed_journal_id else: - allowed_journal_ids = order.payment_mode_id.variable_journal_ids + allowed_journal_ids = mode.variable_journal_ids order.allowed_journal_ids = allowed_journal_ids @api.model diff --git a/account_payment_partner/models/account_invoice.py b/account_payment_partner/models/account_invoice.py index d67770198..cefc81ecd 100644 --- a/account_payment_partner/models/account_invoice.py +++ b/account_payment_partner/models/account_invoice.py @@ -37,3 +37,19 @@ class AccountInvoice(models.Model): invoice = self.browse(line['invoice_id']) res['payment_mode_id'] = invoice.payment_mode_id.id or False return res + + # I think copying payment mode from invoice to refund by default + # is a good idea because the most common way of "paying" a refund is to + # deduct it on the payment of the next invoice (and OCA/bank-payment + # allows to have negative payment lines since March 2016) + @api.model + def _prepare_refund( + self, invoice, date_invoice=None, date=None, description=None, + journal_id=None): + vals = super(AccountInvoice, self)._prepare_refund( + invoice, date_invoice=date_invoice, date=date, + description=description, journal_id=journal_id) + vals['payment_mode_id'] = invoice.payment_mode_id.id + if invoice.type == 'in_invoice': + vals['partner_bank_id'] = invoice.partner_bank_id.id + return vals