From 90c3dc1ab93a94060af377e25e65cc35679253a9 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Mon, 11 Nov 2013 02:10:35 +0100 Subject: [PATCH] New field "recurrent_sequence_type" on mandate with tracking. The field "last_debit_date" is not used to compute the sequence type any more. When the bank account changes, the sequence type is set back to first FIX xml_id of expired mandate mail.message.subtype --- .../account_banking_sdd.py | 63 ++++++++++++++++--- .../account_banking_sdd_view.xml | 33 ++++++++-- .../wizard/export_sdd.py | 57 ++++++++++------- 3 files changed, 117 insertions(+), 36 deletions(-) diff --git a/account_banking_sepa_direct_debit/account_banking_sdd.py b/account_banking_sepa_direct_debit/account_banking_sdd.py index dc42188d0..bfe49d544 100644 --- a/account_banking_sepa_direct_debit/account_banking_sdd.py +++ b/account_banking_sepa_direct_debit/account_banking_sdd.py @@ -105,6 +105,17 @@ class sdd_mandate(orm.Model): lambda self, cr, uid, obj, ctx=None: obj['state'] == 'expired', 'account_banking_sepa_direct_debit.mandate_cancel': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'cancel', + }, + 'recurrent_sequence_type': { + 'account_banking_sepa_direct_debit.recurrent_sequence_type_first': + lambda self, cr, uid, obj, ctx=None: + obj['recurrent_sequence_type'] == 'first', + 'account_banking_sepa_direct_debit.recurrent_sequence_type_recurring': + lambda self, cr, uid, obj, ctx=None: + obj['recurrent_sequence_type'] == 'recurring', + 'account_banking_sepa_direct_debit.recurrent_sequence_type_final': + lambda self, cr, uid, obj, ctx=None: + obj['recurrent_sequence_type'] == 'final', } } @@ -120,17 +131,22 @@ class sdd_mandate(orm.Model): ('recurrent', 'Recurrent'), ('oneoff', 'One-Off'), ], 'Type of Mandate', required=True), + 'recurrent_sequence_type': fields.selection([ + ('first', 'First'), + ('recurring', 'Recurring'), + ('final', 'Final'), + ], 'Sequence Type for Next Debit', + help="This field is only used for Recurrent mandates, not for One-Off mandates."), 'signature_date': fields.date('Date of Signature of the Mandate'), 'scan': fields.binary('Scan of the mandate'), 'last_debit_date': fields.date( - '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."), + 'Date of the Last Debit', readonly=True), 'state': fields.selection([ ('draft', 'Draft'), ('valid', 'Valid'), ('expired', 'Expired'), ('cancel', 'Cancelled'), - ], 'Mandate Status', + ], '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."), # TODO : update help 'payment_line_ids': fields.one2many( 'payment.line', 'sdd_mandate_id', "Related Payment Lines"), @@ -154,7 +170,8 @@ class sdd_mandate(orm.Model): 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' + 'unique_mandate_reference', 'state', 'partner_bank_id', + 'type', 'recurrent_sequence_type', ], context=context): if (mandate['signature_date'] and mandate['signature_date'] > @@ -171,7 +188,7 @@ class sdd_mandate(orm.Model): 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.") + _("Cannot validate the mandate '%s' because it is not attached to a bank account.") % mandate['unique_mandate_reference']) if (mandate['signature_date'] and mandate['last_debit_date'] and @@ -180,13 +197,45 @@ class sdd_mandate(orm.Model): _('Error:'), _("The mandate '%s' can't have a date of last debit before the date of signature.") % mandate['unique_mandate_reference']) + if (mandate['type'] == 'recurrent' + and not mandate['recurrent_sequence_type']): + raise orm.except_orm( + _('Error:'), + _("The recurrent mandate '%s' must have a sequence type.") + % mandate['unique_mandate_reference']) return True _constraints = [ - (_check_sdd_mandate, "Error msg in raise", - ['last_debit_date', 'signature_date', 'state', 'partner_bank_id']), + (_check_sdd_mandate, "Error msg in raise", [ + 'last_debit_date', 'signature_date', 'state', 'partner_bank_id', + 'type', 'recurrent_sequence_type', + ]), ] + def mandate_type_change(self, cr, uid, ids, type): + if type == 'recurrent': + recurrent_sequence_type = 'first' + else: + recurrent_sequence_type = False + res = {'value': {'recurrent_sequence_type': recurrent_sequence_type}} + return res + + def mandate_partner_bank_change( + self, cr, uid, ids, partner_bank_id, type, recurrent_sequence_type, + last_debit_date, state): + if (state == 'valid' and partner_bank_id + and type == 'recurrent' + and recurrent_sequence_type != 'first'): + return { + 'value': {'recurrent_sequence_type': first}, + 'warning': { + 'title': _('Mandate update'), + 'message': _("As you changed the bank account attached to this mandate, the 'Sequence Type' has been set back to 'First'."), + } + } + else: + return True + def validate(self, cr, uid, ids, context=None): to_validate_ids = [] for mandate in self.browse(cr, uid, ids, 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 5bb77ecf0..54a5a6669 100644 --- a/account_banking_sepa_direct_debit/account_banking_sdd_view.xml +++ b/account_banking_sepa_direct_debit/account_banking_sdd_view.xml @@ -97,11 +97,12 @@ - + + - + @@ -119,14 +120,14 @@ sdd.mandate.tree sdd.mandate - + - + @@ -139,6 +140,7 @@ + @@ -175,7 +177,7 @@ SEPA Direct Debit Mandate Validated - + Mandate Expired sdd.mandate @@ -189,6 +191,27 @@ SEPA Direct Debit Mandate Cancelled + + Sequence Type set to First + sdd.mandate + + Sequence Type set to First + + + + Sequence Type set to Recurring + sdd.mandate + + Sequence Type set to Recurring + + + + Sequence Type set to Final + sdd.mandate + + Sequence Type set to Final + + sdd.mandate.res.partner.bank.form diff --git a/account_banking_sepa_direct_debit/wizard/export_sdd.py b/account_banking_sepa_direct_debit/wizard/export_sdd.py index df08915bd..60dda58fe 100644 --- a/account_banking_sepa_direct_debit/wizard/export_sdd.py +++ b/account_banking_sepa_direct_debit/wizard/export_sdd.py @@ -233,7 +233,7 @@ class banking_export_sdd_wizard(orm.TransientModel): transactions_count_1_6 = 0 total_amount = 0.0 amount_control_sum_1_7 = 0.0 - first_recur_lines = {} + lines_per_seq_type = {} # key = sequence type ; value = list of lines as objects # Iterate on payment orders for payment_order in sepa_export.payment_order_ids: @@ -253,7 +253,6 @@ class banking_export_sdd_wizard(orm.TransientModel): _("The SEPA Direct Debit mandate with reference '%s' for partner '%s' has expired.") % (line.sdd_mandate_id.unique_mandate_reference, line.sdd_mandate_id.partner_id.name)) - if not line.sdd_mandate_id.signature_date: raise orm.except_orm( _('Error:'), @@ -268,13 +267,12 @@ class banking_export_sdd_wizard(orm.TransientModel): % (line.sdd_mandate_id.unique_mandate_reference, line.sdd_mandate_id.partner_id.name, line.sdd_mandate_id.signature_date)) - if line.sdd_mandate_id.type == 'oneoff': if not line.sdd_mandate_id.last_debit_date: - if first_recur_lines.get('OOFF'): - first_recur_lines['OOFF'].append(line) + if lines_per_seq_type.get('OOFF'): + lines_per_seq_type['OOFF'].append(line) else: - first_recur_lines['OOFF'] = [line] + lines_per_seq_type['OOFF'] = [line] else: raise orm.except_orm( _('Error:'), @@ -283,18 +281,20 @@ class banking_export_sdd_wizard(orm.TransientModel): line.sdd_mandate_id.partner_id.name, line.sdd_mandate_id.last_debit_date)) elif line.sdd_mandate_id.type == 'recurrent': - if line.sdd_mandate_id.last_debit_date: - if first_recur_lines.get('RCUR'): - first_recur_lines['RCUR'].append(line) - else: - first_recur_lines['RCUR'] = [line] + seq_type_map = { + 'recurring': 'RCUR', + 'first': 'FRST', + 'final': 'FNAL', + } + seq_type_label = line.sdd_mandate_id.recurrent_sequence_type + assert seq_type_label is not False + seq_type = seq_type_map[seq_type_label] + if lines_per_seq_type.get(seq_type): + lines_per_seq_type[seq_type].append(line) else: - if first_recur_lines.get('FRST'): - first_recur_lines['FRST'].append(line) - else: - first_recur_lines['FRST'] = [line] + lines_per_seq_type[seq_type] = [line] - for sequence_type, lines in first_recur_lines.items(): + for sequence_type, lines in lines_per_seq_type.items(): # B. Payment info payment_info_2_0 = etree.SubElement(pain_root, 'PmtInf') payment_info_identification_2_1 = etree.SubElement( @@ -516,14 +516,23 @@ class banking_export_sdd_wizard(orm.TransientModel): wf_service.trg_validate(uid, 'payment.order', order.id, 'done', cr) mandate_ids = [line.sdd_mandate_id.id for line in order.line_ids] self.pool['sdd.mandate'].write( - cr, uid, mandate_ids, { - 'last_debit_date': datetime.today().strftime('%Y-%m-%d') - }, + cr, uid, mandate_ids, + {'last_debit_date': datetime.today().strftime('%Y-%m-%d')}, context=context) - oneoff_mandate_ids = \ - [line.sdd_mandate_id.id for line in order.line_ids - if line.sdd_mandate_id.type == 'oneoff'] + to_expire_ids = [] + first_mandate_ids = [] + for line in order.line_ids: + if line.sdd_mandate_id.type == 'oneoff': + to_expire_ids.append(line.sdd_mandate_id.id) + elif line.sdd_mandate_id.type == 'recurrent': + seq_type = line.sdd_mandate_id.recurrent_sequence_type + if seq_type == 'final': + to_expire_ids.append(line.sdd_mandate_id.id) + elif seq_type == 'first': + first_mandate_ids.append(line.sdd_mandate_id.id) self.pool['sdd.mandate'].write( - cr, uid, oneoff_mandate_ids, {'state': 'expired'}, - context=context) + cr, uid, to_expire_ids, {'state': 'expired'}, context=context) + self.pool['sdd.mandate'].write( + cr, uid, first_mandate_ids, + {'recurrent_sequence_type': 'recurring'}, context=context) return {'type': 'ir.actions.act_window_close'}