[MIG] account_banking_sepa_direct_debit: Migration to 8.0

This commit is contained in:
Pedro M. Baeza
2014-09-10 12:19:20 +02:00
committed by Reyes4711
parent 4a16925d45
commit 50168f44c0
23 changed files with 538 additions and 808 deletions

View File

@@ -20,6 +20,5 @@
#
##############################################################################
from . import company
from . import models
from . import wizard
from . import account_banking_sdd

View File

@@ -22,51 +22,45 @@
{
'name': 'Account Banking SEPA Direct Debit',
'summary': 'Create SEPA files for Direct Debit',
'version': '0.1',
'version': '8.0.0.2.0',
'license': 'AGPL-3',
'author': 'Akretion',
'website': 'http://www.akretion.com',
'author': "Akretion, "
"Serv. Tecnol. Avanzados - Pedro M. Baeza, "
"Odoo Community Association (OCA)",
'website': 'https://github.com/OCA/bank-payment',
'category': 'Banking addons',
'depends': ['account_direct_debit', 'account_banking_pain_base'],
'depends': [
'account_direct_debit',
'account_banking_pain_base',
'account_banking_mandate',
],
'external_dependencies': {
'python': ['unidecode', 'lxml'],
},
'data': [
'security/original_mandate_required_security.xml',
'account_banking_sdd_view.xml',
'sdd_mandate_view.xml',
'res_partner_bank_view.xml',
'account_payment_view.xml',
'company_view.xml',
'mandate_expire_cron.xml',
'account_invoice_view.xml',
'views/account_banking_sdd_view.xml',
'views/account_banking_mandate_view.xml',
'views/res_company_view.xml',
'wizard/export_sdd_view.xml',
'data/mandate_expire_cron.xml',
'data/payment_type_sdd.xml',
'data/mandate_reference_sequence.xml',
'security/original_mandate_required_security.xml',
'security/ir.model.access.csv',
],
'demo': ['sepa_direct_debit_demo.xml'],
'demo': ['demo/sepa_direct_debit_demo.xml'],
'description': '''
Module to export direct debit payment orders in SEPA XML file format.
SEPA PAIN (PAyment INitiation) is the new european standard for
Customer-to-Bank payment instructions.
This module implements SEPA Direct Debit (SDD), more specifically PAIN
versions 008.001.02, 008.001.03 and 008.001.04.
It is part of the ISO 20022 standard, available on http://www.iso20022.org.
Customer-to-Bank payment instructions. This module implements SEPA Direct
Debit (SDD), more specifically PAIN versions 008.001.02, 008.001.03 and
008.001.04. It is part of the ISO 20022 standard, available on
http://www.iso20022.org.
The Implementation Guidelines for SEPA Direct Debit published by the European
Payments Council (http://http://www.europeanpaymentscouncil.eu) use PAIN
version 008.001.02. So if you don't know which version your bank supports,
you should try version 008.001.02 first.
This module uses the framework provided by the banking addons,
cf https://www.github.com/OCA/banking-addons
Please contact Alexis de Lattre from Akretion <alexis.delattre@akretion.com>
for any help or question about this module.
version 008.001.02. So if you don't know which version your bank supports, you
should try version 008.001.02 first.
''',
'active': False,
'installable': True,
}

View File

