diff --git a/account_banking_sepa_direct_debit/__openerp__.py b/account_banking_sepa_direct_debit/__openerp__.py index 8c6a69ef5..e02e72914 100644 --- a/account_banking_sepa_direct_debit/__openerp__.py +++ b/account_banking_sepa_direct_debit/__openerp__.py @@ -34,6 +34,7 @@ 'account_banking_sdd_view.xml', 'account_payment_view.xml', 'company_view.xml', + 'mandate_expire_cron.xml', 'wizard/export_sdd_view.xml', 'data/payment_type_sdd.xml', 'data/mandate_reference_sequence.xml', diff --git a/account_banking_sepa_direct_debit/account_banking_sdd.py b/account_banking_sepa_direct_debit/account_banking_sdd.py index ffbe8ee89..33b7d1bd1 100644 --- a/account_banking_sepa_direct_debit/account_banking_sdd.py +++ b/account_banking_sepa_direct_debit/account_banking_sdd.py @@ -23,6 +23,13 @@ from openerp.osv import orm, fields from openerp.tools.translate import _ from openerp.addons.decimal_precision import decimal_precision as dp from unidecode import unidecode +from datetime import datetime +from dateutil.relativedelta import relativedelta +import logging + +NUMBER_OF_UNUSED_MONTHS_BEFORE_EXPIRY = 36 + +logger = logging.getLogger(__name__) class banking_export_sdd(orm.Model): @@ -88,7 +95,16 @@ class sdd_mandate(orm.Model): _name = 'sdd.mandate' _description = __doc__ _rec_name = 'unique_mandate_reference' + _inherit = ['mail.thread'] _order = 'signature_date desc' + _track = { + 'state': { + 'account_banking_sepa_direct_debit.mandate_valid': + lambda self, cr, uid, obj, ctx=None: obj['state'] == 'valid', + 'account_banking_sepa_direct_debit.mandate_expired': + lambda self, cr, uid, obj, ctx=None: obj['state'] == 'expired', + } + } _columns = { 'partner_bank_id': fields.many2one('res.partner.bank', 'Bank Account'), @@ -108,25 +124,98 @@ class sdd_mandate(orm.Model): 'Date of the Last Debit', help="For recurrent mandates, this field is used to know if the SDD will be of type 'First' or 'Recurring'. For one-off mandates, this field is used to know if the SDD has already been used or not."), 'state': fields.selection([ + ('draft', 'Draft'), ('valid', 'Valid'), ('expired', 'Expired'), + # Do we have to handle cancellation of mandate by customer ? ], 'Mandate Status', help="For a recurrent mandate, this field indicate if the mandate is still valid or if it has expired (a recurrent mandate expires if it's not used during 36 months). For a one-off mandate, it expires after its first use."), + 'payment_line_ids': fields.one2many( + 'payment.line', 'sdd_mandate_id', "Related Payment Lines"), } + _defaults = { + 'company_id': lambda self, cr, uid, context: + self.pool['res.company'].\ + _company_default_get(cr, uid, 'sdd.mandate', context=context), + 'unique_mandate_reference': lambda self, cr, uid, ctx: + self.pool['ir.sequence'].get(cr, uid, 'sdd.mandate.reference'), + 'state': 'draft', + } + _sql_constraints = [( 'mandate_ref_company_uniq', 'unique(unique_mandate_reference, company_id)', 'A Mandate with the same reference already exists for this company !' )] - _defaults = { - 'company_id': lambda self, cr, uid, ctx: - self.pool['res.users'].browse(cr, uid, uid, ctx=ctx).company_id.id, - 'unique_mandate_reference': lambda self, cr, uid, ctx: - self.pool['ir.sequence'].get(cr, uid, 'sdd.mandate.reference'), - 'state': 'valid', - } + def _check_sdd_mandate(self, cr, uid, ids, context=None): + for mandate in self.read(cr, uid, ids, [ + 'last_debit_date', 'signature_date', + 'unique_mandate_reference', 'state', 'partner_bank_id' + ], context=context): + if (mandate['signature_date'] and + mandate['signature_date'] > + datetime.today().strftime('%Y-%m-%d')): + raise orm.except_orm( + _('Error:'), + _("The date of signature of mandate '%s' is in the future!") + % mandate['unique_mandate_reference']) + if mandate['state'] == 'valid' and not mandate['signature_date']: + raise orm.except_orm( + _('Error:'), + _("Cannot validate the mandate '%s' without a date of signature.") + % mandate['unique_mandate_reference']) + if mandate['state'] == 'valid' and not mandate['partner_bank_id']: + raise orm.except_orm( + _('Error:'), + _("Cannot validate the mandate '%s' because it is not linked to a bank account.") + % mandate['unique_mandate_reference']) + + if (mandate['signature_date'] and mandate['last_debit_date'] and + mandate['signature_date'] > mandate['last_debit_date']): + raise orm.except_orm( + _('Error:'), + _("The mandate '%s' can't have a date of last debit before the date of signature.") + % mandate['unique_mandate_reference']) + return True + + _constraints = [ + (_check_sdd_mandate, "Error msg in raise", + ['last_debit_date', 'signature_date', 'state', 'partner_bank_id']), + ] + + def validate(self, cr, uid, ids, context=None): + to_validate_ids = [] + for mandate in self.browse(cr, uid, ids, context=context): + assert mandate.state == 'draft', 'Mandate should be in draft state' + to_validate_ids.append(mandate.id) + self.write( + cr, uid, to_validate_ids, {'state': 'valid'}, context=context) + return True + + def _sdd_mandate_set_state_to_expired(self, cr, uid, context=None): + logger.info('Searching for SDD Mandates that must be set to Expired') + expire_limit_date = datetime.today() + \ + relativedelta(months=-NUMBER_OF_UNUSED_MONTHS_BEFORE_EXPIRY) + expire_limit_date_str = expire_limit_date.strftime('%Y-%m-%d') + expired_mandate_ids = self.search(cr, uid, [ + '|', + ('last_debit_date', '=', False), + ('last_debit_date', '<=', expire_limit_date_str), + ('state', '=', 'valid'), + ('signature_date', '<=', expire_limit_date_str), + ], context=context) + if expired_mandate_ids: + self.write( + cr, uid, expired_mandate_ids, {'state': 'expired'}, + context=context) + logger.info( + 'The following SDD Mandate IDs has been set to expired: %s' + % expired_mandate_ids) + else: + logger.info('0 SDD Mandates must be set to Expired') + return True class res_partner_bank(orm.Model): @@ -146,23 +235,20 @@ class payment_line(orm.Model): 'sdd.mandate', 'SEPA Direct Debit Mandate'), } - def _check_sdd_mandate(self, cr, uid, ids): - for payline in self.browse(cr, uid, ids): - if payline.sdd_mandate_id and not payline.bank_id: - raise orm.except_orm( - _('Error:'), - _("Missing bank account on the payment line with SEPA Direct Debit Mandate '%s'.") - % payline.sdd_mandate_id.unique_mandate_reference) - elif (payline.sdd_mandate_id and payline.bank_id and - payline.sdd_mandate_id.partner_bank_id != - payline.bank_id.id): - raise orm.except_orm( - _('Error:'), - _("The SEPA Direct Debit Mandate '%s' is not related???")) - return True - -# _constraints = [ -# (_check_sdd_mandate, "Mandate must be attached to bank account", ['bank_id', 'sdd_mandate_id']), -# ] - - # TODO inherit create to select the first mandate ?? + def create(self, cr, uid, vals, context=None): + '''Take the first valid mandate of the bank account by default''' + if context is None: + context = {} + if not vals: + vals = {} + partner_bank_id = vals.get('bank_id') + if (context.get('default_payment_order_type') == 'debit' + and partner_bank_id + and 'sdd_mandate_id' not in vals): + mandate_ids = self.pool['sdd.mandate'].search(cr, uid, [ + ('partner_bank_id', '=', partner_bank_id), + ('state', '=', 'valid'), + ], context=context) + if mandate_ids: + vals['sdd_mandate_id'] = mandate_ids[0] + return super(payment_line, self).create(cr, uid, vals, context=context) diff --git a/account_banking_sepa_direct_debit/account_banking_sdd_view.xml b/account_banking_sepa_direct_debit/account_banking_sdd_view.xml index db0b255a2..5f191e693 100644 --- a/account_banking_sepa_direct_debit/account_banking_sdd_view.xml +++ b/account_banking_sepa_direct_debit/account_banking_sdd_view.xml @@ -56,7 +56,7 @@ - Generated SEPA Direct Debit XML files + Generated SEPA Direct Debit Files banking.export.sdd form tree,form @@ -66,11 +66,11 @@
+

-

- + - + - + + + +
+
+ + +
@@ -111,9 +118,9 @@ sdd.mandate.tree sdd.mandate - + - + @@ -123,8 +130,23 @@ + + sdd.mandate.search + sdd.mandate + + + + + + + + + + + + - SEPA Direct Debit Mandate + SEPA Direct Debit Mandates sdd.mandate form tree,form @@ -144,6 +166,23 @@ sequence="20" /> + + + Mandate Validated + sdd.mandate + + SEPA Direct Debit Mandate Validated + + + + Mandate Expired + sdd.mandate + + SEPA Direct Debit Mandate has Expired + + + + sdd.mandate.res.partner.bank.form res.partner.bank diff --git a/account_banking_sepa_direct_debit/mandate_expire_cron.xml b/account_banking_sepa_direct_debit/mandate_expire_cron.xml new file mode 100644 index 000000000..4cb0693d2 --- /dev/null +++ b/account_banking_sepa_direct_debit/mandate_expire_cron.xml @@ -0,0 +1,26 @@ + + + + + + + + + Set SEPA Direct Debit Mandates to Expired + + + 1 + days + -1 + + + + + + + +