[13.0]account_ebics 1.3.0 - generic order type

This commit is contained in:
Luc De Meyer 2020-12-05 18:56:12 +01:00
parent 33af182021
commit e7eaf68c59
8 changed files with 376 additions and 156 deletions

View File

@ -2,104 +2,151 @@
<odoo>
<data noupdate="1">
<!--
File format tested with the following banks:
- GLS Gemeinschaftsbank (Germany)
-->
<record id="ebics_ff_camt_053_001_02_stm" model="ebics.file.format">
<field name="name">camt.053.001.02.stm</field>
<field name="type">down</field>
<field name="order_type">C53</field>
<field name="description">Bank Statement in Format camt.053</field>
<field name="suffix">c53.xml</field>
</record>
<!-- Download formats -->
<!--
File format tested with the following banks:
- Kreissparkasse Göppingen (Germany)
-->
<record id="ebics_ff_camt_052_001_02_stm" model="ebics.file.format">
<field name="name">camt.052.001.02.stm</field>
<record id="ebics_ff_C52" model="ebics.file.format">
<field name="name">camt.052</field>
<field name="type">down</field>
<field name="order_type">C52</field>
<field name="description">Bank Statement in Format camt.052</field>
<field name="download_process_method">camt.052</field>
<field name="description">bank to customer account report in format camt.052</field>
<field name="suffix">c52.xml</field>
</record>
<!--
File format tested with the following banks:
- GLS Gemeinschaftsbank (Germany)
-->
<record id="ebics_ff_pain_001_001_03_sct" model="ebics.file.format">
<field name="name">pain.001.001.03.sct</field>
<field name="type">up</field>
<field name="order_type">CCT</field>
<field name="description">Payment Order in Format pain.001.001.03</field>
<field name="suffix">xml</field>
<record id="ebics_ff_Z52" model="ebics.file.format">
<field name="name">camt.052</field>
<field name="type">down</field>
<field name="order_type">Z52</field>
<field name="download_process_method">camt.052</field>
<field name="description">bank to customer account report in format camt.052</field>
<field name="suffix">c52.xml</field>
</record>
<record id="ebics_ff_C53" model="ebics.file.format">
<field name="name">camt.053</field>
<field name="type">down</field>
<field name="order_type">C53</field>
<field name="download_process_method">camt.053</field>
<field name="description">Bank to customer statement report in format camt.053</field>
<field name="suffix">c53.xml</field>
</record>
<record id="ebics_ff_pain_008_001_02_sdd" model="ebics.file.format">
<field name="name">pain.008.001.02.sdd</field>
<field name="type">up</field>
<field name="order_type">CDD</field>
<field name="description">Sepa Core Direct Debit Order in Format pain.008.001.02</field>
<field name="suffix">xml</field>
<record id="ebics_ff_Z53" model="ebics.file.format">
<field name="name">camt.053</field>
<field name="type">down</field>
<field name="order_type">Z53</field>
<field name="download_process_method">camt.053</field>
<field name="description">Bank to customer statement report in format camt.053</field>
<field name="suffix">c53.xml</field>
</record>
<record id="ebics_ff_C54" model="ebics.file.format">
<field name="name">camt.054</field>
<field name="type">down</field>
<field name="order_type">C54</field>
<field name="download_process_method">camt.054</field>
<field name="description">Bank to customer debit credit notification in format camt.054</field>
<field name="suffix">c52.xml</field>
</record>
<record id="ebics_ff_pain_008_001_02_sbb" model="ebics.file.format">
<field name="name">pain.008.001.02.sbb</field>
<field name="type">up</field>
<field name="order_type">CDB</field>
<field name="description">Sepa Direct Debit (B2B) Order in Format pain.008.001.02</field>
<field name="suffix">xml</field>
<record id="ebics_ff_Z54" model="ebics.file.format">
<field name="name">camt.054</field>
<field name="type">down</field>
<field name="order_type">Z54</field>
<field name="download_process_method">camt.054</field>
<field name="description">Bank to customer debit credit notification in format camt.054</field>
<field name="suffix">c52.xml</field>
</record>
<!--
File format tested with the following banks:
- Credit Suisse (Switzerland)
-->
<record id="ebics_ff_pain_001" model="ebics.file.format">
<field name="name">pain.001</field>
<field name="type">up</field>
<field name="order_type">XE2</field>
<field name="description">Payment Order in Format pain.001.001.03</field>
<field name="suffix">xml</field>
</record>
<!--
File format tested with the following banks:
- Credit Suisse (Switzerland)
-->
<record id="ebics_ff_pain_008" model="ebics.file.format">
<field name="name">pain.008</field>
<field name="type">up</field>
<field name="order_type">XE3</field>
<field name="description">Direct Debit Order in Format pain.008.001.02</field>
<field name="suffix">xml</field>
</record>
<!--
File format tested with the following banks:
- CIC (France)
-->
<record id="ebics_ff_camt_xxx_cfonb120_stm" model="ebics.file.format">
<record id="ebics_ff_FDL_camt_xxx_cfonb120_stm" model="ebics.file.format">
<field name="name">camt.xxx.cfonb120.stm</field>
<field name="type">down</field>
<field name="order_type">FDL</field>
<field name="description">Bank Statement in Format cfonb120</field>
<field name="download_process_method">cfonb120</field>
<field name="description">Bank to customer statement report in format cfonb120</field>
<field name="suffix">cfonb120.dat</field>
</record>
<!--
File format tested with the following banks:
- CIC (France)
-->
<record id="ebics_ff_pain_001_001_02_sct" model="ebics.file.format">
<record id="ebics_ff_CDZ" model="ebics.file.format">
<field name="name">pain.002</field>
<field name="type">down</field>
<field name="order_type">CDZ</field>
<field name="description">Payment status report for direct debit in format pain.002</field>
<field name="suffix">psr.xml</field>
</record>
<record id="ebics_ff_Z01" model="ebics.file.format">
<field name="name">pain.002</field>
<field name="type">down</field>
<field name="order_type">Z01</field>
<field name="description">Payment status report for direct debit in format pain.002</field>
<field name="suffix">psr.xml</field>
</record>
<!-- Upload formats -->
<record id="ebics_ff_CCT" model="ebics.file.format">
<field name="name">pain.001.001.03</field>
<field name="type">up</field>
<field name="order_type">CCT</field>
<field name="description">Payment Order in format pain.001.001.03</field>
<field name="suffix">cct.xml</field>
</record>
<record id="ebics_ff_XE2" model="ebics.file.format">
<field name="name">pain.001.001.03</field>
<field name="type">up</field>
<field name="order_type">XE2</field>
<field name="description">Payment Order in format pain.001.001.03</field>
<field name="suffix">cct.xml</field>
</record>
<record id="ebics_ff_CDD" model="ebics.file.format">
<field name="name">pain.008.001.02.sdd</field>
<field name="type">up</field>
<field name="order_type">CDD</field>
<field name="description">Sepa Core Direct Debit Order in format pain.008.001.02</field>
<field name="suffix">sdd.xml</field>
</record>
<record id="ebics_ff_CDD" model="ebics.file.format">
<field name="name">pain.008.001.02.sdd</field>
<field name="type">up</field>
<field name="order_type">CDD</field>
<field name="description">Sepa Core Direct Debit Order in format pain.008.001.02</field>
<field name="suffix">sdd.xml</field>
</record>
<record id="ebics_ff_XE3" model="ebics.file.format">
<field name="name">pain.008.001.02.sdd</field>
<field name="type">up</field>
<field name="order_type">XE3</field>
<field name="description">Sepa Core Direct Debit Order in format pain.008.001.02</field>
<field name="suffix">sdd.xml</field>
</record>
<record id="ebics_ff_CDB" model="ebics.file.format">
<field name="name">pain.008.001.02.sbb</field>
<field name="type">up</field>
<field name="order_type">CDB</field>
<field name="description">Sepa Direct Debit (B2B) Order in format pain.008.001.02</field>
<field name="suffix">sbb.xml</field>
</record>
<record id="ebics_ff_XE4" model="ebics.file.format">
<field name="name">pain.008.001.02.sbb</field>
<field name="type">up</field>
<field name="order_type">XE4</field>
<field name="description">Sepa Direct Debit (B2B) Order in format pain.008.001.02</field>
<field name="suffix">sbb.xml</field>
</record>
<record id="ebics_ff_FUL_pain_001_001_02_sct" model="ebics.file.format">
<field name="name">pain.001.001.02.sct</field>
<field name="type">up</field>
<field name="order_type">FUL</field>
<field name="description">Payment Order in Format pain.001.001.02</field>
<field name="suffix">xml</field>
<field name="description">Payment Order in format pain.001.001.02</field>
<field name="suffix">cct.xml</field>
</record>
</data>

View File

@ -0,0 +1,41 @@
# Copyright 2009-2020 Noviat.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
_FILE_FORMATS = [
{'xml_id_name': 'ebics_ff_C52',
'download_process_method': 'camt.052',
},
{'xml_id_name': 'ebics_ff_C53',
'download_process_method': 'camt.053',
},
{'xml_id_name': 'ebics_ff_FDL_camt_xxx_cfonb120_stm',
'download_process_method': 'cfonb120',
},
]
def migrate(cr, version):
for ff in _FILE_FORMATS:
_update_file_format(cr, ff)
def _update_file_format(cr, ff):
cr.execute(
"""
SELECT res_id FROM ir_model_data
WHERE module='account_ebics' AND name='{}'
""".format(ff['xml_id_name'])
)
res = cr.fetchone()
if res:
cr.execute(
"""
UPDATE ebics_file_format
SET download_process_method='{download_process_method}'
WHERE id={ff_id};
""".format(
download_process_method=ff['download_process_method'],
ff_id=res[0]
)
)

View File

@ -0,0 +1,70 @@
# Copyright 2009-2020 Noviat.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
_FILE_FORMATS = [
{'old_xml_id_name': 'ebics_ff_camt_052_001_02_stm',
'new_xml_id_name': 'ebics_ff_C52',
'new_name': 'camt.052',
},
{'old_xml_id_name': 'ebics_ff_camt_053_001_02_stm',
'new_xml_id_name': 'ebics_ff_C53',
'new_name': 'camt.053',
},
{'old_xml_id_name': 'ebics_ff_camt_xxx_cfonb120_stm',
'new_xml_id_name': 'ebics_ff_FDL_camt_xxx_cfonb120_stm',
},
{'old_xml_id_name': 'ebics_ff_pain_001_001_03_sct',
'new_xml_id_name': 'ebics_ff_CCT',
},
{'old_xml_id_name': 'ebics_ff_pain_001',
'new_xml_id_name': 'ebics_ff_XE2',
'new_name': 'pain.001.001.03',
},
{'old_xml_id_name': 'ebics_ff_pain_008_001_02_sdd',
'new_xml_id_name': 'ebics_ff_CDD',
},
{'old_xml_id_name': 'ebics_ff_pain_008',
'new_xml_id_name': 'ebics_ff_XE3',
},
{'old_xml_id_name': 'ebics_ff_pain_008_001_02_sbb',
'new_xml_id_name': 'ebics_ff_CDB',
},
{'old_xml_id_name': 'ebics_ff_pain_001_001_02_sct',
'new_xml_id_name': 'ebics_ff_FUL_pain_001_001_02_sct',
},
]
def migrate(cr, version):
if not version:
return
for ff in _FILE_FORMATS:
_update_file_format(cr, ff)
def _update_file_format(cr, ff):
cr.execute(
"""
SELECT id, res_id FROM ir_model_data
WHERE module='account_ebics' AND name='{}'
""".format(ff['old_xml_id_name'])
)
res = cr.fetchone()
if res:
query = """
UPDATE ir_model_data
SET name='{new_xml_id_name}'
WHERE id={xml_id};
""".format(
new_xml_id_name=ff["new_xml_id_name"], xml_id=res[0]
)
if ff.get('new_name'):
query += """
UPDATE ebics_file_format
SET name='{new_name}'
WHERE id={ff_id};
""".format(
new_name=ff["new_name"], ff_id=res[1]
)
cr.execute(query)