@@ -1,440 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# SEPA Direct Debit module for OpenERP
# Copyright (C) 2013 Akretion (http://www.akretion.com)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
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):
'''SEPA Direct Debit export'''
_name = 'banking.export.sdd'
_description = __doc__
_rec_name = 'filename'
def _generate_filename(self, cr, uid, ids, name, arg, context=None):
res = {}
for sepa_file in self.browse(cr, uid, ids, context=context):
ref = sepa_file.payment_order_ids[0].reference
if ref:
label = unidecode(ref.replace('/', '-'))
else:
label = 'error'
res[sepa_file.id] = 'sdd_%s.xml' % label
return res
_columns = {
'payment_order_ids': fields.many2many(
'payment.order',
'account_payment_order_sdd_rel',
'banking_export_sepa_id', 'account_order_id',
'Payment Orders',
readonly=True),
'nb_transactions': fields.integer(
'Number of Transactions', readonly=True),
'total_amount': fields.float(
'Total Amount', digits_compute=dp.get_precision('Account'),
readonly=True),
'batch_booking': fields.boolean(
'Batch Booking', readonly=True,
help="If true, the bank statement will display only one credit "
"line for all the direct debits of the SEPA file ; if false, "
"the bank statement will display one credit line per direct "
"debit of the SEPA file."),
'charge_bearer': fields.selection([
('SLEV', 'Following Service Level'),
('SHAR', 'Shared'),
('CRED', 'Borne by Creditor'),
('DEBT', 'Borne by Debtor'),
], 'Charge Bearer', readonly=True,
help="Following service level : transaction charges are to be "
"applied following the rules agreed in the service level and/or "
"scheme (SEPA Core messages must use this). Shared : "
"transaction charges on the creditor side are to be borne by "
"the creditor, transaction charges on the debtor side are to be "
"borne by the debtor. Borne by creditor : all transaction "
"charges are to be borne by the creditor. Borne by debtor : "
"all transaction charges are to be borne by the debtor."),
'create_date': fields.datetime('Generation Date', readonly=True),
'file': fields.binary('SEPA File', readonly=True),
'filename': fields.function(
_generate_filename, type='char', size=256,
string='Filename', readonly=True, store=True),
'state': fields.selection([
('draft', 'Draft'),
('sent', 'Sent'),
], 'State', readonly=True),
}
_defaults = {
'state': 'draft',
}
class sdd_mandate(orm.Model):
'''SEPA Direct Debit Mandate'''
_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',
'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',
}
}
_columns = {
'partner_bank_id': fields.many2one(
'res.partner.bank', 'Bank Account', track_visibility='onchange'),
'partner_id': fields.related(
'partner_bank_id', 'partner_id', type='many2one',
relation='res.partner', string='Partner', readonly=True),
'company_id': fields.many2one('res.company', 'Company', required=True),
'unique_mandate_reference': fields.char(
'Unique Mandate Reference', size=35, readonly=True,
track_visibility='always'),
'type': fields.selection([
('recurrent', 'Recurrent'),
('oneoff', 'One-Off'),
], 'Type of Mandate', required=True, track_visibility='always'),
'recurrent_sequence_type': fields.selection([
('first', 'First'),
('recurring', 'Recurring'),
('final', 'Final'),
], 'Sequence Type for Next Debit', track_visibility='onchange',
help="This field is only used for Recurrent mandates, not for "
"One-Off mandates."),
'signature_date': fields.date(
'Date of Signature of the Mandate', track_visibility='onchange'),
'scan': fields.binary('Scan of the Mandate'),
'last_debit_date': fields.date(
'Date of the Last Debit', readonly=True),
'state': fields.selection([
('draft', 'Draft'),
('valid', 'Valid'),
('expired', 'Expired'),
('cancel', 'Cancelled'),
], 'Status',
help="Only valid mandates can be used in a payment line. A "
"cancelled mandate is a mandate that has been cancelled by "
"the customer. A one-off mandate expires after its first use. "
"A recurrent mandate expires after it's final use or if it "
"hasn't been used for 36 months."),
'payment_line_ids': fields.one2many(
'payment.line', 'sdd_mandate_id', "Related Payment Lines"),
'sepa_migrated': fields.boolean(
'Migrated to SEPA', track_visibility='onchange',
help="If this field is not active, the mandate section of the "
"next direct debit file that include this mandate will contain "
"the 'Original Mandate Identification' and the 'Original "
"Creditor Scheme Identification'. This is required in a few "
"countries (Belgium for instance), but not in all countries. "
"If this is not required in your country, you should keep this "
"field always active."),
'original_mandate_identification': fields.char(
'Original Mandate Identification', size=35,
track_visibility='onchange',
help="When the field 'Migrated to SEPA' is not active, this "
"field will be used as the Original Mandate Identification in "
"the Direct Debit file."),
}
_defaults = {
'company_id': lambda self, cr, uid, context:
self.pool['res.company']._company_default_get(
cr, uid, 'sdd.mandate', context=context),
'unique_mandate_reference': '/',
'state': 'draft',
'sepa_migrated': True,
}
_sql_constraints = [(
'mandate_ref_company_uniq',
'unique(unique_mandate_reference, company_id)',
'A Mandate with the same reference already exists for this company !'
)]
def create(self, cr, uid, vals, context=None):
if vals.get('unique_mandate_reference', '/') == '/':
vals['unique_mandate_reference'] = \
self.pool['ir.sequence'].next_by_code(
cr, uid, 'sdd.mandate.reference', context=context)
return super(sdd_mandate, self).create(cr, uid, vals, context=context)
def _check_sdd_mandate(self, cr, uid, ids):
for mandate in self.browse(cr, uid, ids):
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 "
"attached 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)
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)
if (mandate.type == 'recurrent' and not mandate.sepa_migrated
and mandate.recurrent_sequence_type != 'first'):
raise orm.except_orm(
_('Error:'),
_("The recurrent mandate '%s' which is not marked as "
"'Migrated to SEPA' must have its recurrent sequence "
"type set to 'First'.")
% mandate.unique_mandate_reference)
if (mandate.type == 'recurrent' and not mandate.sepa_migrated
and not mandate.original_mandate_identification):
raise orm.except_orm(
_('Error:'),
_("You must set the 'Original Mandate Identification' "
"on the recurrent mandate '%s' which is not marked "
"as 'Migrated to SEPA'.")
% mandate.unique_mandate_reference)
return True
_constraints = [
(_check_sdd_mandate, "Error msg in raise", [
'last_debit_date', 'signature_date', 'state', 'partner_bank_id',
'type', 'recurrent_sequence_type', 'sepa_migrated',
'original_mandate_identification',
]),
]
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):
res = {'value': {}}
if partner_bank_id:
partner_bank_read = self.pool['res.partner.bank'].read(
cr, uid, partner_bank_id, ['partner_id'])['partner_id']
if partner_bank_read:
res['value']['partner_id'] = partner_bank_read[0]
if (state == 'valid' and partner_bank_id
and type == 'recurrent'
and recurrent_sequence_type != 'first'):
res['value']['recurrent_sequence_type'] = 'first'
res['warning'] = {
'title': _('Mandate update'),
'message': _(
"As you changed the bank account attached to this "
"mandate, the 'Sequence Type' has been set back to "
"'First'."),
}
return res
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 cancel(self, cr, uid, ids, context=None):
to_cancel_ids = []
for mandate in self.browse(cr, uid, ids, context=context):
assert mandate.state in ('draft', 'valid'),\
'Mandate should be in draft or valid state'
to_cancel_ids.append(mandate.id)
self.write(
cr, uid, to_cancel_ids, {'state': 'cancel'}, context=context)
return True
def back2draft(self, cr, uid, ids, context=None):
to_draft_ids = []
for mandate in self.browse(cr, uid, ids, context=context):
assert mandate.state == 'cancel',\
'Mandate should be in cancel state'
to_draft_ids.append(mandate.id)
self.write(
cr, uid, to_draft_ids, {'state': 'draft'}, 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):
_inherit = 'res.partner.bank'
_columns = {
'sdd_mandate_ids': fields.one2many(
'sdd.mandate', 'partner_bank_id', 'SEPA Direct Debit Mandates'),
}
class payment_line(orm.Model):
_inherit = 'payment.line'
_columns = {
'sdd_mandate_id': fields.many2one(
'sdd.mandate', 'SEPA Direct Debit Mandate',
domain=[('state', '=', 'valid')]),
}
def create(self, cr, uid, vals, context=None):
'''If the customer invoice has a mandate, take it
otherwise, take the first valid mandate of the bank account'''
if context is None:
context = {}
if not vals:
vals = {}
partner_bank_id = vals.get('bank_id')
move_line_id = vals.get('move_line_id')
if (context.get('default_payment_order_type') == 'debit'
and 'sdd_mandate_id' not in vals):
if move_line_id:
line = self.pool['account.move.line'].browse(
cr, uid, move_line_id, context=context)
if (line.invoice and line.invoice.type == 'out_invoice'
and line.invoice.sdd_mandate_id):
vals.update({
'sdd_mandate_id': line.invoice.sdd_mandate_id.id,
'bank_id':
line.invoice.sdd_mandate_id.partner_bank_id.id,
})
if 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)
def _check_mandate_bank_link(self, cr, uid, ids):
for payline in self.browse(cr, uid, ids):
if (payline.sdd_mandate_id and payline.bank_id
and payline.sdd_mandate_id.partner_bank_id.id !=
payline.bank_id.id):
raise orm.except_orm(
_('Error:'),
_("The payment line with reference '%s' has the bank "
"account '%s' which is not attached to the mandate "
"'%s' (this mandate is attached to the bank account "
"'%s').") % (
payline.name,
self.pool['res.partner.bank'].name_get(
cr, uid, [payline.bank_id.id])[0][1],
payline.sdd_mandate_id.unique_mandate_reference,
self.pool['res.partner.bank'].name_get(
cr, uid,
[payline.sdd_mandate_id.partner_bank_id.id])[0][1],
))
return True
_constraints = [
(_check_mandate_bank_link, 'Error msg in raise',
['sdd_mandate_id', 'bank_id']),
]
class account_invoice(orm.Model):
_inherit = 'account.invoice'
_columns = {
'sdd_mandate_id': fields.many2one(
'sdd.mandate', 'SEPA Direct Debit Mandate',
domain=[('state', '=', 'valid')], readonly=True,
states={'draft': [('readonly', False)]})
}

