mirror of
https://github.com/OCA/bank-payment.git
synced 2025-02-02 10:37:31 +02:00
Reduce pep8 warnings Use create_date instead of a dedicated datetime field The creation of the SEPA file now has a _prepare function Other improvements in the code.
This commit is contained in:
committed by
Enric Tobella
parent
137377a445
commit
e08ee1676e
@@ -34,7 +34,12 @@ class banking_export_sdd(orm.Model):
|
||||
def _generate_filename(self, cr, uid, ids, name, arg, context=None):
|
||||
res = {}
|
||||
for sepa_file in self.browse(cr, uid, ids, context=context):
|
||||
res[sepa_file.id] = 'sdd_%s.xml' % (sepa_file.payment_order_ids[0].reference and unidecode(sepa_file.payment_order_ids[0].reference.replace('/', '-')) or 'error')
|
||||
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 = {
|
||||
@@ -42,27 +47,27 @@ class banking_export_sdd(orm.Model):
|
||||
'payment.order',
|
||||
'account_payment_order_sdd_rel',
|
||||
'banking_export_sepa_id', 'account_order_id',
|
||||
'Payment orders',
|
||||
'Payment Orders',
|
||||
readonly=True),
|
||||
'requested_collec_date': fields.date(
|
||||
'Requested collection date', readonly=True),
|
||||
'Requested Collection Date', readonly=True),
|
||||
'nb_transactions': fields.integer(
|
||||
'Number of transactions', readonly=True),
|
||||
'Number of Transactions', readonly=True),
|
||||
'total_amount': fields.float(
|
||||
'Total amount', digits_compute=dp.get_precision('Account'),
|
||||
'Total Amount', digits_compute=dp.get_precision('Account'),
|
||||
readonly=True),
|
||||
'batch_booking': fields.boolean(
|
||||
'Batch booking', readonly=True,
|
||||
'Batch Booking', readonly=True,
|
||||
help="If true, the bank statement will display only one credit line for all the direct debits of the SEPA XML file ; if false, the bank statement will display one credit line per direct debit of the SEPA XML file."),
|
||||
'charge_bearer': fields.selection([
|
||||
('SHAR', 'Shared'),
|
||||
('CRED', 'Borne by creditor'),
|
||||
('DEBT', 'Borne by debtor'),
|
||||
('SLEV', 'Following service level'),
|
||||
], 'Charge bearer', readonly=True,
|
||||
('CRED', 'Borne by Creditor'),
|
||||
('DEBT', 'Borne by Debtor'),
|
||||
('SLEV', 'Following Service Level'),
|
||||
], 'Charge Bearer', readonly=True,
|
||||
help='Shared : transaction charges on the sender side are to be borne by the debtor, transaction charges on the receiver side are to be borne by the creditor (most transfers use this). 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. Following service level : transaction charges are to be applied following the rules agreed in the service level and/or scheme.'),
|
||||
'generation_date': fields.datetime('Generation date', readonly=True),
|
||||
'file': fields.binary('SEPA XML file', readonly=True),
|
||||
'create_date': fields.datetime('Generation Date', readonly=True),
|
||||
'file': fields.binary('SEPA XML File', readonly=True),
|
||||
'filename': fields.function(
|
||||
_generate_filename, type='char', size=256,
|
||||
string='Filename', readonly=True, store=True),
|
||||
@@ -74,7 +79,6 @@ class banking_export_sdd(orm.Model):
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'generation_date': fields.date.context_today,
|
||||
'state': 'draft',
|
||||
}
|
||||
|
||||
@@ -117,10 +121,9 @@ class sdd_mandate(orm.Model):
|
||||
)]
|
||||
|
||||
_defaults = {
|
||||
'company_id': lambda self, cr, uid, context:
|
||||
self.pool['res.users'].browse(cr, uid, uid, context=context).\
|
||||
company_id.id,
|
||||
'unique_mandate_reference': lambda self, cr, uid, context:
|
||||
'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',
|
||||
}
|
||||
@@ -150,10 +153,12 @@ class payment_line(orm.Model):
|
||||
_('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:
|
||||
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??"))
|
||||
_("The SEPA Direct Debit Mandate '%s' is not related???"))
|
||||
return True
|
||||
|
||||
# _constraints = [
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<field name="requested_collec_date" />
|
||||
<field name="batch_booking" />
|
||||
<field name="charge_bearer"/>
|
||||
<field name="generation_date" />
|
||||
<field name="create_date" />
|
||||
<newline />
|
||||
<field name="file" filename="filename"/>
|
||||
<field name="filename" invisible="True"/>
|
||||
@@ -48,7 +48,7 @@
|
||||
<tree string="SEPA Direct Debit">
|
||||
<field name="filename"/>
|
||||
<field name="requested_collec_date"/>
|
||||
<field name="generation_date"/>
|
||||
<field name="create_date"/>
|
||||
<field name="nb_transactions"/>
|
||||
</tree>
|
||||
</field>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
<!-- TODO In the suitable_bank_types field, we should add l10n_fr_rib via a small stupid module -->
|
||||
<record id="export_sdd_008_001_02" model="payment.mode.type">
|
||||
<field name="name">SEPA Direct Debit v02</field>
|
||||
<field name="name">SEPA Direct Debit v02 (recommended)</field>
|
||||
<field name="code">pain.008.001.02</field>
|
||||
<field name="suitable_bank_types"
|
||||
eval="[(6,0,[ref('base_iban.bank_iban')])]" />
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
from openerp.tools.translate import _
|
||||
from openerp.tools.safe_eval import safe_eval
|
||||
from openerp import tools, netsvc
|
||||
import base64
|
||||
from datetime import datetime, timedelta
|
||||
@@ -35,33 +36,41 @@ _logger = logging.getLogger(__name__)
|
||||
|
||||
class banking_export_sdd_wizard(orm.TransientModel):
|
||||
_name = 'banking.export.sdd.wizard'
|
||||
_description = 'Export SEPA Direct Debit XML file'
|
||||
_description = 'Export SEPA Direct Debit File'
|
||||
_columns = {
|
||||
'state': fields.selection([('create', 'Create'), ('finish', 'Finish')],
|
||||
'State', readonly=True),
|
||||
'batch_booking': fields.boolean('Batch booking',
|
||||
'state': fields.selection([
|
||||
('create', 'Create'),
|
||||
('finish', 'Finish'),
|
||||
], 'State', readonly=True),
|
||||
'batch_booking': fields.boolean(
|
||||
'Batch Booking',
|
||||
help="If true, the bank statement will display only one debit line for all the wire transfers of the SEPA XML file ; if false, the bank statement will display one debit line per wire transfer of the SEPA XML file."),
|
||||
'requested_collec_date': fields.date('Requested collection date',
|
||||
'requested_collec_date': fields.date(
|
||||
'Requested Collection Date',
|
||||
help='This is the date on which the collection should be made by the bank. Please keep in mind that banks only execute on working days.'),
|
||||
'charge_bearer': fields.selection([
|
||||
('SHAR', 'Shared'),
|
||||
('CRED', 'Borne by creditor'),
|
||||
('DEBT', 'Borne by debtor'),
|
||||
('SLEV', 'Following service level'),
|
||||
], 'Charge bearer', required=True,
|
||||
('CRED', 'Borne by Creditor'),
|
||||
('DEBT', 'Borne by Debtor'),
|
||||
('SLEV', 'Following Service Level'),
|
||||
], 'Charge Bearer', required=True,
|
||||
help='Shared : transaction charges on the sender side are to be borne by the debtor, transaction charges on the receiver side are to be borne by the creditor (most transfers use this). 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. Following service level : transaction charges are to be applied following the rules agreed in the service level and/or scheme.'),
|
||||
'nb_transactions': fields.related('file_id', 'nb_transactions',
|
||||
type='integer', string='Number of transactions', readonly=True),
|
||||
'total_amount': fields.related('file_id', 'total_amount', type='float',
|
||||
string='Total amount', readonly=True),
|
||||
'file_id': fields.many2one('banking.export.sdd', 'SDD XML file', readonly=True),
|
||||
'file': fields.related('file_id', 'file', string="File", type='binary',
|
||||
'nb_transactions': fields.related(
|
||||
'file_id', 'nb_transactions', type='integer',
|
||||
string='Number of Transactions', readonly=True),
|
||||
'total_amount': fields.related(
|
||||
'file_id', 'total_amount', type='float', string='Total Amount',
|
||||
readonly=True),
|
||||
'filename': fields.related('file_id', 'filename', string="Filename",
|
||||
type='char', size=256, readonly=True),
|
||||
'payment_order_ids': fields.many2many('payment.order',
|
||||
'wiz_sdd_payorders_rel', 'wizard_id', 'payment_order_id',
|
||||
'Payment orders', readonly=True),
|
||||
'file_id': fields.many2one(
|
||||
'banking.export.sdd', 'SDD XML File', readonly=True),
|
||||
'file': fields.related(
|
||||
'file_id', 'file', string="File", type='binary', readonly=True),
|
||||
'filename': fields.related(
|
||||
'file_id', 'filename', string="Filename", type='char', size=256,
|
||||
readonly=True),
|
||||
'payment_order_ids': fields.many2many(
|
||||
'payment.order', 'wiz_sdd_payorders_rel', 'wizard_id',
|
||||
'payment_order_id', 'Payment Orders', readonly=True),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
@@ -76,37 +85,94 @@ class banking_export_sdd_wizard(orm.TransientModel):
|
||||
if partner_bank_obj.is_iban_valid(cr, uid, iban, context=context):
|
||||
return iban.replace(' ', '')
|
||||
else:
|
||||
raise orm.except_orm(_('Error:'), _("This IBAN is not valid : %s") % iban)
|
||||
raise orm.except_orm(
|
||||
_('Error:'), _("This IBAN is not valid : %s") % iban)
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
payment_order_ids = context.get('active_ids', [])
|
||||
vals.update({
|
||||
'payment_order_ids': [[6, 0, payment_order_ids]],
|
||||
})
|
||||
return super(banking_export_sdd_wizard, self).create(cr, uid,
|
||||
vals, context=context)
|
||||
return super(banking_export_sdd_wizard, self).create(
|
||||
cr, uid, vals, context=context)
|
||||
|
||||
def _prepare_field(self, cr, uid, field_name, field_value, max_size=0, sepa_export=False, line=False, sequence_type=False, context=None):
|
||||
def _prepare_field(
|
||||
self, cr, uid, field_name, field_value, max_size=0,
|
||||
sepa_export=False, line=False, sequence_type=False, context=None):
|
||||
'''This function is designed to be inherited !'''
|
||||
eval_ctx = {
|
||||
'sepa_export': sepa_export,
|
||||
'line': line,
|
||||
'sequence_type': sequence_type,
|
||||
}
|
||||
try:
|
||||
# SEPA uses XML, and XML = UTF-8 with support for all characters...
|
||||
# But we are dealing with banks
|
||||
# SEPA uses XML ; XML = UTF-8 ; UTF-8 = support for all characters
|
||||
# But we are dealing with banks...
|
||||
# and many banks don't want non-ASCCI characters !
|
||||
# cf section 1.4 "Character set" of the SEPA Core Direct Debit
|
||||
# Customer-to-bank implementation guidelines version 6.0
|
||||
value = unidecode(eval(field_value))
|
||||
# Customer-to-bank implementation guidelines
|
||||
value = unidecode(safe_eval(field_value, eval_ctx))
|
||||
except:
|
||||
if line:
|
||||
raise orm.except_orm(_('Error:'), _("Cannot compute the '%s' of the Payment Line with Invoice Reference '%s'.") % (field_name, self.pool['account.invoice'].name_get(cr, uid, [line.ml_inv_ref.id], context=context)[0][1]))
|
||||
raise orm.except_orm(
|
||||
_('Error:'),
|
||||
_("Cannot compute the '%s' of the Payment Line with Invoice Reference '%s'.")
|
||||
% (field_name, self.pool['account.invoice'].name_get(
|
||||
cr, uid, [line.ml_inv_ref.id], context=context)[0][1]))
|
||||
else:
|
||||
raise orm.except_orm(_('Error:'), _("Cannot compute the '%s'.") % field_name)
|
||||
raise orm.except_orm(
|
||||
_('Error:'),
|
||||
_("Cannot compute the '%s'.") % field_name)
|
||||
if not isinstance(value, (str, unicode)):
|
||||
raise orm.except_orm(_('Field type error:'), _("The type of the field '%s' is %s. It should be a string or unicode.") % (field_name, type(value)))
|
||||
raise orm.except_orm(
|
||||
_('Field type error:'),
|
||||
_("The type of the field '%s' is %s. It should be a string or unicode.")
|
||||
% (field_name, type(value)))
|
||||
if not value:
|
||||
raise orm.except_orm(_('Error:'), _("The '%s' is empty or 0. It should have a non-null value.") % field_name)
|
||||
raise orm.except_orm(
|
||||
_('Error:'),
|
||||
_("The '%s' is empty or 0. It should have a non-null value.")
|
||||
% field_name)
|
||||
if max_size and len(value) > max_size:
|
||||
value = value[0:max_size]
|
||||
return value
|
||||
|
||||
def _prepare_export_sepa(
|
||||
self, cr, uid, sepa_export, total_amount, transactions_count,
|
||||
xml_string, context=None):
|
||||
return {
|
||||
'batch_booking': sepa_export.batch_booking,
|
||||
'charge_bearer': sepa_export.charge_bearer,
|
||||
'requested_collec_date': sepa_export.requested_collec_date,
|
||||
'total_amount': total_amount,
|
||||
'nb_transactions': transactions_count,
|
||||
'file': base64.encodestring(xml_string),
|
||||
'payment_order_ids': [
|
||||
(6, 0, [x.id for x in sepa_export.payment_order_ids])
|
||||
],
|
||||
}
|
||||
|
||||
def _validate_xml(self, cr, uid, xml_string, pain_flavor):
|
||||
xsd_etree_obj = etree.parse(
|
||||
tools.file_open(
|
||||
'account_banking_sepa_direct_debit/data/%s.xsd'
|
||||
% pain_flavor))
|
||||
official_pain_schema = etree.XMLSchema(xsd_etree_obj)
|
||||
|
||||
try:
|
||||
root_to_validate = etree.fromstring(xml_string)
|
||||
official_pain_schema.assertValid(root_to_validate)
|
||||
except Exception, e:
|
||||
_logger.warning(
|
||||
"The XML file is invalid against the XML Schema Definition")
|
||||
_logger.warning(xml_string)
|
||||
_logger.warning(e)
|
||||
raise orm.except_orm(
|
||||
_('Error:'),
|
||||
_('The generated XML file is not valid against the official XML Schema Definition. The generated XML file and the full error have been written in the server logs. Here is the error, which may give you an idea on the cause of the problem : %s')
|
||||
% str(e))
|
||||
return True
|
||||
|
||||
def create_sepa(self, cr, uid, ids, context=None):
|
||||
'''
|
||||
Creates the SEPA Direct Debit file. That's the important code !
|
||||
@@ -131,7 +197,8 @@ class banking_export_sdd_wizard(orm.TransientModel):
|
||||
if sepa_export.requested_collec_date:
|
||||
my_requested_collec_date = sepa_export.requested_collec_date
|
||||
else:
|
||||
my_requested_collec_date = datetime.strftime(datetime.today() + timedelta(days=1), '%Y-%m-%d')
|
||||
my_requested_collec_date = datetime.strftime(
|
||||
datetime.today() + timedelta(days=1), '%Y-%m-%d')
|
||||
|
||||
pain_ns = {
|
||||
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
||||
@@ -141,19 +208,22 @@ class banking_export_sdd_wizard(orm.TransientModel):
|
||||
root = etree.Element('Document', nsmap=pain_ns)
|
||||
pain_root = etree.SubElement(root, root_xml_tag)
|
||||
|
||||
my_company_name = self._prepare_field(cr, uid, 'Company Name',
|
||||
'sepa_export.payment_order_ids[0].company_id.partner_id.name',
|
||||
name_maxsize, sepa_export=sepa_export, context=context)
|
||||
my_company_name = self._prepare_field(
|
||||
cr, uid, 'Company Name',
|
||||
'sepa_export.payment_order_ids[0].company_id.partner_id.name',
|
||||
name_maxsize, sepa_export=sepa_export, context=context)
|
||||
|
||||
# A. Group header
|
||||
group_header_1_0 = etree.SubElement(pain_root, 'GrpHdr')
|
||||
message_identification_1_1 = etree.SubElement(group_header_1_0, 'MsgId')
|
||||
message_identification_1_1.text = self._prepare_field(cr, uid,
|
||||
'Message Identification',
|
||||
message_identification_1_1 = etree.SubElement(
|
||||
group_header_1_0, 'MsgId')
|
||||
message_identification_1_1.text = self._prepare_field(
|
||||
cr, uid, 'Message Identification',
|
||||
'sepa_export.payment_order_ids[0].reference', 35,
|
||||
sepa_export=sepa_export, context=context)
|
||||
creation_date_time_1_2 = etree.SubElement(group_header_1_0, 'CreDtTm')
|
||||
creation_date_time_1_2.text = datetime.strftime(datetime.today(), '%Y-%m-%dT%H:%M:%S')
|
||||
creation_date_time_1_2.text = datetime.strftime(
|
||||
datetime.today(), '%Y-%m-%dT%H:%M:%S')
|
||||
nb_of_transactions_1_6 = etree.SubElement(group_header_1_0, 'NbOfTxs')
|
||||
control_sum_1_7 = etree.SubElement(group_header_1_0, 'CtrlSum')
|
||||
initiating_party_1_8 = etree.SubElement(group_header_1_0, 'InitgPty')
|
||||
@@ -176,7 +246,7 @@ class banking_export_sdd_wizard(orm.TransientModel):
|
||||
_('Error:'),
|
||||
_("Missing SEPA Direct Debit mandate on the payment line with partner '%s' and Invoice ref '%s'.")
|
||||
% (line.partner_id.name,
|
||||
line.ml_inv_ref.number))
|
||||
line.ml_inv_ref.number))
|
||||
if line.sdd_mandate_id.state != 'valid':
|
||||
raise orm.except_orm(
|
||||
_('Error:'),
|
||||
@@ -190,7 +260,8 @@ class banking_export_sdd_wizard(orm.TransientModel):
|
||||
_("Missing signature date on SEPA Direct Debit mandate with reference '%s' for partner '%s'.")
|
||||
% (line.sdd_mandate_id.unique_mandate_reference,
|
||||
line.sdd_mandate_id.partner_id.name))
|
||||
elif line.sdd_mandate_id.signature_date > datetime.today().strftime('%Y-%m-%d'):
|
||||
elif (line.sdd_mandate_id.signature_date >
|
||||
datetime.today().strftime('%Y-%m-%d')):
|
||||
raise orm.except_orm(
|
||||
_('Error:'),
|
||||
_("The signature date on SEPA Direct Debit mandate with reference '%s' for partner '%s' is '%s', which is in the future !")
|
||||
@@ -206,11 +277,11 @@ class banking_export_sdd_wizard(orm.TransientModel):
|
||||
first_recur_lines['OOFF'] = [line]
|
||||
else:
|
||||
raise orm.except_orm(
|
||||
_('Error:'),
|
||||
_("The mandate with reference '%s' for partner '%s' has type set to 'One-Off' and it has a last debit date set to '%s', so we can't use it.")
|
||||
% (line.sdd_mandate_id.unique_mandate_reference,
|
||||
line.sdd_mandate_id.partner_id.name,
|
||||
line.sdd_mandate_id.last_debit_date))
|
||||
_('Error:'),
|
||||
_("The mandate with reference '%s' for partner '%s' has type set to 'One-Off' and it has a last debit date set to '%s', so we can't use it.")
|
||||
% (line.sdd_mandate_id.unique_mandate_reference,
|
||||
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'):
|
||||
@@ -226,7 +297,8 @@ class banking_export_sdd_wizard(orm.TransientModel):
|
||||
for sequence_type, lines in first_recur_lines.items():
|
||||
# B. Payment info
|
||||
payment_info_2_0 = etree.SubElement(pain_root, 'PmtInf')
|
||||
payment_info_identification_2_1 = etree.SubElement(payment_info_2_0, 'PmtInfId')
|
||||
payment_info_identification_2_1 = etree.SubElement(
|
||||
payment_info_2_0, 'PmtInfId')
|
||||
payment_info_identification_2_1.text = self._prepare_field(
|
||||
cr, uid, 'Payment Information Identification',
|
||||
"sequence_type + '-' + sepa_export.payment_order_ids[0].reference",
|
||||
@@ -241,57 +313,73 @@ class banking_export_sdd_wizard(orm.TransientModel):
|
||||
# Implementation guidelines" v6.0 says that control sum
|
||||
# and nb_of_transactions should be present
|
||||
# at both "group header" level and "payment info" level
|
||||
nb_of_transactions_2_4 = etree.SubElement(payment_info_2_0, 'NbOfTxs')
|
||||
nb_of_transactions_2_4 = etree.SubElement(
|
||||
payment_info_2_0, 'NbOfTxs')
|
||||
control_sum_2_5 = etree.SubElement(payment_info_2_0, 'CtrlSum')
|
||||
payment_type_info_2_6 = etree.SubElement(payment_info_2_0, 'PmtTpInf')
|
||||
service_level_2_8 = etree.SubElement(payment_type_info_2_6, 'SvcLvl')
|
||||
payment_type_info_2_6 = etree.SubElement(
|
||||
payment_info_2_0, 'PmtTpInf')
|
||||
service_level_2_8 = etree.SubElement(
|
||||
payment_type_info_2_6, 'SvcLvl')
|
||||
service_level_code_2_9 = etree.SubElement(service_level_2_8, 'Cd')
|
||||
service_level_code_2_9.text = 'SEPA'
|
||||
local_instrument_2_11 = etree.SubElement(payment_type_info_2_6, 'LclInstrm')
|
||||
local_instr_code_2_12 = etree.SubElement(local_instrument_2_11, 'Cd')
|
||||
local_instrument_2_11 = etree.SubElement(
|
||||
payment_type_info_2_6, 'LclInstrm')
|
||||
local_instr_code_2_12 = etree.SubElement(
|
||||
local_instrument_2_11, 'Cd')
|
||||
local_instr_code_2_12.text = 'CORE'
|
||||
# 2.14 Sequence Type MANDATORY
|
||||
# this message element must indicate ‘FRST
|
||||
# 'FRST' = First ; 'OOFF' = One Off ; 'RCUR' : Recurring
|
||||
# 'FNAL' = Final
|
||||
sequence_type_2_14 = etree.SubElement(payment_type_info_2_6, 'SeqTp')
|
||||
sequence_type_2_14 = etree.SubElement(
|
||||
payment_type_info_2_6, 'SeqTp')
|
||||
sequence_type_2_14.text = sequence_type
|
||||
|
||||
requested_collec_date_2_18 = etree.SubElement(payment_info_2_0, 'ReqdColltnDt')
|
||||
requested_collec_date_2_18 = etree.SubElement(
|
||||
payment_info_2_0, 'ReqdColltnDt')
|
||||
requested_collec_date_2_18.text = my_requested_collec_date
|
||||
creditor_2_19 = etree.SubElement(payment_info_2_0, 'Cdtr')
|
||||
creditor_name = etree.SubElement(creditor_2_19, 'Nm')
|
||||
creditor_name.text = my_company_name
|
||||
creditor_account_2_20 = etree.SubElement(payment_info_2_0, 'CdtrAcct')
|
||||
creditor_account_2_20 = etree.SubElement(
|
||||
payment_info_2_0, 'CdtrAcct')
|
||||
creditor_account_id = etree.SubElement(creditor_account_2_20, 'Id')
|
||||
creditor_account_iban = etree.SubElement(creditor_account_id, 'IBAN')
|
||||
creditor_account_iban.text = self._validate_iban(cr, uid,
|
||||
self._prepare_field(cr, uid, 'Company IBAN',
|
||||
creditor_account_iban = etree.SubElement(
|
||||
creditor_account_id, 'IBAN')
|
||||
creditor_account_iban.text = self._validate_iban(
|
||||
cr, uid, self._prepare_field(
|
||||
cr, uid, 'Company IBAN',
|
||||
'sepa_export.payment_order_ids[0].mode.bank_id.acc_number',
|
||||
sepa_export=sepa_export, context=context),
|
||||
context=context)
|
||||
|
||||
creditor_agent_2_21 = etree.SubElement(payment_info_2_0, 'CdtrAgt')
|
||||
creditor_agent_institution = etree.SubElement(creditor_agent_2_21, 'FinInstnId')
|
||||
creditor_agent_bic = etree.SubElement(creditor_agent_institution, bic_xml_tag)
|
||||
creditor_agent_bic.text = self._prepare_field(cr, uid, 'Company BIC',
|
||||
creditor_agent_institution = etree.SubElement(
|
||||
creditor_agent_2_21, 'FinInstnId')
|
||||
creditor_agent_bic = etree.SubElement(
|
||||
creditor_agent_institution, bic_xml_tag)
|
||||
creditor_agent_bic.text = self._prepare_field(
|
||||
cr, uid, 'Company BIC',
|
||||
'sepa_export.payment_order_ids[0].mode.bank_id.bank.bic',
|
||||
sepa_export=sepa_export, context=context)
|
||||
|
||||
charge_bearer_2_24 = etree.SubElement(payment_info_2_0, 'ChrgBr')
|
||||
charge_bearer_2_24.text = sepa_export.charge_bearer
|
||||
|
||||
creditor_scheme_identification_2_27 = etree.SubElement(payment_info_2_0, 'CdtrSchmeId')
|
||||
csi_id = etree.SubElement(creditor_scheme_identification_2_27, 'Id')
|
||||
creditor_scheme_identification_2_27 = etree.SubElement(
|
||||
payment_info_2_0, 'CdtrSchmeId')
|
||||
csi_id = etree.SubElement(
|
||||
creditor_scheme_identification_2_27, 'Id')
|
||||
csi_privateid = csi_id = etree.SubElement(csi_id, 'PrvtId')
|
||||
csi_other = etree.SubElement(csi_privateid, 'Othr')
|
||||
csi_other_id = etree.SubElement(csi_other, 'Id')
|
||||
csi_other_id.text = self._prepare_field(cr, uid,
|
||||
'SEPA Creditor Identifier',
|
||||
csi_other_id.text = self._prepare_field(
|
||||
cr, uid, 'SEPA Creditor Identifier',
|
||||
'sepa_export.payment_order_ids[0].company_id.sepa_creditor_identifier',
|
||||
sepa_export=sepa_export, context=context)
|
||||
csi_scheme_name = etree.SubElement(csi_other, 'SchmeNm')
|
||||
csi_scheme_name_proprietary = etree.SubElement(csi_scheme_name, 'Prtry')
|
||||
csi_scheme_name_proprietary = etree.SubElement(
|
||||
csi_scheme_name, 'Prtry')
|
||||
csi_scheme_name_proprietary.text = 'SEPA'
|
||||
|
||||
transactions_count_2_4 = 0
|
||||
@@ -299,22 +387,29 @@ class banking_export_sdd_wizard(orm.TransientModel):
|
||||
for line in lines:
|
||||
transactions_count_2_4 += 1
|
||||
# C. Direct Debit Transaction Info
|
||||
dd_transaction_info_2_28 = etree.SubElement(payment_info_2_0, 'DrctDbtTxInf')
|
||||
payment_identification_2_29 = etree.SubElement(dd_transaction_info_2_28, 'PmtId')
|
||||
# Instruction identification (2.30) is not mandatory, so we don't use it
|
||||
end2end_identification_2_31 = etree.SubElement(payment_identification_2_29, 'EndToEndId')
|
||||
end2end_identification_2_31.text = self._prepare_field(cr, uid,
|
||||
'End to End Identification', 'line.name', 35,
|
||||
dd_transaction_info_2_28 = etree.SubElement(
|
||||
payment_info_2_0, 'DrctDbtTxInf')
|
||||
payment_identification_2_29 = etree.SubElement(
|
||||
dd_transaction_info_2_28, 'PmtId')
|
||||
end2end_identification_2_31 = etree.SubElement(
|
||||
payment_identification_2_29, 'EndToEndId')
|
||||
end2end_identification_2_31.text = self._prepare_field(
|
||||
cr, uid, 'End to End Identification', 'line.name', 35,
|
||||
line=line, context=context)
|
||||
currency_name = self._prepare_field(cr, uid, 'Currency Code',
|
||||
'line.currency.name', 3, line=line, context=context)
|
||||
instructed_amount_2_44 = etree.SubElement(dd_transaction_info_2_28, 'InstdAmt', Ccy=currency_name)
|
||||
currency_name = self._prepare_field(
|
||||
cr, uid, 'Currency Code', 'line.currency.name', 3,
|
||||
line=line, context=context)
|
||||
instructed_amount_2_44 = etree.SubElement(
|
||||
dd_transaction_info_2_28, 'InstdAmt', Ccy=currency_name)
|
||||
instructed_amount_2_44.text = '%.2f' % line.amount_currency
|
||||
amount_control_sum_1_7 += line.amount_currency
|
||||
amount_control_sum_2_5 += line.amount_currency
|
||||
dd_transaction_2_46 = etree.SubElement(dd_transaction_info_2_28, 'DrctDbtTx')
|
||||
mandate_related_info_2_47 = etree.SubElement(dd_transaction_2_46, 'MndtRltdInf')
|
||||
mandate_identification_2_48 = etree.SubElement(mandate_related_info_2_47, 'MndtId')
|
||||
dd_transaction_2_46 = etree.SubElement(
|
||||
dd_transaction_info_2_28, 'DrctDbtTx')
|
||||
mandate_related_info_2_47 = etree.SubElement(
|
||||
dd_transaction_2_46, 'MndtRltdInf')
|
||||
mandate_identification_2_48 = etree.SubElement(
|
||||
mandate_related_info_2_47, 'MndtId')
|
||||
mandate_identification_2_48.text = self._prepare_field(
|
||||
cr, uid, 'Unique Mandate Reference',
|
||||
'line.sdd_mandate_id.unique_mandate_reference',
|
||||
@@ -327,81 +422,67 @@ class banking_export_sdd_wizard(orm.TransientModel):
|
||||
line=line, context=context)
|
||||
|
||||
# TODO look at 2.50 "Amendment Indicator
|
||||
debtor_agent_2_70 = etree.SubElement(dd_transaction_info_2_28, 'DbtrAgt')
|
||||
debtor_agent_institution = etree.SubElement(debtor_agent_2_70, 'FinInstnId')
|
||||
debtor_agent_bic = etree.SubElement(debtor_agent_institution, bic_xml_tag)
|
||||
debtor_agent_bic.text = self._prepare_field(cr, uid,
|
||||
'Customer BIC', 'line.bank_id.bank.bic',
|
||||
debtor_agent_2_70 = etree.SubElement(
|
||||
dd_transaction_info_2_28, 'DbtrAgt')
|
||||
debtor_agent_institution = etree.SubElement(
|
||||
debtor_agent_2_70, 'FinInstnId')
|
||||
debtor_agent_bic = etree.SubElement(
|
||||
debtor_agent_institution, bic_xml_tag)
|
||||
debtor_agent_bic.text = self._prepare_field(
|
||||
cr, uid, 'Customer BIC', 'line.bank_id.bank.bic',
|
||||
line=line, context=context)
|
||||
debtor_2_72 = etree.SubElement(dd_transaction_info_2_28, 'Dbtr')
|
||||
debtor_2_72 = etree.SubElement(
|
||||
dd_transaction_info_2_28, 'Dbtr')
|
||||
debtor_name = etree.SubElement(debtor_2_72, 'Nm')
|
||||
debtor_name.text = self._prepare_field(cr, uid,
|
||||
'Customer Name', 'line.partner_id.name',
|
||||
debtor_name.text = self._prepare_field(
|
||||
cr, uid, 'Customer Name', 'line.partner_id.name',
|
||||
name_maxsize, line=line, context=context)
|
||||
debtor_account_2_73 = etree.SubElement(dd_transaction_info_2_28, 'DbtrAcct')
|
||||
debtor_account_2_73 = etree.SubElement(
|
||||
dd_transaction_info_2_28, 'DbtrAcct')
|
||||
debtor_account_id = etree.SubElement(debtor_account_2_73, 'Id')
|
||||
debtor_account_iban = etree.SubElement(debtor_account_id, 'IBAN')
|
||||
debtor_account_iban.text = self._validate_iban(cr, uid,
|
||||
self._prepare_field(cr, uid, 'Customer IBAN',
|
||||
debtor_account_iban = etree.SubElement(
|
||||
debtor_account_id, 'IBAN')
|
||||
debtor_account_iban.text = self._validate_iban(
|
||||
cr, uid, self._prepare_field(
|
||||
cr, uid, 'Customer IBAN',
|
||||
'line.bank_id.acc_number', line=line,
|
||||
context=context),
|
||||
context=context)
|
||||
remittance_info_2_88 = etree.SubElement(dd_transaction_info_2_28, 'RmtInf')
|
||||
context=context),
|
||||
context=context)
|
||||
remittance_info_2_88 = etree.SubElement(
|
||||
dd_transaction_info_2_28, 'RmtInf')
|
||||
# switch to Structured (Strdr) ? If we do it, beware that the format is not the same between pain 02 and pain 03
|
||||
remittance_info_unstructured_2_89 = etree.SubElement(remittance_info_2_88, 'Ustrd')
|
||||
remittance_info_unstructured_2_89.text = self._prepare_field(cr, uid,
|
||||
'Remittance Information', 'line.communication',
|
||||
remittance_info_unstructured_2_89 = etree.SubElement(
|
||||
remittance_info_2_88, 'Ustrd')
|
||||
remittance_info_unstructured_2_89.text = self._prepare_field(
|
||||
cr, uid, 'Remittance Information', 'line.communication',
|
||||
140, line=line, context=context)
|
||||
nb_of_transactions_2_4.text = str(transactions_count_2_4)
|
||||
control_sum_2_5.text = '%.2f' % amount_control_sum_2_5
|
||||
nb_of_transactions_1_6.text = str(transactions_count_1_6)
|
||||
control_sum_1_7.text = '%.2f' % amount_control_sum_1_7
|
||||
|
||||
|
||||
xml_string = etree.tostring(root, pretty_print=True, encoding='UTF-8', xml_declaration=True)
|
||||
_logger.debug("Generated SDD XML file in format %s below" % pain_flavor)
|
||||
xml_string = etree.tostring(
|
||||
root, pretty_print=True, encoding='UTF-8', xml_declaration=True)
|
||||
_logger.debug(
|
||||
"Generated SDD XML file in format %s below" % pain_flavor)
|
||||
_logger.debug(xml_string)
|
||||
xsd_etree_obj = etree.parse(tools.file_open('account_banking_sepa_direct_debit/data/%s.xsd' % pain_flavor))
|
||||
official_pain_schema = etree.XMLSchema(xsd_etree_obj)
|
||||
_logger.debug("Printing %s XML Schema definition:" % pain_flavor)
|
||||
_logger.debug(etree.tostring(xsd_etree_obj, pretty_print=True, encoding='UTF-8', xml_declaration=True))
|
||||
|
||||
try:
|
||||
# If I do official_pain_schema.assertValid(root), then I get this
|
||||
# error msg in the exception :
|
||||
# The generated XML file is not valid against the official XML Schema Definition. The generated XML file and the full error have been written in the server logs. Here is the error, which may give you an idea on the cause of the problem : Element 'Document': No matching global declaration available for the validation root.
|
||||
# So I re-import the SEPA XML from the string, and give this
|
||||
# so validation
|
||||
# If you know how I can avoid that, please tell me -- Alexis
|
||||
root_to_validate = etree.fromstring(xml_string)
|
||||
official_pain_schema.assertValid(root_to_validate)
|
||||
except Exception, e:
|
||||
_logger.warning("The XML file is invalid against the XML Schema Definition")
|
||||
_logger.warning(xml_string)
|
||||
_logger.warning(e)
|
||||
raise orm.except_orm(_('Error:'), _('The generated XML file is not valid against the official XML Schema Definition. The generated XML file and the full error have been written in the server logs. Here is the error, which may give you an idea on the cause of the problem : %s') % str(e))
|
||||
self._validate_xml(cr, uid, xml_string, pain_flavor)
|
||||
|
||||
# CREATE the banking.export.sepa record
|
||||
file_id = self.pool.get('banking.export.sdd').create(cr, uid,
|
||||
{
|
||||
'batch_booking': sepa_export.batch_booking,
|
||||
'charge_bearer': sepa_export.charge_bearer,
|
||||
'requested_collec_date': sepa_export.requested_collec_date,
|
||||
'total_amount': total_amount,
|
||||
'nb_transactions': transactions_count_1_6,
|
||||
'file': base64.encodestring(xml_string),
|
||||
'payment_order_ids': [
|
||||
(6, 0, [x.id for x in sepa_export.payment_order_ids])
|
||||
],
|
||||
}, context=context)
|
||||
file_id = self.pool.get('banking.export.sdd').create(
|
||||
cr, uid, self._prepare_export_sepa(
|
||||
cr, uid, sepa_export, total_amount, transactions_count_1_6,
|
||||
xml_string, context=context),
|
||||
context=context)
|
||||
|
||||
self.write(cr, uid, ids, {
|
||||
'file_id': file_id,
|
||||
'state': 'finish',
|
||||
self.write(
|
||||
cr, uid, ids, {
|
||||
'file_id': file_id,
|
||||
'state': 'finish',
|
||||
}, context=context)
|
||||
|
||||
action = {
|
||||
'name': 'SEPA Direct Debit XML',
|
||||
'name': 'SEPA Direct Debit File',
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form,tree',
|
||||
@@ -422,12 +503,14 @@ class banking_export_sdd_wizard(orm.TransientModel):
|
||||
|
||||
def save_sepa(self, cr, uid, ids, context=None):
|
||||
'''
|
||||
Save the SEPA Direct Debit file: mark all payments in the file as 'sent'.
|
||||
Write 'last debit date' on mandate and set oneoff mandate to expired
|
||||
Save the SEPA Direct Debit file: mark all payments in the file
|
||||
as 'sent'. Write 'last debit date' on mandate and set oneoff
|
||||
mandate to expired
|
||||
'''
|
||||
sepa_export = self.browse(cr, uid, ids[0], context=context)
|
||||
self.pool.get('banking.export.sdd').write(cr, uid,
|
||||
sepa_export.file_id.id, {'state': 'sent'}, context=context)
|
||||
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)
|
||||
@@ -437,7 +520,9 @@ class banking_export_sdd_wizard(orm.TransientModel):
|
||||
'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']
|
||||
oneoff_mandate_ids = \
|
||||
[line.sdd_mandate_id.id for line in order.line_ids
|
||||
if line.sdd_mandate_id.type == 'oneoff']
|
||||
self.pool['sdd.mandate'].write(
|
||||
cr, uid, oneoff_mandate_ids, {'state': 'expired'},
|
||||
context=context)
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<form string="SEPA Direct Debit XML file generation" version="7.0">
|
||||
<field name="state" invisible="True"/>
|
||||
<group states="create">
|
||||
<separator colspan="4" string="Processing details" />
|
||||
<field name="batch_booking" />
|
||||
<field name="requested_collec_date" />
|
||||
<field name="charge_bearer" />
|
||||
|
||||
Reference in New Issue
Block a user