View File

@ -70,7 +70,7 @@ class EbicsFile(models.Model):
raise UserError(_(
"You can only remove EBICS files in state 'Draft'."))
# execute format specific actions
ff = ebics_file.format_id.name
ff = ebics_file.format_id.download_process_method
if ff in ff_methods:
if ff_methods[ff].get('unlink'):
ff_methods[ff]['unlink'](ebics_file)
@ -92,7 +92,7 @@ class EbicsFile(models.Model):
self = self.with_context(ctx)
self.note_process = ''
ff_methods = self._file_format_methods()
ff = self.format_id.name
ff = self.format_id.download_process_method
if ff in ff_methods:
if ff_methods[ff].get('process'):
res = ff_methods[ff]['process'](self)
@ -120,15 +120,18 @@ class EbicsFile(models.Model):
for extra file formats.
"""
res = {
'camt.xxx.cfonb120.stm':
'cfonb120':
{'process': self._process_cfonb120,
'unlink': self._unlink_cfonb120},
'camt.052.001.02.stm':
'camt.052':
{'process': self._process_camt052,
'unlink': self._unlink_camt052},
'camt.053.001.02.stm':
'camt.053':
{'process': self._process_camt053,
'unlink': self._unlink_camt053},
'camt.054':
{'process': self._process_camt054,
'unlink': self._unlink_camt054},
}
return res
@ -294,6 +297,20 @@ class EbicsFile(models.Model):
"""
pass
@staticmethod
def _process_camt054(self):
import_module = 'account_bank_statement_import_camt_oca'
self._check_import_module(import_module)
return self._process_camt053(self)
@staticmethod
def _unlink_camt054(self):
"""
Placeholder for camt054 specific actions before removing the
EBICS data file and its related bank statements.
"""
pass
@staticmethod
def _process_camt053(self):
import_module = 'account_bank_statement_import_camt%'