View File

@@ -1,22 +0,0 @@
<?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>
<record id="invoice_form" model="ir.ui.view">
<field name="name">add.sdd.mandate.on.customer.invoice.form</field>
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_form"/>
<field name="arch" type="xml">
<field name="partner_bank_id" position="after">
<field name="sdd_mandate_id" domain="[('partner_id', '=', partner_id), ('state', '=', 'valid')]" attrs="{'invisible': [('type', '=', 'out_refund')]}"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -1,26 +0,0 @@
<?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>
<record id="sdd_view_payment_order_form" model="ir.ui.view">
<field name="name">sdd.payment.order.form</field>
<field name="model">payment.order</field>
<field name="inherit_id" ref="account_payment.view_payment_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='line_ids']/form/notebook/page/group/field[@name='bank_id']" position="after">
<field name="sdd_mandate_id" domain="[('partner_bank_id', '=', bank_id), ('state', '=', 'valid')]" invisible="context.get('default_payment_order_type')!='debit'" context="{'default_partner_bank_id': bank_id}"/>
<newline />
</xpath>
<xpath expr="//field[@name='line_ids']/tree/field[@name='bank_id']" position="after">
<field name="sdd_mandate_id" string="SDD Mandate" invisible="context.get('default_payment_order_type')!='debit'"/>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,24 @@
<?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">
<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>
<field name="doall" eval="False"/>
<field name="model" eval="'account.banking.mandate'"/>
<field name="function" eval="'_sdd_mandate_set_state_to_expired'" />
<field name="args" eval="'()'"/>
</record>
</data>
</openerp>

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="sdd_mandate_seq_type" model="ir.sequence.type">
<field name="name">SDD Mandate Reference</field>
<field name="code">sdd.mandate.reference</field>
</record>
<record id="sdd_mandate_seq" model="ir.sequence">
<field name="name">SDD Mandate Reference</field>
<field name="code">sdd.mandate.reference</field>
<field name="prefix">RUM</field>
<field name="padding" eval="7"/>
<!-- remember that max size for the mandate ref is 35 -->
</record>
</data>
</openerp>

