add support for ebics 3.0

This commit is contained in:
Luc De Meyer 2022-11-18 10:27:25 +01:00
parent 5996e5ab2e
commit da5b0d58c8
8 changed files with 173 additions and 21 deletions

View File

@ -194,5 +194,4 @@ You can also find this information in the doc folder of this module (file EBICS_
Known Issues / Roadmap Known Issues / Roadmap
====================== ======================
- add support for EBICS 3.0
- add support to import externally generated keys & certificates (currently only 3SKey signature certificate) - add support to import externally generated keys & certificates (currently only 3SKey signature certificate)

View File

@ -4,6 +4,7 @@
<!-- Download formats --> <!-- Download formats -->
<record id="ebics_ff_C52" model="ebics.file.format"> <record id="ebics_ff_C52" model="ebics.file.format">
<field name="ebics_version">2</field>
<field name="name">camt.052</field> <field name="name">camt.052</field>
<field name="type">down</field> <field name="type">down</field>
<field name="order_type">C52</field> <field name="order_type">C52</field>
@ -13,6 +14,7 @@
</record> </record>
<record id="ebics_ff_Z52" model="ebics.file.format"> <record id="ebics_ff_Z52" model="ebics.file.format">
<field name="ebics_version">2</field>
<field name="name">camt.052</field> <field name="name">camt.052</field>
<field name="type">down</field> <field name="type">down</field>
<field name="order_type">Z52</field> <field name="order_type">Z52</field>
@ -22,6 +24,7 @@
</record> </record>
<record id="ebics_ff_C53" model="ebics.file.format"> <record id="ebics_ff_C53" model="ebics.file.format">
<field name="ebics_version">2</field>
<field name="name">camt.053</field> <field name="name">camt.053</field>
<field name="type">down</field> <field name="type">down</field>
<field name="order_type">C53</field> <field name="order_type">C53</field>
@ -33,6 +36,7 @@
</record> </record>
<record id="ebics_ff_Z53" model="ebics.file.format"> <record id="ebics_ff_Z53" model="ebics.file.format">
<field name="ebics_version">2</field>
<field name="name">camt.053</field> <field name="name">camt.053</field>
<field name="type">down</field> <field name="type">down</field>
<field name="order_type">Z53</field> <field name="order_type">Z53</field>
@ -44,6 +48,7 @@
</record> </record>
<record id="ebics_ff_C54" model="ebics.file.format"> <record id="ebics_ff_C54" model="ebics.file.format">
<field name="ebics_version">2</field>
<field name="name">camt.054</field> <field name="name">camt.054</field>
<field name="type">down</field> <field name="type">down</field>
<field name="order_type">C54</field> <field name="order_type">C54</field>
@ -55,6 +60,7 @@
</record> </record>
<record id="ebics_ff_Z54" model="ebics.file.format"> <record id="ebics_ff_Z54" model="ebics.file.format">
<field name="ebics_version">2</field>
<field name="name">camt.054</field> <field name="name">camt.054</field>
<field name="type">down</field> <field name="type">down</field>
<field name="order_type">Z54</field> <field name="order_type">Z54</field>
@ -66,6 +72,7 @@
</record> </record>
<record id="ebics_ff_FDL_camt_xxx_cfonb120_stm" model="ebics.file.format"> <record id="ebics_ff_FDL_camt_xxx_cfonb120_stm" model="ebics.file.format">
<field name="ebics_version">2</field>
<field name="name">camt.xxx.cfonb120.stm</field> <field name="name">camt.xxx.cfonb120.stm</field>
<field name="type">down</field> <field name="type">down</field>
<field name="order_type">FDL</field> <field name="order_type">FDL</field>
@ -77,6 +84,7 @@
</record> </record>
<record id="ebics_ff_CDZ" model="ebics.file.format"> <record id="ebics_ff_CDZ" model="ebics.file.format">
<field name="ebics_version">2</field>
<field name="name">pain.002</field> <field name="name">pain.002</field>
<field name="type">down</field> <field name="type">down</field>
<field name="order_type">CDZ</field> <field name="order_type">CDZ</field>
@ -87,6 +95,7 @@
</record> </record>
<record id="ebics_ff_Z01" model="ebics.file.format"> <record id="ebics_ff_Z01" model="ebics.file.format">
<field name="ebics_version">2</field>
<field name="name">pain.002</field> <field name="name">pain.002</field>
<field name="type">down</field> <field name="type">down</field>
<field name="order_type">Z01</field> <field name="order_type">Z01</field>
@ -97,9 +106,23 @@
<field name="suffix">psr.xml</field> <field name="suffix">psr.xml</field>
</record> </record>
<record id="ebics_ff_btf_cfonb120" model="ebics.file.format">
<field name="ebics_version">3</field>
<field name="type">down</field>
<field name="order_type">BTD</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>
<field name="btf_service">EOP</field>
<field name="btf_message">cfonb120</field>
</record>
<!-- Upload formats --> <!-- Upload formats -->
<record id="ebics_ff_LCR" model="ebics.file.format"> <record id="ebics_ff_LCR" model="ebics.file.format">
<field name="ebics_version">2</field>
<field name="name">pain.xxx.cfonb160.dco</field> <field name="name">pain.xxx.cfonb160.dco</field>
<field name="type">up</field> <field name="type">up</field>
<field name="order_type">FUL</field> <field name="order_type">FUL</field>
@ -108,6 +131,7 @@
</record> </record>
<record id="ebics_ff_CCT" model="ebics.file.format"> <record id="ebics_ff_CCT" model="ebics.file.format">
<field name="ebics_version">2</field>
<field name="name">pain.001.001.03</field> <field name="name">pain.001.001.03</field>
<field name="type">up</field> <field name="type">up</field>
<field name="order_type">CCT</field> <field name="order_type">CCT</field>
@ -116,6 +140,7 @@
</record> </record>
<record id="ebics_ff_XE2" model="ebics.file.format"> <record id="ebics_ff_XE2" model="ebics.file.format">
<field name="ebics_version">2</field>
<field name="name">pain.001.001.03</field> <field name="name">pain.001.001.03</field>
<field name="type">up</field> <field name="type">up</field>
<field name="order_type">XE2</field> <field name="order_type">XE2</field>
@ -124,6 +149,7 @@
</record> </record>
<record id="ebics_ff_CDD" model="ebics.file.format"> <record id="ebics_ff_CDD" model="ebics.file.format">
<field name="ebics_version">2</field>
<field name="name">pain.008.001.02.sdd</field> <field name="name">pain.008.001.02.sdd</field>
<field name="type">up</field> <field name="type">up</field>
<field name="order_type">CDD</field> <field name="order_type">CDD</field>
@ -134,6 +160,7 @@
</record> </record>
<record id="ebics_ff_XE3" model="ebics.file.format"> <record id="ebics_ff_XE3" model="ebics.file.format">
<field name="ebics_version">2</field>
<field name="name">pain.008.001.02.sdd</field> <field name="name">pain.008.001.02.sdd</field>
<field name="type">up</field> <field name="type">up</field>
<field name="order_type">XE3</field> <field name="order_type">XE3</field>
@ -144,6 +171,7 @@
</record> </record>
<record id="ebics_ff_CDB" model="ebics.file.format"> <record id="ebics_ff_CDB" model="ebics.file.format">
<field name="ebics_version">2</field>
<field name="name">pain.008.001.02.sbb</field> <field name="name">pain.008.001.02.sbb</field>
<field name="type">up</field> <field name="type">up</field>
<field name="order_type">CDB</field> <field name="order_type">CDB</field>
@ -154,6 +182,7 @@
</record> </record>
<record id="ebics_ff_XE4" model="ebics.file.format"> <record id="ebics_ff_XE4" model="ebics.file.format">
<field name="ebics_version">2</field>
<field name="name">pain.008.001.02.sbb</field> <field name="name">pain.008.001.02.sbb</field>
<field name="type">up</field> <field name="type">up</field>
<field name="order_type">XE4</field> <field name="order_type">XE4</field>
@ -164,6 +193,7 @@
</record> </record>
<record id="ebics_ff_FUL_pain_001_001_02_sct" model="ebics.file.format"> <record id="ebics_ff_FUL_pain_001_001_02_sct" model="ebics.file.format">
<field name="ebics_version">2</field>
<field name="name">pain.001.001.02.sct</field> <field name="name">pain.001.001.02.sct</field>
<field name="type">up</field> <field name="type">up</field>
<field name="order_type">FUL</field> <field name="order_type">FUL</field>
@ -171,4 +201,15 @@
<field name="suffix">xml</field> <field name="suffix">xml</field>
</record> </record>
<record id="ebics_ff_btf_SCT" model="ebics.file.format">
<field name="ebics_version">3</field>
<field name="type">up</field>
<field name="order_type">BTU</field>
<field name="description">SEPA credit transfer</field>
<field name="suffix">txt</field>
<field name="btf_service">SCT</field>
<field name="btf_message">pain.001</field>
<field name="btf_scope">GLB</field>
</record>
</odoo> </odoo>

View File

@ -51,7 +51,11 @@ class EbicsConfig(models.Model):
help="Contact your bank to get the EBICS URL.", help="Contact your bank to get the EBICS URL.",
) )
ebics_version = fields.Selection( ebics_version = fields.Selection(
selection=[("H003", "H003 (2.4)"), ("H004", "H004 (2.5)")], selection=[
("H003", "H003 (2.4)"),
("H004", "H004 (2.5)"),
("H005", "H005 (3.0)"),
],
string="EBICS protocol version", string="EBICS protocol version",
readonly=True, readonly=True,
states={"draft": [("readonly", False)]}, states={"draft": [("readonly", False)]},

View File

@ -9,9 +9,17 @@ class EbicsFileFormat(models.Model):
_description = "EBICS File Formats" _description = "EBICS File Formats"
_order = "type,name,order_type" _order = "type,name,order_type"
ebics_version = fields.Selection(
selection=[
("2", "2"),
("3", "3"),
],
string="EBICS protocol version",
required=True,
default="2",
)
name = fields.Char( name = fields.Char(
string="Request Type", string="Request Type",
required=True,
help="E.g. camt.xxx.cfonb120.stm, pain.001.001.03.sct.\n" help="E.g. camt.xxx.cfonb120.stm, pain.001.001.03.sct.\n"
"Specify camt.052, camt.053, camt.054 for camt " "Specify camt.052, camt.053, camt.054 for camt "
"Order Types such as C53, Z53, C54, Z54.\n" "Order Types such as C53, Z53, C54, Z54.\n"
@ -23,7 +31,8 @@ class EbicsFileFormat(models.Model):
) )
order_type = fields.Char( order_type = fields.Char(
required=True, required=True,
help="E.g. C53 (check your EBICS contract).\n" help="EBICS 3.0: BTD (download) or BTU (upload).\n"
"EBICS 2.0: E.g. C53 (check your EBICS contract). "
"For most banks in France you should use the " "For most banks in France you should use the "
"format neutral Order Types 'FUL' for upload " "format neutral Order Types 'FUL' for upload "
"and 'FDL' for download.", "and 'FDL' for download.",
@ -48,7 +57,48 @@ class EbicsFileFormat(models.Model):
description = fields.Char() description = fields.Char()
suffix = fields.Char( suffix = fields.Char(
required=True, required=True,
help="Specify the filename suffix for this File Format." "\nE.g. c53.xml", help="Specify the filename suffix for this File Format.\nE.g. c53.xml",
)
# EBICS 3.0 BTF
btf_service = fields.Char(
string="BTF Service",
help="BTF Service Name)\n"
"The service code name consisting of 3 alphanumeric characters "
"[A-Z0-9] (e.g. SCT, SDD, STM, EOP)",
)
btf_message = fields.Char(
string="BTF Message Name",
help="BTF Message Name\n"
"The message name consisting of up to 10 alphanumeric characters "
"[a-z0-9.] (eg. pain.001, pain.008, camt.053)",
)
btf_scope = fields.Char(
string="BTF Scope",
help="Scope of service.\n"
"Either an ISO-3166 ALPHA 2 country code or an issuer code "
"of 3 alphanumeric characters [A-Z0-9].",
)
btf_option = fields.Char(
string="BTF Option",
help="The service option code consisting of 3-10 alphanumeric "
"characters [A-Z0-9] (eg. COR, B2B)",
)
btf_container = fields.Char(
string="BTF Container",
help="Type of container consisting of 3 characters [A-Z] (eg. XML, ZIP).",
)
btf_version = fields.Char(
string="BTF Version",
help="Message version consisting of 2 numeric characters [0-9] (eg. 03).",
)
btf_variant = fields.Char(
string="BTF Variant",
help="Message variant consisting of 3 numeric characters [0-9] (eg. 001).",
)
btf_format = fields.Char(
string="BTF Format",
help="Message format consisting of 1-4 alphanumeric characters [A-Z0-9] "
"(eg. XML, JSON, PDF).",
) )
@api.model @api.model
@ -60,3 +110,10 @@ class EbicsFileFormat(models.Model):
def _onchange_type(self): def _onchange_type(self):
if self.type == "up": if self.type == "up":
self.download_process_method = False self.download_process_method = False
def name_get(self):
res = []
for rec in self:
name = rec.ebics_version == "2" and rec.name or rec.btf_message
res.append((rec.id, name))
return res

View File

@ -173,7 +173,9 @@ class EbicsUserID(models.Model):
for rec in self: for rec in self:
keys_dir = rec.ebics_config_id.ebics_keys keys_dir = rec.ebics_config_id.ebics_keys
rec.ebics_keys_fn = ( rec.ebics_keys_fn = (
rec.name and keys_dir and (keys_dir + "/" + rec.name + "_keys") rec.name
and keys_dir
and (keys_dir + "/" + rec.name.replace(" ", "_") + "_keys")
) )
@api.depends("ebics_keys_fn") @api.depends("ebics_keys_fn")
@ -254,7 +256,7 @@ class EbicsUserID(models.Model):
# enable import of all type of certicates: A00x, X002, E002 # enable import of all type of certicates: A00x, X002, E002
if self.swift_3skey: if self.swift_3skey:
kwargs = { kwargs = {
self.ebics_config_id.ebics_key_version: base64.decodestring( self.ebics_config_id.ebics_key_version: base64.decodebytes(
self.swift_3skey_certificate self.swift_3skey_certificate
), ),
} }
@ -300,7 +302,7 @@ class EbicsUserID(models.Model):
) )
try: try:
supported_versions = client.HEV() supported_versions = client.HEV()
if ebics_version not in supported_versions: if supported_versions and ebics_version not in supported_versions:
err_msg = _("EBICS version mismatch.") + "\n" err_msg = _("EBICS version mismatch.") + "\n"
err_msg += _("Versions supported by your bank:") err_msg += _("Versions supported by your bank:")
for k in supported_versions: for k in supported_versions:
@ -321,9 +323,11 @@ class EbicsUserID(models.Model):
tb, tb,
) )
raise UserError( raise UserError(
_("urlopen error:\n url '%(url)s' - %(val)s"), _(
"urlopen error:\n url '%(url)s' - %(val)s",
url=self.ebics_config_id.ebics_url, url=self.ebics_config_id.ebics_url,
val=str(value), val=str(value),
)
) from err ) from err
except EbicsFunctionalError as err: except EbicsFunctionalError as err:
e = exc_info() e = exc_info()
@ -441,7 +445,7 @@ class EbicsUserID(models.Model):
) )
self.write( self.write(
{ {
"ebics_public_bank_keys": base64.encodestring(public_bank_keys), "ebics_public_bank_keys": base64.encodebytes(public_bank_keys),
"ebics_public_bank_keys_fn": fn, "ebics_public_bank_keys_fn": fn,
"state": "to_verify", "state": "to_verify",
} }

View File

@ -66,7 +66,7 @@
<field name="ebics_key_bitlength" /> <field name="ebics_key_bitlength" />
<field <field
name="order_number" name="order_number"
attrs="{'invisible': [('ebics_version', '=', 'H004')]}" attrs="{'invisible': [('ebics_version', '!=', 'H003')]}"
/> />
</group> </group>
<field name="company_ids" invisible="1" /> <field name="company_ids" invisible="1" />

View File

@ -6,6 +6,7 @@
<field name="model">ebics.file.format</field> <field name="model">ebics.file.format</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree> <tree>
<field name="ebics_version" />
<field name="type" /> <field name="type" />
<field name="order_type" /> <field name="order_type" />
<field name="signature_class" /> <field name="signature_class" />
@ -22,6 +23,7 @@
<form string="EBICS File Format"> <form string="EBICS File Format">
<group name="main"> <group name="main">
<group name="main-left"> <group name="main-left">
<field name="ebics_version" />
<field name="type" /> <field name="type" />
<field name="suffix" /> <field name="suffix" />
<field <field
@ -33,7 +35,42 @@
</group> </group>
<group name="main-right"> <group name="main-right">
<field name="order_type" /> <field name="order_type" />
<field name="name" /> <field
name="name"
attrs="{'required': [('ebics_version', '=', '2')], 'invisible': [('ebics_version', '=', '3')]}"
/>
<field
name="btf_service"
attrs="{'required': [('ebics_version', '=', '3')], 'invisible': [('ebics_version', '=', '2')]}"
/>
<field
name="btf_message"
attrs="{'required': [('ebics_version', '=', '3')], 'invisible': [('ebics_version', '=', '2')]}"
/>
<field
name="btf_scope"
attrs="{'invisible': [('ebics_version', '=', '2')]}"
/>
<field
name="btf_option"
attrs="{'invisible': [('ebics_version', '=', '2')]}"
/>
<field
name="btf_container"
attrs="{'invisible': [('ebics_version', '=', '2')]}"
/>
<field
name="btf_version"
attrs="{'invisible': [('ebics_version', '=', '2')]}"
/>
<field
name="btf_variant"
attrs="{'invisible': [('ebics_version', '=', '2')]}"
/>
<field
name="btf_format"
attrs="{'invisible': [('ebics_version', '=', '2')]}"
/>
</group> </group>
</group> </group>
<group name="description"> <group name="description">

View File

@ -22,6 +22,7 @@ _logger = logging.getLogger(__name__)
try: try:
import fintech import fintech
from fintech.ebics import ( from fintech.ebics import (
BusinessTransactionFormat,
EbicsBank, EbicsBank,
EbicsClient, EbicsClient,
EbicsFunctionalError, EbicsFunctionalError,
@ -81,9 +82,6 @@ class EbicsXfer(models.TransientModel):
order_type = fields.Char( order_type = fields.Char(
related="format_id.order_type", related="format_id.order_type",
string="Order Type", string="Order Type",
help="For most banks in France you should use the "
"format neutral Order Types 'FUL' for upload "
"and 'FDL' for download.",
) )
test_mode = fields.Boolean( test_mode = fields.Boolean(
help="Select this option to test if the syntax of " help="Select this option to test if the syntax of "
@ -202,7 +200,19 @@ class EbicsXfer(models.TransientModel):
for df in download_formats: for df in download_formats:
try: try:
success = False success = False
if df.order_type == "FDL": if df.order_type == "BTD":
btf = BusinessTransactionFormat(
df.btf_service,
df.btf_message,
scope=df.btf_scope or None,
option=df.btf_option or None,
container=df.btf_container or None,
version=df.btf_version or None,
variant=df.btf_variant or None,
format=df.btf_format or None,
)
data = client.BTD(btf, start=date_from, end=date_to)
elif df.order_type == "FDL":
data = client.FDL(df.name, date_from, date_to) data = client.FDL(df.name, date_from, date_to)
else: else:
params = None params = None
@ -323,7 +333,7 @@ class EbicsXfer(models.TransientModel):
self.note = "" self.note = ""
client = self._setup_client() client = self._setup_client()
if client: if client:
upload_data = base64.decodestring(self.upload_data) upload_data = base64.decodebytes(self.upload_data)
ef_format = self.format_id ef_format = self.format_id
OrderID = False OrderID = False
try: try: