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
+
+
+
+
+
+
+
+