View File

@@ -15,7 +15,7 @@
<field name="sepa_creditor_identifier">FR78ZZZ424242</field>
</record>
<record id="res_partner_12_mandate" model="sdd.mandate">
<record id="res_partner_12_mandate" model="account.banking.mandate">
<field name="partner_bank_id" ref="account_banking_payment_export.res_partner_12_iban"/>
<field name="type">recurrent</field>
<field name="recurrent_sequence_type">first</field>

View File

@@ -1,26 +0,0 @@
<?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>

View File

@@ -0,0 +1,25 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# SEPA Direct Debit module for OpenERP
# Copyright (C) 2013 Akretion (http://www.akretion.com)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import banking_export_sdd
from . import res_company
from . import account_banking_mandate

View File

@@ -0,0 +1,145 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# SEPA Direct Debit module for OpenERP
# Copyright (C) 2013 Akretion (http://www.akretion.com)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, fields, api, exceptions, _
from datetime import datetime
from dateutil.relativedelta import relativedelta
import logging
NUMBER_OF_UNUSED_MONTHS_BEFORE_EXPIRY = 36
logger = logging.getLogger(__name__)
class AccountBankingMandate(models.Model):
"""SEPA Direct Debit Mandate"""
_inherit = 'account.banking.mandate'
_track = {
'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',
}
}
type = fields.Selection([('recurrent', 'Recurrent'),
('oneoff', 'One-Off')],
string='Type of Mandate', required=True,
track_visibility='always')
recurrent_sequence_type = fields.Selection(
[('first', 'First'),
('recurring', 'Recurring'),
('final', 'Final')],
string='Sequence Type for Next Debit', track_visibility='onchange',
help="This field is only used for Recurrent mandates, not for "
"One-Off mandates.", default="first")
sepa_migrated = fields.Boolean(
string='Migrated to SEPA', track_visibility='onchange',
help="If this field is not active, the mandate section of the next "
"direct debit file that include this mandate will contain the "
"'Original Mandate Identification' and the 'Original Creditor "
"Scheme Identification'. This is required in a few countries "
"(Belgium for instance), but not in all countries. If this is "
"not required in your country, you should keep this field always "
"active.", default=True)
original_mandate_identification = fields.Char(
string='Original Mandate Identification', track_visibility='onchange',
help="When the field 'Migrated to SEPA' is not active, this field "
"will be used as the Original Mandate Identification in the "
"Direct Debit file.")
scheme = fields.Selection([('CORE', 'Basic (CORE)'),
('B2B', 'Enterprise (B2B)')],
string='Scheme', required=True, default="CORE")
@api.one
@api.constrains('type', 'recurrent_sequence_type')
def _check_recurring_type(self):
if (self.type == 'recurrent'
and not self.recurrent_sequence_type):
raise exceptions.Warning(
_("The recurrent mandate '%s' must have a sequence type.")
% self.unique_mandate_reference)
@api.one
@api.constrains('type', 'recurrent_sequence_type', 'sepa_migrated')
def _check_migrated_to_sepa(self):
if (self.type == 'recurrent' and not self.sepa_migrated
and self.recurrent_sequence_type != 'first'):
raise exceptions.Warning(
_("The recurrent mandate '%s' which is not marked as "
"'Migrated to SEPA' must have its recurrent sequence type "
"set to 'First'.") % self.unique_mandate_reference)
@api.one
@api.constrains('type', 'original_mandate_identification', 'sepa_migrated')
def _check_original_mandate_identification(self):
if (self.type == 'recurrent' and not self.sepa_migrated
and not self.original_mandate_identification):
raise exceptions.Warning(
_("You must set the 'Original Mandate Identification' on the "
"recurrent mandate '%s' which is not marked as 'Migrated to "
"SEPA'.") % self.unique_mandate_reference)
@api.one
@api.onchange('partner_bank_id')
def mandate_partner_bank_change(self):
super(AccountBankingMandate, self).mandate_partner_bank_change()
res = {}
if (self.state == 'valid' and self.partner_bank_id
and self.type == 'recurrent'
and self.recurrent_sequence_type != 'first'):
self.recurrent_sequence_type = 'first'
res['warning'] = {
'title': _('Mandate update'),
'message': _("As you changed the bank account attached to "
"this mandate, the 'Sequence Type' has been set "
"back to 'First'."),
}
return res
@api.multi
def _sdd_mandate_set_state_to_expired(self):
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_mandates = self.search(
['|',
('last_debit_date', '=', False),
('last_debit_date', '<=', expire_limit_date_str),
('state', '=', 'valid'),
('signature_date', '<=', expire_limit_date_str)])
if expired_mandates:
expired_mandates.write({'state': 'expired'})
logger.info(
'The following SDD Mandate IDs has been set to expired: %s'
% expired_mandates.ids)
else:
logger.info('0 SDD Mandates must be set to Expired')
return True