View File

@ -1,29 +1,41 @@
# Copyright 2009-2020 Noviat.
# License LGPL-3 or later (http://www.gnu.org/licenses/lpgl).
from odoo import fields, models
from odoo import api, fields, models
class EbicsFileFormat(models.Model):
_name = 'ebics.file.format'
_description = 'EBICS File Formats'
_order = 'type,name'
_order = 'type,name,order_type'
name = fields.Char(
string='Request Type',
required=True,
help="E.g. camt.053.001.02.stm, camt.xxx.cfonb120.stm, "
"pain.001.001.03.sct (check your EBICS contract).\n")
help="E.g. camt.xxx.cfonb120.stm, pain.001.001.03.sct.\n"
"Specify camt.052, camt.053, camt.054 for camt "
"Order Types such as C53, Z53, C54, Z54.\n"
"This name has to match the 'Request Type' in your "
"EBICS contract for Order Type 'FDL' or 'FUL'.\n")
type = fields.Selection(
selection=[('down', 'Download'),
('up', 'Upload')],
required=True)
order_type = fields.Char(
string='Order Type',
required=True,
help="E.g. C53 (check your EBICS contract).\n"
"For most banks in France you should use the "
"format neutral Order Types 'FUL' for upload "
"and 'FDL' for download.")
download_process_method = fields.Selection(
selection='_selection_download_process_method',
help="Enable processing within Odoo of the downloaded file "
"via the 'Process' button."
"E.g. specify camt.053 to import a camt.053 file and create "
"a bank statement.")
# TODO:
# move signature_class parameter so that it can be set per EBICS config
signature_class = fields.Selection(
selection=[('E', 'Single signature'),
('T', 'Transport signature')],
@ -32,9 +44,19 @@ class EbicsFileFormat(models.Model):
"ERP system when using class 'E' to prevent unauthorised "
"users to make supplier payments."
"\nLeave this field empty to use the default "
"defined for your bank connection.")
"defined for your EBICS UserID.")
description = fields.Char()
suffix = fields.Char(
required=True,
help="Specify the filename suffix for this File Format."
"\nE.g. camt.053.xml")
"\nE.g. c53.xml")
@api.model
def _selection_download_process_method(self):
methods = self.env['ebics.file']._file_format_methods().keys()
return [(x, x) for x in methods]
@api.onchange('type')
def _onchange_type(self):
if self.type == 'up':
self.download_process_method = False

