mirror of
https://github.com/OCA/bank-payment.git
synced 2025-02-02 10:37:31 +02:00
More work on mandates : - constraints - display related payment lines - set mandates to expired after 36 months of inactivity (via a cron) - add tracking/chatter - add default draft state + validate button
[FIX] In Debit mode, don't use the partner_bank_id of the customer invoice ! In the wizard 'Select invoices to pay', add maturity date in the view of the account move lines
This commit is contained in:
committed by
Enric Tobella
parent
2144be6408
commit
d8245177e2
@@ -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',
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
|
||||
|
||||
<record id="action_account_banking_sdd" model="ir.actions.act_window">
|
||||
<field name="name">Generated SEPA Direct Debit XML files</field>
|
||||
<field name="name">Generated SEPA Direct Debit Files</field>
|
||||
<field name="res_model">banking.export.sdd</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
@@ -66,11 +66,11 @@
|
||||
<menuitem id="menu_account_banking_sdd"
|
||||
parent="account_payment.menu_main_payment"
|
||||
action="action_account_banking_sdd"
|
||||
sequence="15"
|
||||
sequence="20"
|
||||
/>
|
||||
|
||||
<act_window id="act_banking_export_sdd_payment_order"
|
||||
name="Generated SEPA Direct Debit files"
|
||||
name="Generated SEPA Direct Debit Files"
|
||||
domain="[('payment_order_ids', '=', active_id)]"
|
||||
res_model="banking.export.sdd"
|
||||
src_model="payment.order"
|
||||
@@ -84,25 +84,32 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="SEPA Direct Debit Mandate" version="7.0">
|
||||
<header>
|
||||
<button name="validate" type="object" string="Validate" states="draft"/>
|
||||
<field name="state" widget="statusbar"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
<label string="SEPA Direct Debit Mandate"/>
|
||||
<field name="unique_mandate_reference" class="oe_inline"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group name="main">
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="partner_id" invisible="not context.get('sdd_mandate_main_view', False)"/>
|
||||
<field name="partner_id" invisible="not context.get('sdd_mandate_main_view')"/>
|
||||
<field name="type"/>
|
||||
<field name="signature_date"/>
|
||||
<field name="scan"/>
|
||||
<field name="last_debit_date"/>
|
||||
<field name="partner_bank_id"/>
|
||||
<field name="partner_bank_id" invisible="not context.get('sdd_mandate_main_view')"/>
|
||||
</group>
|
||||
<group name="payment_lines" string="Related Payment Lines">
|
||||
<field name="payment_line_ids" nolabel="1"/>
|
||||
</group>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
@@ -111,9 +118,9 @@
|
||||
<field name="name">sdd.mandate.tree</field>
|
||||
<field name="model">sdd.mandate</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="SEPA Direct Debit Mandate" colors="blue:state=='expired'">
|
||||
<tree string="SEPA Direct Debit Mandate" colors="blue:state=='draft';black:state=='expired'">
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="partner_id" invisible="not context.get('sdd_mandate_main_view', False)"/>
|
||||
<field name="partner_id" invisible="not context.get('sdd_mandate_main_view')"/>
|
||||
<field name="unique_mandate_reference" string="Reference"/>
|
||||
<field name="type" string="Type"/>
|
||||
<field name="signature_date" string="Signature Date"/>
|
||||
@@ -123,8 +130,23 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="sdd_mandate_search" model="ir.ui.view">
|
||||
<field name="name">sdd.mandate.search</field>
|
||||
<field name="model">sdd.mandate</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search SEPA Direct Debit Mandates">
|
||||
<field name="partner_id"/>
|
||||
<filter name="draft" string="Draft" domain="[('state', '=', 'draft')]" />
|
||||
<filter name="valid" string="Valid" domain="[('state', '=', 'valid')]" />
|
||||
<filter name="expired" string="Expired" domain="[('state', '=', 'expired')]" />
|
||||
<filter name="oneoff" string="One-Off" domain="[('type', '=', 'oneoff')]" />
|
||||
<filter name="recurrent" string="Recurrent" domain="[('type', '=', 'recurrent')]" />
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="sdd_mandate_action" model="ir.actions.act_window">
|
||||
<field name="name">SEPA Direct Debit Mandate</field>
|
||||
<field name="name">SEPA Direct Debit Mandates</field>
|
||||
<field name="res_model">sdd.mandate</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
@@ -144,6 +166,23 @@
|
||||
sequence="20"
|
||||
/>
|
||||
|
||||
<!-- notification in the chatter -->
|
||||
<record id="mandate_valid" model="mail.message.subtype">
|
||||
<field name="name">Mandate Validated</field>
|
||||
<field name="res_model">sdd.mandate</field>
|
||||
<field name="default" eval="False"/>
|
||||
<field name="description">SEPA Direct Debit Mandate Validated</field>
|
||||
</record>
|
||||
|
||||
<record id="mandate_expire" model="mail.message.subtype">
|
||||
<field name="name">Mandate Expired</field>
|
||||
<field name="res_model">sdd.mandate</field>
|
||||
<field name="default" eval="False"/>
|
||||
<field name="description">SEPA Direct Debit Mandate has Expired</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
<record id="sdd_mandate_partner_bank_form" model="ir.ui.view">
|
||||
<field name="name">sdd.mandate.res.partner.bank.form</field>
|
||||
<field name="model">res.partner.bank</field>
|
||||
|
||||
26
account_banking_sepa_direct_debit/mandate_expire_cron.xml
Normal file
26
account_banking_sepa_direct_debit/mandate_expire_cron.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
Copyright (C) 2013 Akretion (http://www.akretion.com/)
|
||||
@author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
The licence is in the file __openerp__.py
|
||||
-->
|
||||
|
||||
<openerp>
|
||||
|
||||
<data noupdate="1"> <!-- noupdate = 1 for the 'active' field -->
|
||||
<record id="sdd_mandate_expire_cron" model="ir.cron">
|
||||
<field name="name">Set SEPA Direct Debit Mandates to Expired</field>
|
||||
<field name="active" eval="True"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">days</field>
|
||||
<field name="numbercall">-1</field> <!-- don't limit the number of calls -->
|
||||
<field name="doall" eval="False"/>
|
||||
<field name="model" eval="'sdd.mandate'"/>
|
||||
<field name="function" eval="'_sdd_mandate_set_state_to_expired'" />
|
||||
<field name="args" eval="'()'"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
Reference in New Issue
Block a user