View File

@@ -0,0 +1,82 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# SEPA Direct Debit module for OpenERP
# Copyright (C) 2013 Akretion (http://www.akretion.com)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, fields, api
from openerp.addons.decimal_precision import decimal_precision as dp
try:
from unidecode import unidecode
except ImportError:
unidecode = None
class BankingExportSdd(models.Model):
"""SEPA Direct Debit export"""
_name = 'banking.export.sdd'
_description = __doc__
_rec_name = 'filename'
@api.one
def _generate_filename(self):
filename = ''
if self.payment_order_ids:
ref = self.payment_order_ids[0].reference
label = unidecode(ref.replace('/', '-')) if ref else 'error'
filename = 'sdd_%s.xml' % label
self.filename = filename
payment_order_ids = fields.Many2many(
comodel_name='payment.order',
relation='account_payment_order_sdd_rel',
column1='banking_export_sepa_id', column2='account_order_id',
string='Payment Orders',
readonly=True)
nb_transactions = fields.Integer(
string='Number of Transactions', readonly=True)
total_amount = fields.Float(
string='Total Amount', digits_compute=dp.get_precision('Account'),
readonly=True)
batch_booking = fields.Boolean(
'Batch Booking', readonly=True,
help="If true, the bank statement will display only one credit line "
"for all the direct debits of the SEPA file ; if false, the bank "
"statement will display one credit line per direct debit of the "
"SEPA file.")
charge_bearer = fields.Selection(
[('SLEV', 'Following Service Level'),
('SHAR', 'Shared'),
('CRED', 'Borne by Creditor'),
('DEBT', 'Borne by Debtor')], 'Charge Bearer', readonly=True,
help="Following service level : transaction charges are to be applied "
"following the rules agreed in the service level and/or scheme "
"(SEPA Core messages must use this). Shared : transaction "
"charges on the creditor side are to be borne by the creditor, "
"transaction charges on the debtor side are to be borne by the "
"debtor. Borne by creditor : all transaction charges are to be "
"borne by the creditor. Borne by debtor : all transaction "
"charges are to be borne by the debtor.")
create_date = fields.Datetime(string='Generation Date', readonly=True)
file = fields.Binary(string='SEPA File', readonly=True)
filename = fields.Char(compute=_generate_filename, size=256,
string='Filename', readonly=True, store=True)
state = fields.Selection([('draft', 'Draft'), ('sent', 'Sent')],
string='State', readonly=True, default='draft')

View File