View File

@ -32,10 +32,10 @@ try:
keycode=fintech_register_keycode,
users=fintech_register_users)
except RuntimeError as e:
if e.message == "'register' can be called only once":
if str(e) == "'register' can be called only once":
pass
else:
_logger.error(e.message)
_logger.error(str(e))
fintech.register()
except Exception:
msg = "fintech.register error"

View File

@ -20,13 +20,21 @@
<field name="model">ebics.file.format</field>
<field name="arch" type="xml">
<form string="EBICS File Format">
<group col="4">
<field name="type"/>
<field name="order_type"/>
<field name="name"/>
<field name="signature_class"/>
<field name="suffix"/>
<newline/>
<group name="main">
<group name="main-left">
<field name="type"/>
<field name="suffix"/>
<field name="download_process_method"
attrs="{'invisible': [('type', '=', 'up')]}"
force_save="1"/>
<field name="signature_class"/>
</group>
<group name="main-right">
<field name="order_type"/>
<field name="name"/>
</group>
</group>
<group name="description">
<field name="description"/>
</group>
</form>

View File

@ -64,8 +64,10 @@ class EbicsXfer(models.TransientModel):
format_id = fields.Many2one(
comodel_name='ebics.file.format',
string='EBICS File Format',
help="Select EBICS File Format to upload/download.")
help="Select EBICS File Format to upload/download."
"\nLeave blank to download all available files.")
order_type = fields.Char(
related='format_id.order_type',
string='Order Type',
help="For most banks in France you should use the "
"format neutral Order Types 'FUL' for upload "
@ -160,56 +162,73 @@ class EbicsXfer(models.TransientModel):
self.note = ''
client = self._setup_client()
if client:
download_formats = (
self.format_id
or self.ebics_config_id.ebics_file_format_ids.filtered(
lambda r: r.type == 'down'
)
)
ebics_files = self.env['ebics.file']
success = False
order_type = self.order_type or 'FDL'
date_from = self.date_from and self.date_from.isoformat() or None
date_to = self.date_to and self.date_to.isoformat() or None
try:
if order_type == 'FDL':
data = client.FDL(self.format_id.name, date_from, date_to)
for df in download_formats:
try:
success = False
if df.order_type == 'FDL':
data = client.FDL(df.name, date_from, date_to)
else:
params = None
if date_from and date_to:
params = {'DateRange': {
'Start': date_from,
'End': date_to,
}}
data = client.download(df.order_type, params=params)
ebics_files += self._handle_download_data(data, df)
success = True
except EbicsFunctionalError:
e = exc_info()
self.note += '\n'
self.note += _(
"EBICS Functional Error during download of File Format %s (%s):"
) % (df.name, df.order_type)
self.note += '\n'
self.note += '%s (code: %s)' % (e[1].message, e[1].code)
except EbicsTechnicalError:
e = exc_info()
self.note += '\n'
self.note += _(
"EBICS Technical Error during download of File Format %s (%s):"
) % (df.name, df.order_type)
self.note += '\n'
self.note += '%s (code: %s)' % (e[1].message, e[1].code)
except EbicsVerificationError:
self.note += '\n'
self.note += _(
"EBICS Verification Error during download of "
"File Format %s (%s):"
) % (df.name, df.order_type)
self.note += '\n'
self.note += _("The EBICS response could not be verified.")
except UserError as e:
self.note += '\n'
self.note += _(
"Warning during download of File Format %s (%s):"
) % (df.name, df.order_type)
self.note += '\n'
self.note += e.name
except Exception:
self.note += '\n'
self.note += _(
"Unknown Error during download of File Format %s (%s):"
) % (df.name, df.order_type)
tb = ''.join(format_exception(*exc_info()))
self.note += '\n%s' % tb
else:
params = None
if date_from and date_to:
params = {'DateRange': {
'Start': date_from,
'End': date_to,
}}
data = client.download(order_type, params=params)
ebics_files += self._handle_download_data(data, self.format_id)
success = True
except EbicsFunctionalError:
e = exc_info()
self.note += '\n'
self.note += _("EBICS Functional Error:")
self.note += '\n'
self.note += '%s (code: %s)' % (e[1].message, e[1].code)
except EbicsTechnicalError:
e = exc_info()
self.note += '\n'
self.note += _("EBICS Technical Error:")
self.note += '\n'
self.note += '%s (code: %s)' % (e[1].message, e[1].code)
except EbicsVerificationError:
self.note += '\n'
self.note += _("EBICS Verification Error:")
self.note += '\n'
self.note += _("The EBICS response could not be verified.")
except UserError as e:
self.note += '\n'
self.note += _("Warning:")
self.note += '\n'
self.note += e.name
except Exception:
self.note += '\n'
self.note += _("Unknown Error")
tb = ''.join(format_exception(*exc_info()))
self.note += '\n%s' % tb
else:
# mark received data so that it is not included in further
# downloads
trans_id = client.last_trans_id
client.confirm_download(trans_id=trans_id, success=success)
# mark received data so that it is not included in further
# downloads
trans_id = client.last_trans_id
client.confirm_download(trans_id=trans_id, success=success)
ctx['ebics_file_ids'] = ebics_files._ids
@ -258,9 +277,7 @@ class EbicsXfer(models.TransientModel):
ef_format = self.format_id
OrderID = False
try:
order_type = self.order_type or 'FUL'
method = hasattr(client, order_type) \
and getattr(client, order_type)
order_type = self.order_type
if order_type == 'FUL':
kwargs = {}
bank = self.ebics_config_id.journal_ids[0].bank_id
@ -269,9 +286,7 @@ class EbicsXfer(models.TransientModel):
kwargs['country'] = cc
if self.test_mode:
kwargs['TEST'] = 'TRUE'
OrderID = method(ef_format.name, upload_data, **kwargs)
elif order_type in ['CCT', 'CDD', 'CDB']:
OrderID = method(upload_data)
OrderID = client.FUL(ef_format.name, upload_data, **kwargs)
else:
OrderID = client.upload(order_type, upload_data)
if OrderID:
@ -509,17 +524,17 @@ class EbicsXfer(models.TransientModel):
else:
o_list[-i] = chr(ord(c) + 1)
break
next = ''.join(o_list)
if next == 'ZZZZ':
next = 'A000'
self.ebics_config_id.order_number = next
next_nr = ''.join(o_list)
if next_nr == 'ZZZZ':
next_nr = 'A000'
self.ebics_config_id.order_number = next_nr
def _insert_line_terminator(self, data_in, line_len):
data_in = data_in.replace(b'\n', b'').replace(b'\r', b'')
data_out = b''
max = len(data_in)
max_len = len(data_in)
i = 0
while i + line_len <= max:
while i + line_len <= max_len:
data_out += data_in[i:i + line_len] + b'\n'
i += line_len
return data_out