@@ -20,29 +20,27 @@
#
##############################################################################
from openerp.osv import orm, fields
from openerp import models, fields, api, exceptions, _
import logging
logger = logging.getLogger(__name__)
class res_company(orm.Model):
class ResCompany(models.Model):
_inherit = 'res.company'
_columns = {
'sepa_creditor_identifier': fields.char(
'SEPA Creditor Identifier', size=35,
help="Enter the Creditor Identifier that has been attributed "
"to your company to make SEPA Direct Debits. This identifier "
"is composed of :\n- your country ISO code (2 letters)\n- a "
"2-digits checkum\n- a 3-letters business code\n- a "
"country-specific identifier"),
'original_creditor_identifier': fields.char(
'Original Creditor Identifier', size=70),
}
sepa_creditor_identifier = fields.Char(
string='SEPA Creditor Identifier', size=35,
help="Enter the Creditor Identifier that has been attributed to your "
"company to make SEPA Direct Debits. This identifier is composed "
"of :\n- your country ISO code (2 letters)\n- a 2-digits "
"checkum\n- a 3-letters business code\n- a country-specific "
"identifier")
original_creditor_identifier = fields.Char(
string='Original Creditor Identifier', size=70)
def is_sepa_creditor_identifier_valid(
self, cr, uid, sepa_creditor_identifier, context=None):
self, sepa_creditor_identifier):
"""Check if SEPA Creditor Identifier is valid
@param sepa_creditor_identifier: SEPA Creditor Identifier as str
or unicode
@@ -51,12 +49,11 @@ class res_company(orm.Model):
if not isinstance(sepa_creditor_identifier, (str, unicode)):
return False
try:
sci_str = str(sepa_creditor_identifier)
sci = str(sepa_creditor_identifier).lower()
except:
logger.warning(
"SEPA Creditor ID should contain only ASCII caracters.")
return False
sci = sci_str.lower()
if len(sci) < 9:
return False
before_replacement = sci[7:] + sci[0:2] + '00'
@@ -70,21 +67,14 @@ class res_company(orm.Model):
after_replacement += char
logger.debug(
"SEPA ID check after_replacement = %s" % after_replacement)
if int(sci[2:4]) == (98 - (int(after_replacement) % 97)):
return True
else:
return False
return int(sci[2:4]) == (98 - (int(after_replacement) % 97))
def _check_sepa_creditor_identifier(self, cr, uid, ids):
for company in self.browse(cr, uid, ids):
if company.sepa_creditor_identifier:
if not self.is_sepa_creditor_identifier_valid(
cr, uid, company.sepa_creditor_identifier):
return False
return True
_constraints = [
(_check_sepa_creditor_identifier,
"Invalid SEPA Creditor Identifier.",
['sepa_creditor_identifier']),
]
@api.one
@api.constrains('sepa_creditor_identifier')
def _check_sepa_creditor_identifier(self):
if self.sepa_creditor_identifier:
if not self.is_sepa_creditor_identifier_valid(
self.sepa_creditor_identifier):
raise exceptions.Warning(
_('Error'),
_("Invalid SEPA Creditor Identifier."))

View File

@@ -1,48 +0,0 @@
<?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>
<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>
<field name="inherit_id" ref="base.view_partner_bank_form"/>
<field name="arch" type="xml">
<group name="bank" position="after">
<group name="sdd_mandates" string="SEPA Direct Debit Mandates" colspan="4">
<field name="sdd_mandate_ids" context="{'default_partner_bank_id': active_id, 'sdd_mandate_bank_partner_view': True}" nolabel="1"/>
</group>
</group>
</field>
</record>
<record id="sdd_mandate_partner_bank_tree" model="ir.ui.view">
<field name="name">sdd.mandate.res.partner.bank.tree</field>
<field name="model">res.partner.bank</field>
<field name="inherit_id" ref="base.view_partner_bank_tree"/>
<field name="arch" type="xml">
<field name="partner_id" position="after">
<field name="sdd_mandate_ids" string="SDD Mandates"/>
</field>
</field>
</record>
<!-- add number of mandates in this list of bank accounts
on the partner form -->
<record id="sdd_mandate_partner_form" model="ir.ui.view">
<field name="name">sdd.mandate.partner.form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="account.view_partner_property_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='bank_ids']/tree/field[@name='owner_name']" position="after">
<field name="sdd_mandate_ids" string="SDD Mandates"/>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@@ -1,152 +0,0 @@
<?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>
<record id="sdd_mandate_form" model="ir.ui.view">
<field name="name">sdd.mandate.form</field>
<field name="model">sdd.mandate</field>
<field name="arch" type="xml">
<form string="SEPA Direct Debit Mandate" version="7.0">
<header>
<button name="validate" type="object" string="Validate" states="draft" class="oe_highlight"/>
<button name="cancel" type="object" string="Cancel" states="draft,valid"/>
<button name="back2draft" type="object" string="Back to Draft"
states="cancel" groups="account.group_account_manager"
confirm="You should set a mandate back to draft only if you cancelled it by mistake. Do you want to continue ?"/>
<field name="state" widget="statusbar"/>
</header>
<sheet>
<div class="oe_title">
<h1>
<field name="unique_mandate_reference" class="oe_inline"/>
</h1>
</div>
<group name="main">
<field name="company_id" groups="base.group_multi_company"/>
<field name="partner_bank_id"
on_change="mandate_partner_bank_change(partner_bank_id, type, recurrent_sequence_type, last_debit_date, state)"
invisible="context.get('sdd_mandate_bank_partner_view')"
/>
<field name="partner_id" invisible="context.get('sdd_mandate_bank_partner_view')"/>
<field name="type" on_change="mandate_type_change(type)"/>
<field name="recurrent_sequence_type" attrs="{'invisible': [('type', '=', 'oneoff')], 'required': [('type', '=', 'recurrent')]}"/>
<field name="signature_date"/>
<field name="scan"/>
<field name="last_debit_date"/>
<field name="sepa_migrated" groups="account_banking_sepa_direct_debit.group_original_mandate_required"/>
<field name="original_mandate_identification" attrs="{'invisible': [('sepa_migrated', '=', True)], 'required': [('sepa_migrated', '=', False)]}" groups="account_banking_sepa_direct_debit.group_original_mandate_required"/>
</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>
<record id="sdd_mandate_tree" model="ir.ui.view">
<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=='draft';black:state in ('expired', 'cancel')">
<field name="company_id" groups="base.group_multi_company"/>
<field name="partner_id" invisible="context.get('sdd_mandate_bank_partner_view')"/>
<field name="unique_mandate_reference" string="Reference"/>
<field name="type" string="Type"/>
<field name="signature_date" string="Signature Date"/>
<field name="last_debit_date"/>
<field name="state"/>
</tree>
</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="cancel" string="Cancelled" domain="[('state', '=', 'cancel')]" />
<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 Mandates</field>
<field name="res_model">sdd.mandate</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a new SEPA Direct Debit Mandate.
</p><p>
A SEPA Direct Debit Mandate is a document signed by your customer that gives you the autorization to do one or several direct debits on his bank account.
</p>
</field>
</record>
<menuitem id="sdd_mandate_menu"
parent="account_payment.menu_main_payment"
action="sdd_mandate_action"
sequence="20"
/>
<!-- notifications 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_expired" 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="mandate_cancel" model="mail.message.subtype">
<field name="name">Mandate Cancelled</field>
<field name="res_model">sdd.mandate</field>
<field name="default" eval="False"/>
<field name="description">SEPA Direct Debit Mandate Cancelled</field>
</record>
<record id="recurrent_sequence_type_first" model="mail.message.subtype">
<field name="name">Sequence Type set to First</field>
<field name="res_model">sdd.mandate</field>
<field name="default" eval="False"/>
<field name="description">Sequence Type set to First</field>
</record>
<record id="recurrent_sequence_type_recurring" model="mail.message.subtype">
<field name="name">Sequence Type set to Recurring</field>
<field name="res_model">sdd.mandate</field>
<field name="default" eval="False"/>
<field name="description">Sequence Type set to Recurring</field>
</record>
<record id="recurrent_sequence_type_final" model="mail.message.subtype">
<field name="name">Sequence Type set to Final</field>
<field name="res_model">sdd.mandate</field>
<field name="default" eval="False"/>
<field name="description">Sequence Type set to Final</field>
</record>
</data>
</openerp>

View File

@@ -1,4 +1,2 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_banking_export_sdd","Full access on banking.export.sdd","model_banking_export_sdd","account_payment.group_account_payment",1,1,1,1
"access_sdd_mandate","Full access on sdd.mandate","model_sdd_mandate","account_payment.group_account_payment",1,1,1,1
"access_sdd_mandate_read","Read access on sdd.mandate","model_sdd_mandate","base.group_user",1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_banking_export_sdd Full access on banking.export.sdd model_banking_export_sdd account_payment.group_account_payment 1 1 1 1
access_sdd_mandate Full access on sdd.mandate model_sdd_mandate account_payment.group_account_payment 1 1 1 1
access_sdd_mandate_read Read access on sdd.mandate model_sdd_mandate base.group_user 1 0 0 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,117 @@
<?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
-->
<!--
Customize all mandate views (and actions) to fit SEPA mandate style
-->
<openerp>
<data>
<record id="sdd_mandate_form" model="ir.ui.view">
<field name="name">sdd.mandate.form</field>
<field name="model">account.banking.mandate</field>
<field name="inherit_id" ref="account_banking_mandate.view_mandate_form"/>
<field name="arch" type="xml">
<data>
<field name="partner_id" position="after">
<field name="type"/>
<field name="recurrent_sequence_type" attrs="{'invisible': [('type', '=', 'oneoff')], 'required': [('type', '=', 'recurrent')]}"/>
</field>
<field name="last_debit_date" position="after">
<field name="sepa_migrated" groups="account_banking_sepa_direct_debit.group_original_mandate_required"/>
<field name="original_mandate_identification" attrs="{'invisible': [('sepa_migrated', '=', True)], 'required': [('sepa_migrated', '=', False)]}" groups="account_banking_sepa_direct_debit.group_original_mandate_required"/>
</field>
</data>
</field>
</record>
<record id="sdd_mandate_tree" model="ir.ui.view">
<field name="name">sdd.mandate.tree</field>
<field name="model">account.banking.mandate</field>
<field name="inherit_id" ref="account_banking_mandate.view_mandate_tree"/>
<field name="arch" type="xml">
<field name="unique_mandate_reference" position="after">
<field name="type" string="Type"/>
</field>
</field>
</record>
<record id="sdd_mandate_search" model="ir.ui.view">
<field name="name">sdd.mandate.search</field>
<field name="model">account.banking.mandate</field>
<field name="inherit_id" ref="account_banking_mandate.view_mandate_search"/>
<field name="arch" type="xml">
<filter name="expired" position="after">
<filter name="oneoff" string="One-Off" domain="[('type', '=', 'oneoff')]" />
<filter name="recurrent" string="Recurrent" domain="[('type', '=', 'recurrent')]" />
</filter>
</field>
</record>
<record id="mandate_action" model="ir.actions.act_window">
<field name="name">SEPA Direct Debit Mandates</field>
<field name="res_model">account.banking.mandate</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a new SEPA Direct Debit Mandate.
</p><p>
A SEPA Direct Debit Mandate is a document signed by your customer that gives you the autorization to do one or several direct debits on his bank account.
</p>
</field>
</record>
<menuitem id="account_banking_mandate.mandate_menu"
parent="account_payment.menu_main_payment"
action="mandate_action"
sequence="20"
/>
<record id="sdd_mandate_partner_bank_tree" model="ir.ui.view">
<field name="name">sdd.mandate.res.partner.bank.tree</field>
<field name="model">res.partner.bank</field>
<field name="inherit_id" ref="account_banking_mandate.mandate_partner_bank_tree"/>
<field name="arch" type="xml">
<field name="mandate_ids" position="attributes">
<attribute name="string">SDD Mandates</attribute>
</field>
</field>
</record>
<record id="sdd_mandate_partner_form" model="ir.ui.view">
<field name="name">sdd.mandate.partner.form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="account_banking_mandate.mandate_partner_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='bank_ids']/tree/field[@name='mandate_ids']" position="attributes">
<attribute name="string">SDD Mandates</attribute>
</xpath>
</field>
</record>
<record id="recurrent_sequence_type_first" model="mail.message.subtype">
<field name="name">Sequence Type set to First</field>
<field name="res_model">account.banking.mandate</field>
<field name="default" eval="False"/>
<field name="description">Sequence Type set to First</field>
</record>
<record id="recurrent_sequence_type_recurring" model="mail.message.subtype">
<field name="name">Sequence Type set to Recurring</field>
<field name="res_model">account.banking.mandate</field>
<field name="default" eval="False"/>
<field name="description">Sequence Type set to Recurring</field>
</record>
<record id="recurrent_sequence_type_final" model="mail.message.subtype">
<field name="name">Sequence Type set to Final</field>
<field name="res_model">account.banking.mandate</field>
<field name="default" eval="False"/>
<field name="description">Sequence Type set to Final</field>
</record>
</data>
</openerp>

View File

@@ -23,7 +23,7 @@
from openerp.osv import orm, fields
from openerp.tools.translate import _
from openerp import netsvc
from openerp import workflow
from datetime import datetime
from lxml import etree
@@ -195,6 +195,7 @@ class banking_export_sdd_wizard(orm.TransientModel):
"line with partner '%s' and Invoice ref '%s'.")
% (line.partner_id.name,
line.ml_inv_ref.number))
scheme = line.sdd_mandate_id.scheme
if line.sdd_mandate_id.state != 'valid':
raise orm.except_orm(
_('Error:'),
@@ -225,8 +226,7 @@ class banking_export_sdd_wizard(orm.TransientModel):
line.sdd_mandate_id.recurrent_sequence_type
assert seq_type_label is not False
seq_type = seq_type_map[seq_type_label]
key = (requested_date, priority, seq_type)
key = (requested_date, priority, seq_type, scheme)
if key in lines_per_group:
lines_per_group[key].append(line)
else:
@@ -237,7 +237,7 @@ class banking_export_sdd_wizard(orm.TransientModel):
cr, uid, line.id,
{'date': requested_date}, context=context)
for (requested_date, priority, sequence_type), lines in \
for (requested_date, priority, sequence_type, scheme), lines in \
lines_per_group.items():
# B. Payment info
payment_info_2_0, nb_of_transactions_2_4, control_sum_2_5 = \
@@ -246,7 +246,7 @@ class banking_export_sdd_wizard(orm.TransientModel):
"sepa_export.payment_order_ids[0].reference + '-' + "
"sequence_type + '-' + requested_date.replace('-', '') "
"+ '-' + priority",
priority, 'CORE', sequence_type, requested_date, {
priority, scheme, sequence_type, requested_date, {
'sepa_export': sepa_export,
'sequence_type': sequence_type,
'priority': priority,
@@ -422,9 +422,8 @@ class banking_export_sdd_wizard(orm.TransientModel):
self.pool.get('banking.export.sdd').write(
cr, uid, sepa_export.file_id.id, {'state': 'sent'},
context=context)
wf_service = netsvc.LocalService('workflow')
for order in sepa_export.payment_order_ids:
wf_service.trg_validate(uid, 'payment.order', order.id, 'done', cr)
workflow.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,