mirror of
https://github.com/brain-tec/account_ebics.git
synced 2024-11-23 12:42:04 +00:00
Merge pull request #3 from Noviat/16.0
Syncing from upstream Noviat/account_ebics (16.0)
This commit is contained in:
commit
cc75cb0df3
@ -27,8 +27,6 @@ and fintech 7.2.7 or higher for EBICS 3.0.
|
|||||||
|
|
||||||
SWIFT 3SKey support requires fintech 6.4 or higher.
|
SWIFT 3SKey support requires fintech 6.4 or higher.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
|
||||||
|
|
||||||
We also recommend to consider the installation of the following modules:
|
We also recommend to consider the installation of the following modules:
|
||||||
@ -71,9 +69,17 @@ We also recommend to consider the installation of the following modules:
|
|||||||
|
|
||||||
- account_ebics_payment_order
|
- account_ebics_payment_order
|
||||||
|
|
||||||
Recommended if you are using the OCA account_payment_order module.
|
Required if you are using the OCA account_payment_order module.
|
||||||
|
|
||||||
Cf. https://github.com/Noviat/account_ebics and https://github.com/OCA/bank-payment
|
Cf. https://github.com/OCA/bank-payment
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
|
- account_ebics_oca_statement_import
|
||||||
|
|
||||||
|
Required if you are using the OCA Bank Statement import modules.
|
||||||
|
|
||||||
|
https://github.com/OCA/bank-statement-import
|
||||||
|
|
||||||
|
|
|
|
||||||
|
|
||||||
@ -93,27 +99,6 @@ We also recommend to consider the installation of the following modules:
|
|||||||
|
|
||||||
|
|
|
|
||||||
|
|
||||||
- account_statement_import_helper
|
|
||||||
|
|
||||||
Required if you are processing bank statements with local bank account numbers (e.g. french CFONB files)
|
|
||||||
and using import parsers based upon the OCA account_statement_import module.
|
|
||||||
|
|
||||||
The import helper will match the local bank account number with the IBAN number specified on the Odoo Financial journal.
|
|
||||||
|
|
||||||
Cf. https://github.com/Noviat/noviat-apps
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
- account_bank_statement_import_helper
|
|
||||||
|
|
||||||
Required if you are processing bank statements with local bank account numbers
|
|
||||||
and using import parsers based upon the Odoo Enterprise account_bank_statement_import module.
|
|
||||||
|
|
||||||
The import helper will match the local bank account number with the IBAN number specified on the Odoo Financial journal.
|
|
||||||
|
|
||||||
Cf. https://github.com/Noviat/noviat-apps
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
Fintech license
|
Fintech license
|
||||||
---------------
|
---------------
|
||||||
@ -130,13 +115,6 @@ The name of the licensee.
|
|||||||
|
|
||||||
The keycode of the licensed version.
|
The keycode of the licensed version.
|
||||||
|
|
||||||
- fintech_register_users
|
|
||||||
|
|
||||||
The licensed EBICS user ids. It must be a string or a list of user ids.
|
|
||||||
|
|
||||||
You should NOT specify this parameter if your license is subsciption
|
|
||||||
based (with monthly recurring billing).
|
|
||||||
|
|
||||||
|
|
|
|
||||||
| Example:
|
| Example:
|
||||||
|
|
|
|
||||||
@ -146,7 +124,6 @@ based (with monthly recurring billing).
|
|||||||
; fintech
|
; fintech
|
||||||
fintech_register_name = MyCompany
|
fintech_register_name = MyCompany
|
||||||
fintech_register_keycode = AB1CD-E2FG-3H-IJ4K-5L
|
fintech_register_keycode = AB1CD-E2FG-3H-IJ4K-5L
|
||||||
fintech_register_users = USER1, USER2
|
|
||||||
|
|
||||||
|
|
|
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
"name": "EBICS banking protocol",
|
"name": "EBICS banking protocol",
|
||||||
"version": "16.0.1.3.1",
|
"version": "16.0.1.4.0",
|
||||||
"license": "LGPL-3",
|
"license": "LGPL-3",
|
||||||
"author": "Noviat",
|
"author": "Noviat",
|
||||||
"website": "https://www.noviat.com",
|
"website": "https://www.noviat.com",
|
||||||
|
@ -8,3 +8,4 @@ class AccountBankStatement(models.Model):
|
|||||||
_inherit = "account.bank.statement"
|
_inherit = "account.bank.statement"
|
||||||
|
|
||||||
ebics_file_id = fields.Many2one(comodel_name="ebics.file", string="EBICS Data File")
|
ebics_file_id = fields.Many2one(comodel_name="ebics.file", string="EBICS Data File")
|
||||||
|
import_format = fields.Char(readonly=True)
|
||||||
|
@ -91,14 +91,6 @@ class EbicsConfig(models.Model):
|
|||||||
"between customer and financial institution. "
|
"between customer and financial institution. "
|
||||||
"The human user also can authorise orders.",
|
"The human user also can authorise orders.",
|
||||||
)
|
)
|
||||||
ebics_files = fields.Char(
|
|
||||||
string="EBICS Files Root",
|
|
||||||
required=True,
|
|
||||||
readonly=True,
|
|
||||||
states={"draft": [("readonly", False)]},
|
|
||||||
default=lambda self: self._default_ebics_files(),
|
|
||||||
help="Root Directory for EBICS File Transfer Folders.",
|
|
||||||
)
|
|
||||||
# We store the EBICS keys in a separate directory in the file system.
|
# We store the EBICS keys in a separate directory in the file system.
|
||||||
# This directory requires special protection to reduce fraude.
|
# This directory requires special protection to reduce fraude.
|
||||||
ebics_keys = fields.Char(
|
ebics_keys = fields.Char(
|
||||||
@ -253,14 +245,3 @@ class EbicsConfig(models.Model):
|
|||||||
)
|
)
|
||||||
% dirname
|
% dirname
|
||||||
)
|
)
|
||||||
|
|
||||||
def _check_ebics_files(self):
|
|
||||||
dirname = self.ebics_files or ""
|
|
||||||
if not os.path.exists(dirname):
|
|
||||||
raise UserError(
|
|
||||||
_(
|
|
||||||
"EBICS Files Root Directory %s is not available."
|
|
||||||
"\nPlease contact your system administrator."
|
|
||||||
)
|
|
||||||
% dirname
|
|
||||||
)
|
|
||||||
|
@ -11,12 +11,13 @@ from lxml import etree
|
|||||||
|
|
||||||
from odoo import _, fields, models
|
from odoo import _, fields, models
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
from odoo.tools.safe_eval import safe_eval
|
|
||||||
|
|
||||||
from odoo.addons.base.models.res_bank import sanitize_account_number
|
from odoo.addons.base.models.res_bank import sanitize_account_number
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
DUP_CHECK_FORMATS = ["cfonb120", "camt053"]
|
||||||
|
|
||||||
|
|
||||||
class EbicsFile(models.Model):
|
class EbicsFile(models.Model):
|
||||||
_name = "ebics.file"
|
_name = "ebics.file"
|
||||||
@ -177,31 +178,95 @@ class EbicsFile(models.Model):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _process_download_result(self, res):
|
def _lookup_journal(self, res, acc_number, currency_code):
|
||||||
|
currency = self.env["res.currency"].search(
|
||||||
|
[("name", "=ilike", currency_code)], limit=1
|
||||||
|
)
|
||||||
|
journal = self.env["account.journal"]
|
||||||
|
if not currency:
|
||||||
|
message = _("Currency %(cc)s not found.", cc=currency_code)
|
||||||
|
res["notifications"].append({"type": "error", "message": message})
|
||||||
|
return (currency, journal)
|
||||||
|
|
||||||
|
journals = self.env["account.journal"].search(
|
||||||
|
[
|
||||||
|
("type", "=", "bank"),
|
||||||
|
(
|
||||||
|
"bank_account_id.sanitized_acc_number",
|
||||||
|
"ilike",
|
||||||
|
acc_number,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if not journals:
|
||||||
|
message = _(
|
||||||
|
"No financial journal found for Account Number %(nbr)s, "
|
||||||
|
"Currency %(cc)s",
|
||||||
|
nbr=acc_number,
|
||||||
|
cc=currency_code,
|
||||||
|
)
|
||||||
|
res["notifications"].append({"type": "error", "message": message})
|
||||||
|
return (currency, journal)
|
||||||
|
|
||||||
|
for jrnl in journals:
|
||||||
|
journal_currency = jrnl.currency_id or jrnl.company_id.currency_id
|
||||||
|
if journal_currency != currency:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
journal = jrnl
|
||||||
|
break
|
||||||
|
|
||||||
|
if not journal:
|
||||||
|
message = _(
|
||||||
|
"No financial journal found for Account Number %(nbr)s, "
|
||||||
|
"Currency %(cc)s",
|
||||||
|
nbr=acc_number,
|
||||||
|
cc=currency_code,
|
||||||
|
)
|
||||||
|
res["notifications"].append({"type": "error", "message": message})
|
||||||
|
return (currency, journal)
|
||||||
|
|
||||||
|
def _process_download_result(self, res, file_format=None):
|
||||||
|
"""
|
||||||
|
We perform a duplicate statement check after the creation of the bank
|
||||||
|
statements since we rely on Odoo Enterprise or OCA modules for the
|
||||||
|
bank statement creation.
|
||||||
|
From a development standpoint (code creation/maintenance) a check after
|
||||||
|
creation is the easiest way.
|
||||||
|
"""
|
||||||
statement_ids = res["statement_ids"]
|
statement_ids = res["statement_ids"]
|
||||||
notifications = res["notifications"]
|
notifications = res["notifications"]
|
||||||
statements = self.env["account.bank.statement"].sudo().browse(statement_ids)
|
statements = self.env["account.bank.statement"].sudo().browse(statement_ids)
|
||||||
st_cnt = len(statement_ids)
|
if statements:
|
||||||
|
statements.write({"import_format": file_format})
|
||||||
|
statements = self._statement_duplicate_check(res, statements)
|
||||||
|
st_cnt = len(statements)
|
||||||
warning_cnt = error_cnt = 0
|
warning_cnt = error_cnt = 0
|
||||||
if notifications:
|
if notifications:
|
||||||
|
errors = []
|
||||||
|
warnings = []
|
||||||
for notif in notifications:
|
for notif in notifications:
|
||||||
if notif["type"] == "error":
|
if notif["type"] == "error":
|
||||||
error_cnt += 1
|
error_cnt += 1
|
||||||
|
parts = [notif[k] for k in notif if k in ("message", "details")]
|
||||||
|
errors.append("\n".join(parts))
|
||||||
elif notif["type"] == "warning":
|
elif notif["type"] == "warning":
|
||||||
warning_cnt += 1
|
warning_cnt += 1
|
||||||
parts = [notif[k] for k in notif if k in ("message", "details")]
|
parts = [notif[k] for k in notif if k in ("message", "details")]
|
||||||
self.note_process += "\n".join(parts)
|
warnings.append("\n".join(parts))
|
||||||
self.note_process += "\n\n"
|
|
||||||
self.note_process += "\n"
|
self.note_process += _("Process file %(fn)s results:", fn=self.name)
|
||||||
if error_cnt:
|
if error_cnt:
|
||||||
self.note_process += (
|
self.note_process += "\n\n" + _("Errors") + ":\n"
|
||||||
_("Number of errors detected during import: %s") % error_cnt
|
self.note_process += "\n".join(errors)
|
||||||
)
|
self.note_process += "\n\n"
|
||||||
self.note_process += "\n"
|
self.note_process += _("Number of errors: %(nr)s", nr=error_cnt)
|
||||||
if warning_cnt:
|
if warning_cnt:
|
||||||
self.note_process += (
|
self.note_process += "\n\n" + _("Warnings") + ":\n"
|
||||||
_("Number of warnings detected during import: %s") % warning_cnt
|
self.note_process += "\n".join(warnings)
|
||||||
)
|
self.note_process += "\n\n"
|
||||||
|
self.note_process += _("Number of warnings: %(nr)s", nr=warning_cnt)
|
||||||
|
self.note_process += "\n"
|
||||||
if st_cnt:
|
if st_cnt:
|
||||||
self.note_process += "\n\n"
|
self.note_process += "\n\n"
|
||||||
self.note_process += _(
|
self.note_process += _(
|
||||||
@ -211,16 +276,17 @@ class EbicsFile(models.Model):
|
|||||||
)
|
)
|
||||||
self.note_process += "\n"
|
self.note_process += "\n"
|
||||||
for statement in statements:
|
for statement in statements:
|
||||||
self.note_process += ("\n%s, %s (%s)") % (
|
self.note_process += "\n" + _(
|
||||||
statement.date,
|
"Statement %(st)s dated %(date)s (Company: %(cpy)s)",
|
||||||
statement.name,
|
st=statement.name,
|
||||||
statement.company_id.name,
|
date=statement.date,
|
||||||
|
cpy=statement.company_id.name,
|
||||||
)
|
)
|
||||||
if statement_ids:
|
if statements:
|
||||||
self.sudo().bank_statement_ids = [(4, x) for x in statement_ids]
|
self.sudo().bank_statement_ids = [(4, x) for x in statements.ids]
|
||||||
company_ids = self.sudo().bank_statement_ids.mapped("company_id").ids
|
company_ids = self.sudo().bank_statement_ids.mapped("company_id").ids
|
||||||
self.company_ids = [(6, 0, company_ids)]
|
self.company_ids = [(6, 0, company_ids)]
|
||||||
ctx = dict(self.env.context, statement_ids=statement_ids)
|
ctx = dict(self.env.context, statement_ids=statements.ids)
|
||||||
module = __name__.split("addons.")[1].split(".")[0]
|
module = __name__.split("addons.")[1].split(".")[0]
|
||||||
result_view = self.env.ref("%s.ebics_file_view_form_result" % module)
|
result_view = self.env.ref("%s.ebics_file_view_form_result" % module)
|
||||||
return {
|
return {
|
||||||
@ -235,104 +301,47 @@ class EbicsFile(models.Model):
|
|||||||
"type": "ir.actions.act_window",
|
"type": "ir.actions.act_window",
|
||||||
}
|
}
|
||||||
|
|
||||||
def _process_cfonb120(self):
|
def _statement_duplicate_check(self, res, statements):
|
||||||
"""
|
"""
|
||||||
Disable this code while waiting on OCA cfonb release for 16.0
|
This check is required for import modules that do not
|
||||||
|
set the 'unique_import_id' on the statement lines.
|
||||||
|
E.g. OCA camt import
|
||||||
"""
|
"""
|
||||||
# pylint: disable=W0101
|
to_unlink = self.env["account.bank.statement"]
|
||||||
raise NotImplementedError
|
for statement in statements.filtered(
|
||||||
|
lambda r: r.import_format in DUP_CHECK_FORMATS
|
||||||
|
):
|
||||||
|
dup = self.env["account.bank.statement"].search_count(
|
||||||
|
[
|
||||||
|
("id", "!=", statement.id),
|
||||||
|
("name", "=", statement.name),
|
||||||
|
("company_id", "=", statement.company_id.id),
|
||||||
|
("date", "=", statement.date),
|
||||||
|
("import_format", "=", statement.import_format),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if dup:
|
||||||
|
message = _(
|
||||||
|
"Statement %(st_name)s dated %(date)s has already been imported.",
|
||||||
|
st_name=statement.name,
|
||||||
|
date=statement.date,
|
||||||
|
)
|
||||||
|
res["notifications"].append({"type": "warning", "message": message})
|
||||||
|
to_unlink += statement
|
||||||
|
res["statement_ids"] = [
|
||||||
|
x for x in res["statement_ids"] if x not in to_unlink.ids
|
||||||
|
]
|
||||||
|
statements -= to_unlink
|
||||||
|
to_unlink.unlink()
|
||||||
|
return statements
|
||||||
|
|
||||||
|
def _process_cfonb120(self):
|
||||||
import_module = "account_statement_import_fr_cfonb"
|
import_module = "account_statement_import_fr_cfonb"
|
||||||
self._check_import_module(import_module)
|
self._check_import_module(import_module)
|
||||||
wiz_model = "account.statement.import"
|
res = {"statement_ids": [], "notifications": []}
|
||||||
data_file = base64.b64decode(self.data)
|
st_datas = self._split_cfonb(res)
|
||||||
lines = data_file.split(b"\n")
|
self._process_bank_statement_oca(res, st_datas)
|
||||||
wiz_vals_list = []
|
return self._process_download_result(res, file_format="cfonb120")
|
||||||
st_lines = b""
|
|
||||||
transactions = False
|
|
||||||
for line in lines:
|
|
||||||
rec_type = line[0:2]
|
|
||||||
acc_number = line[21:32]
|
|
||||||
st_lines += line + b"\n"
|
|
||||||
if rec_type == b"04":
|
|
||||||
transactions = True
|
|
||||||
if rec_type == b"07":
|
|
||||||
if transactions:
|
|
||||||
fn = "_".join([acc_number.decode(), self.name])
|
|
||||||
wiz_vals_list.append(
|
|
||||||
{
|
|
||||||
"statement_filename": fn,
|
|
||||||
"statement_file": base64.b64encode(st_lines),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
st_lines = b""
|
|
||||||
transactions = False
|
|
||||||
result_action = self.env["ir.actions.act_window"]._for_xml_id(
|
|
||||||
"account.action_bank_statement_tree"
|
|
||||||
)
|
|
||||||
result_action["context"] = safe_eval(result_action["context"])
|
|
||||||
statement_ids = []
|
|
||||||
notifications = []
|
|
||||||
for i, wiz_vals in enumerate(wiz_vals_list, start=1):
|
|
||||||
result = {
|
|
||||||
"statement_ids": [],
|
|
||||||
"notifications": [],
|
|
||||||
}
|
|
||||||
statement_filename = wiz_vals["statement_filename"]
|
|
||||||
wiz = (
|
|
||||||
self.env[wiz_model]
|
|
||||||
.with_context(active_model="ebics.file")
|
|
||||||
.create(wiz_vals)
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
with self.env.cr.savepoint():
|
|
||||||
file_data = base64.b64decode(wiz_vals["statement_file"])
|
|
||||||
msg_hdr = _(
|
|
||||||
"{} : Import failed for statement number %(index)s, filename %(fn)s:\n",
|
|
||||||
index=i,
|
|
||||||
fn=statement_filename,
|
|
||||||
)
|
|
||||||
wiz.import_single_file(file_data, result)
|
|
||||||
if not result["statement_ids"]:
|
|
||||||
message = msg_hdr.format(_("Warning"))
|
|
||||||
message += _(
|
|
||||||
"You have already imported this file, or this file "
|
|
||||||
"only contains already imported transactions."
|
|
||||||
)
|
|
||||||
notifications += [
|
|
||||||
{
|
|
||||||
"type": "warning",
|
|
||||||
"message": message,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
statement_ids.extend(result["statement_ids"])
|
|
||||||
notifications.extend(result["notifications"])
|
|
||||||
|
|
||||||
except UserError as e:
|
|
||||||
message = msg_hdr.format(_("Error"))
|
|
||||||
message += "".join(e.args)
|
|
||||||
notifications += [
|
|
||||||
{
|
|
||||||
"type": "error",
|
|
||||||
"message": message,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
except Exception:
|
|
||||||
tb = "".join(format_exception(*exc_info()))
|
|
||||||
message = msg_hdr.format(_("Error"))
|
|
||||||
message += tb
|
|
||||||
notifications += [
|
|
||||||
{
|
|
||||||
"type": "error",
|
|
||||||
"message": message,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
result_action["context"]["notifications"] = notifications
|
|
||||||
result_action["domain"] = [("id", "in", statement_ids)]
|
|
||||||
return self._process_result_action(result_action)
|
|
||||||
|
|
||||||
def _unlink_cfonb120(self):
|
def _unlink_cfonb120(self):
|
||||||
"""
|
"""
|
||||||
@ -340,10 +349,45 @@ class EbicsFile(models.Model):
|
|||||||
EBICS data file and its related bank statements.
|
EBICS data file and its related bank statements.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def _split_cfonb(self, res):
|
||||||
|
"""
|
||||||
|
Split CFONB file received via EBICS per statement.
|
||||||
|
Statements without transactions are removed.
|
||||||
|
"""
|
||||||
|
datas = []
|
||||||
|
file_data = base64.b64decode(self.data)
|
||||||
|
lines = file_data.split(b"\n")
|
||||||
|
st_lines = b""
|
||||||
|
transactions = False
|
||||||
|
for line in lines:
|
||||||
|
rec_type = line[0:2]
|
||||||
|
currency_code = line[16:19].decode()
|
||||||
|
acc_number = line[21:32].decode()
|
||||||
|
st_lines += line + b"\n"
|
||||||
|
if rec_type == b"04":
|
||||||
|
transactions = True
|
||||||
|
if rec_type == b"07":
|
||||||
|
if transactions:
|
||||||
|
currency, journal = self._lookup_journal(
|
||||||
|
res, acc_number, currency_code
|
||||||
|
)
|
||||||
|
if currency and journal:
|
||||||
|
datas.append(
|
||||||
|
{
|
||||||
|
"acc_number": acc_number,
|
||||||
|
"journal_id": journal.id,
|
||||||
|
"company_id": journal.company_id.id,
|
||||||
|
"data": base64.b64encode(st_lines),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
st_lines = b""
|
||||||
|
transactions = False
|
||||||
|
return datas
|
||||||
|
|
||||||
def _process_camt052(self):
|
def _process_camt052(self):
|
||||||
import_module = "account_statement_import_camt"
|
import_module = "account_statement_import_camt"
|
||||||
self._check_import_module(import_module)
|
self._check_import_module(import_module)
|
||||||
return self._process_camt053(self)
|
return self._process_camt053(file_format="camt052")
|
||||||
|
|
||||||
def _unlink_camt052(self):
|
def _unlink_camt052(self):
|
||||||
"""
|
"""
|
||||||
@ -354,7 +398,7 @@ class EbicsFile(models.Model):
|
|||||||
def _process_camt054(self):
|
def _process_camt054(self):
|
||||||
import_module = "account_statement_import_camt"
|
import_module = "account_statement_import_camt"
|
||||||
self._check_import_module(import_module)
|
self._check_import_module(import_module)
|
||||||
return self._process_camt053(self)
|
return self._process_camt053(file_format="camt054")
|
||||||
|
|
||||||
def _unlink_camt054(self):
|
def _unlink_camt054(self):
|
||||||
"""
|
"""
|
||||||
@ -362,7 +406,7 @@ class EbicsFile(models.Model):
|
|||||||
EBICS data file and its related bank statements.
|
EBICS data file and its related bank statements.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _process_camt053(self):
|
def _process_camt053(self, file_format=None):
|
||||||
"""
|
"""
|
||||||
The Odoo standard statement import is based on manual selection
|
The Odoo standard statement import is based on manual selection
|
||||||
of a financial journal before importing the electronic statement file.
|
of a financial journal before importing the electronic statement file.
|
||||||
@ -394,28 +438,26 @@ class EbicsFile(models.Model):
|
|||||||
res = {"statement_ids": [], "notifications": []}
|
res = {"statement_ids": [], "notifications": []}
|
||||||
st_datas = self._split_camt(res)
|
st_datas = self._split_camt(res)
|
||||||
if author == "oca":
|
if author == "oca":
|
||||||
self._process_camt053_oca(res, st_datas)
|
self._process_bank_statement_oca(res, st_datas)
|
||||||
else:
|
else:
|
||||||
self._process_camt053_oe(res, st_datas)
|
self._process_bank_statement_oe(res, st_datas)
|
||||||
return self._process_download_result(res)
|
file_format = file_format or "camt053"
|
||||||
|
return self._process_download_result(res, file_format=file_format)
|
||||||
|
|
||||||
def _process_camt053_oca(self, res, st_datas):
|
def _process_bank_statement_oca(self, res, st_datas):
|
||||||
msg_hdr = _("{} : Import failed for file %(fn)s:\n", fn=self.name)
|
|
||||||
for st_data in st_datas:
|
for st_data in st_datas:
|
||||||
try:
|
try:
|
||||||
with self.env.cr.savepoint():
|
with self.env.cr.savepoint():
|
||||||
self._create_statement_camt053_oca(res, st_data)
|
self._create_bank_statement_oca(res, st_data)
|
||||||
except UserError as e:
|
except UserError as e:
|
||||||
message = msg_hdr.format(_("Error"))
|
res["notifications"].append(
|
||||||
message += "".join(e.args)
|
{"type": "error", "message": "".join(e.args)}
|
||||||
res["notifications"].append({"type": "error", "message": message})
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
tb = "".join(format_exception(*exc_info()))
|
tb = "".join(format_exception(*exc_info()))
|
||||||
message = msg_hdr.format(_("Error"))
|
res["notifications"].append({"type": "error", "message": tb})
|
||||||
message += tb
|
|
||||||
res["notifications"].append({"type": "error", "message": message})
|
|
||||||
|
|
||||||
def _create_statement_camt053_oca(self, res, st_data):
|
def _create_bank_statement_oca(self, res, st_data):
|
||||||
wiz = (
|
wiz = (
|
||||||
self.env["account.statement.import"]
|
self.env["account.statement.import"]
|
||||||
.with_company(st_data["company_id"])
|
.with_company(st_data["company_id"])
|
||||||
@ -424,27 +466,28 @@ class EbicsFile(models.Model):
|
|||||||
)
|
)
|
||||||
wiz.import_single_file(base64.b64decode(st_data["data"]), res)
|
wiz.import_single_file(base64.b64decode(st_data["data"]), res)
|
||||||
|
|
||||||
def _process_camt053_oe(self, res, st_datas):
|
def _process_bank_statement_oe(self, res, st_datas):
|
||||||
"""
|
"""
|
||||||
We execute a cr.commit() after every statement import since we get a
|
We execute a cr.commit() after every statement import since we get a
|
||||||
'savepoint does not exist' error when using 'with self.env.cr.savepoint()'.
|
'savepoint does not exist' error when using 'with self.env.cr.savepoint()'.
|
||||||
"""
|
"""
|
||||||
msg_hdr = _("{} : Import failed for file %(fn)s:\n", fn=self.name)
|
|
||||||
for st_data in st_datas:
|
for st_data in st_datas:
|
||||||
try:
|
try:
|
||||||
self._create_statement_camt053_oe(res, st_data)
|
self._create_bank_statement_oe(res, st_data)
|
||||||
self.env.cr.commit() # pylint: disable=E8102
|
self.env.cr.commit() # pylint: disable=E8102
|
||||||
except UserError as e:
|
except UserError as e:
|
||||||
message = msg_hdr.format(_("Error"))
|
msg = "".join(e.args)
|
||||||
message += "".join(e.args)
|
msg += "\n"
|
||||||
res["notifications"].append({"type": "error", "message": message})
|
msg += _(
|
||||||
|
"Statement for Account Number %(nr)s has not been processed.",
|
||||||
|
nr=st_data["acc_number"],
|
||||||
|
)
|
||||||
|
res["notifications"].append({"type": "error", "message": msg})
|
||||||
except Exception:
|
except Exception:
|
||||||
tb = "".join(format_exception(*exc_info()))
|
tb = "".join(format_exception(*exc_info()))
|
||||||
message = msg_hdr.format(_("Error"))
|
res["notifications"].append({"type": "error", "message": tb})
|
||||||
message += tb
|
|
||||||
res["notifications"].append({"type": "error", "message": message})
|
|
||||||
|
|
||||||
def _create_statement_camt053_oe(self, res, st_data):
|
def _create_bank_statement_oe(self, res, st_data):
|
||||||
attachment = (
|
attachment = (
|
||||||
self.env["ir.attachment"]
|
self.env["ir.attachment"]
|
||||||
.with_company(st_data["company_id"])
|
.with_company(st_data["company_id"])
|
||||||
@ -486,20 +529,14 @@ class EbicsFile(models.Model):
|
|||||||
Statements without transactions are removed.
|
Statements without transactions are removed.
|
||||||
"""
|
"""
|
||||||
datas = []
|
datas = []
|
||||||
msg_hdr = _("{} : Import failed for file %(fn)s:\n", fn=self.name)
|
|
||||||
file_data = base64.b64decode(self.data)
|
file_data = base64.b64decode(self.data)
|
||||||
root = etree.fromstring(file_data, parser=etree.XMLParser(recover=True))
|
root = etree.fromstring(file_data, parser=etree.XMLParser(recover=True))
|
||||||
if root is None:
|
if root is None:
|
||||||
message = msg_hdr.format(_("Error"))
|
message = _("Invalid XML file.")
|
||||||
message += _("Invalid XML file.")
|
|
||||||
res["notifications"].append({"type": "error", "message": message})
|
res["notifications"].append({"type": "error", "message": message})
|
||||||
ns = {k or "ns": v for k, v in root.nsmap.items()}
|
ns = {k or "ns": v for k, v in root.nsmap.items()}
|
||||||
for i, stmt in enumerate(root[0].findall("ns:Stmt", ns), start=1):
|
stmts = root[0].findall("ns:Stmt", ns)
|
||||||
msg_hdr = _(
|
for i, stmt in enumerate(stmts):
|
||||||
"{} : Import failed for statement number %(index)s, filename %(fn)s:\n",
|
|
||||||
index=i,
|
|
||||||
fn=self.name,
|
|
||||||
)
|
|
||||||
acc_number = sanitize_account_number(
|
acc_number = sanitize_account_number(
|
||||||
stmt.xpath(
|
stmt.xpath(
|
||||||
"ns:Acct/ns:Id/ns:IBAN/text() | ns:Acct/ns:Id/ns:Othr/ns:Id/text()",
|
"ns:Acct/ns:Id/ns:IBAN/text() | ns:Acct/ns:Id/ns:Othr/ns:Id/text()",
|
||||||
@ -507,72 +544,34 @@ class EbicsFile(models.Model):
|
|||||||
)[0]
|
)[0]
|
||||||
)
|
)
|
||||||
if not acc_number:
|
if not acc_number:
|
||||||
message = msg_hdr.format(_("Error"))
|
message = _("No bank account number found.")
|
||||||
message += _("No bank account number found.")
|
|
||||||
res["notifications"].append({"type": "error", "message": message})
|
res["notifications"].append({"type": "error", "message": message})
|
||||||
continue
|
continue
|
||||||
currency_code = stmt.xpath(
|
currency_code = stmt.xpath(
|
||||||
"ns:Acct/ns:Ccy/text() | ns:Bal/ns:Amt/@Ccy", namespaces=ns
|
"ns:Acct/ns:Ccy/text() | ns:Bal/ns:Amt/@Ccy", namespaces=ns
|
||||||
)[0]
|
)[0]
|
||||||
currency = self.env["res.currency"].search(
|
|
||||||
[("name", "=ilike", currency_code)], limit=1
|
|
||||||
)
|
|
||||||
if not currency:
|
|
||||||
message = msg_hdr.format(_("Error"))
|
|
||||||
message += _("Currency %(cc)s not found.", cc=currency_code)
|
|
||||||
res["notifications"].append({"type": "error", "message": message})
|
|
||||||
continue
|
|
||||||
journal = self.env["account.journal"].search(
|
|
||||||
[
|
|
||||||
("type", "=", "bank"),
|
|
||||||
(
|
|
||||||
"bank_account_id.sanitized_acc_number",
|
|
||||||
"ilike",
|
|
||||||
acc_number,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
if not journal:
|
|
||||||
message = msg_hdr.format(_("Error"))
|
|
||||||
message += _(
|
|
||||||
"No financial journal found for Account Number %(nbr)s, "
|
|
||||||
"Currency %(cc)s",
|
|
||||||
nbr=acc_number,
|
|
||||||
cc=currency_code,
|
|
||||||
)
|
|
||||||
res["notifications"].append({"type": "error", "message": message})
|
|
||||||
continue
|
|
||||||
|
|
||||||
journal_currency = journal.currency_id or journal.company_id.currency_id
|
|
||||||
if journal_currency != currency:
|
|
||||||
message = msg_hdr.format(_("Error"))
|
|
||||||
message += _(
|
|
||||||
"No financial journal found for Account Number %(nbr)s, "
|
|
||||||
"Currency %(cc)s",
|
|
||||||
nbr=acc_number,
|
|
||||||
cc=currency_code,
|
|
||||||
)
|
|
||||||
res["notifications"].append({"type": "error", "message": message})
|
|
||||||
continue
|
|
||||||
|
|
||||||
root_new = deepcopy(root)
|
root_new = deepcopy(root)
|
||||||
entries = False
|
entries = False
|
||||||
for j, el in enumerate(root_new[0].findall("ns:Stmt", ns), start=1):
|
for j, el in enumerate(root_new[0].findall("ns:Stmt", ns)):
|
||||||
if j != i:
|
if j != i:
|
||||||
el.getparent().remove(el)
|
el.getparent().remove(el)
|
||||||
else:
|
else:
|
||||||
entries = el.findall("ns:Ntry", ns)
|
entries = el.findall("ns:Ntry", ns)
|
||||||
if not entries:
|
if not entries:
|
||||||
continue
|
continue
|
||||||
|
else:
|
||||||
datas.append(
|
currency, journal = self._lookup_journal(res, acc_number, currency_code)
|
||||||
{
|
if not (currency and journal):
|
||||||
"acc_number": acc_number,
|
continue
|
||||||
"journal_id": journal.id,
|
datas.append(
|
||||||
"company_id": journal.company_id.id,
|
{
|
||||||
"data": base64.b64encode(etree.tostring(root_new)),
|
"acc_number": acc_number,
|
||||||
}
|
"journal_id": journal.id,
|
||||||
)
|
"company_id": journal.company_id.id,
|
||||||
|
"data": base64.b64encode(etree.tostring(root_new)),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return datas
|
return datas
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ class EbicsUserID(models.Model):
|
|||||||
"by means of the SWIFT 3SKey token.",
|
"by means of the SWIFT 3SKey token.",
|
||||||
)
|
)
|
||||||
swift_3skey_certificate = fields.Binary(string="3SKey Certficate")
|
swift_3skey_certificate = fields.Binary(string="3SKey Certficate")
|
||||||
swift_3skey_certificate_fn = fields.Char(string="EBICS certificate name")
|
swift_3skey_certificate_fn = fields.Char(string="3SKey Certificate Filename")
|
||||||
# X.509 Distinguished Name attributes used to
|
# X.509 Distinguished Name attributes used to
|
||||||
# create self-signed X.509 certificates
|
# create self-signed X.509 certificates
|
||||||
ebics_key_x509 = fields.Boolean(
|
ebics_key_x509 = fields.Boolean(
|
||||||
@ -255,7 +255,6 @@ class EbicsUserID(models.Model):
|
|||||||
Create new keys and certificates for this user
|
Create new keys and certificates for this user
|
||||||
"""
|
"""
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
self.ebics_config_id._check_ebics_files()
|
|
||||||
if self.state != "draft":
|
if self.state != "draft":
|
||||||
raise UserError(
|
raise UserError(
|
||||||
_("Set state to 'draft' before Bank Key (re)initialisation.")
|
_("Set state to 'draft' before Bank Key (re)initialisation.")
|
||||||
@ -442,7 +441,6 @@ class EbicsUserID(models.Model):
|
|||||||
must be downloaded and checked for consistency.
|
must be downloaded and checked for consistency.
|
||||||
"""
|
"""
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
self.ebics_config_id._check_ebics_files()
|
|
||||||
if self.state != "get_bank_keys":
|
if self.state != "get_bank_keys":
|
||||||
raise UserError(_("Set state to 'Get Keys from Bank'."))
|
raise UserError(_("Set state to 'Get Keys from Bank'."))
|
||||||
try:
|
try:
|
||||||
|
@ -429,8 +429,17 @@ which allows to see all statements downloaded via the ir.cron automated EBICS do
|
|||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li><p class="first">account_ebics_payment_order</p>
|
<li><p class="first">account_ebics_payment_order</p>
|
||||||
<p>Recommended if you are using the OCA account_payment_order module.</p>
|
<p>Required if you are using the OCA account_payment_order module.</p>
|
||||||
<p>Cf. <a class="reference external" href="https://github.com/Noviat/account_ebics">https://github.com/Noviat/account_ebics</a> and <a class="reference external" href="https://github.com/OCA/bank-payment">https://github.com/OCA/bank-payment</a></p>
|
<p>Cf. <a class="reference external" href="https://github.com/OCA/bank-payment">https://github.com/OCA/bank-payment</a></p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="line-block">
|
||||||
|
<div class="line"><br /></div>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li><p class="first">account_ebics_oca_statement_import</p>
|
||||||
|
<p>Required if you are using the OCA Bank Statement import modules.</p>
|
||||||
|
<p><a class="reference external" href="https://github.com/OCA/bank-statement-import">https://github.com/OCA/bank-statement-import</a></p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="line-block">
|
<div class="line-block">
|
||||||
@ -454,28 +463,6 @@ which allows to see all statements downloaded via the ir.cron automated EBICS do
|
|||||||
<div class="line-block">
|
<div class="line-block">
|
||||||
<div class="line"><br /></div>
|
<div class="line"><br /></div>
|
||||||
</div>
|
</div>
|
||||||
<ul>
|
|
||||||
<li><p class="first">account_statement_import_helper</p>
|
|
||||||
<p>Required if you are processing bank statements with local bank account numbers (e.g. french CFONB files)
|
|
||||||
and using import parsers based upon the OCA account_statement_import module.</p>
|
|
||||||
<p>The import helper will match the local bank account number with the IBAN number specified on the Odoo Financial journal.</p>
|
|
||||||
<p>Cf. <a class="reference external" href="https://github.com/Noviat/noviat-apps">https://github.com/Noviat/noviat-apps</a></p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="line-block">
|
|
||||||
<div class="line"><br /></div>
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><p class="first">account_bank_statement_import_helper</p>
|
|
||||||
<p>Required if you are processing bank statements with local bank account numbers
|
|
||||||
and using import parsers based upon the Odoo Enterprise account_bank_statement_import module.</p>
|
|
||||||
<p>The import helper will match the local bank account number with the IBAN number specified on the Odoo Financial journal.</p>
|
|
||||||
<p>Cf. <a class="reference external" href="https://github.com/Noviat/noviat-apps">https://github.com/Noviat/noviat-apps</a></p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="line-block">
|
|
||||||
<div class="line"><br /></div>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="fintech-license">
|
<div class="section" id="fintech-license">
|
||||||
<h3>Fintech license</h3>
|
<h3>Fintech license</h3>
|
||||||
<p>If you have a valid Fintech.ebics license, you should add the following
|
<p>If you have a valid Fintech.ebics license, you should add the following
|
||||||
@ -488,12 +475,6 @@ licensing parameters to the odoo server configuration file:</p>
|
|||||||
<li>fintech_register_keycode</li>
|
<li>fintech_register_keycode</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>The keycode of the licensed version.</p>
|
<p>The keycode of the licensed version.</p>
|
||||||
<ul class="simple">
|
|
||||||
<li>fintech_register_users</li>
|
|
||||||
</ul>
|
|
||||||
<p>The licensed EBICS user ids. It must be a string or a list of user ids.</p>
|
|
||||||
<p>You should NOT specify this parameter if your license is subsciption
|
|
||||||
based (with monthly recurring billing).</p>
|
|
||||||
<div class="line-block">
|
<div class="line-block">
|
||||||
<div class="line"><br /></div>
|
<div class="line"><br /></div>
|
||||||
<div class="line">Example:</div>
|
<div class="line">Example:</div>
|
||||||
@ -503,7 +484,6 @@ based (with monthly recurring billing).</p>
|
|||||||
; fintech
|
; fintech
|
||||||
fintech_register_name = MyCompany
|
fintech_register_name = MyCompany
|
||||||
fintech_register_keycode = AB1CD-E2FG-3H-IJ4K-5L
|
fintech_register_keycode = AB1CD-E2FG-3H-IJ4K-5L
|
||||||
fintech_register_users = USER1, USER2
|
|
||||||
</pre>
|
</pre>
|
||||||
<div class="line-block">
|
<div class="line-block">
|
||||||
<div class="line"><br /></div>
|
<div class="line"><br /></div>
|
||||||
|
@ -52,7 +52,6 @@
|
|||||||
<field name="ebics_host" />
|
<field name="ebics_host" />
|
||||||
<field name="ebics_url" />
|
<field name="ebics_url" />
|
||||||
<field name="ebics_partner" />
|
<field name="ebics_partner" />
|
||||||
<field name="ebics_files" />
|
|
||||||
<field name="ebics_keys" />
|
<field name="ebics_keys" />
|
||||||
</group>
|
</group>
|
||||||
<group name="main-right">
|
<group name="main-right">
|
||||||
|
@ -27,7 +27,6 @@ class EbicsAdminOrder(models.TransientModel):
|
|||||||
|
|
||||||
def ebics_admin_order(self):
|
def ebics_admin_order(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
self.ebics_config_id._check_ebics_files()
|
|
||||||
client = self._setup_client()
|
client = self._setup_client()
|
||||||
if not client:
|
if not client:
|
||||||
self.note += (
|
self.note += (
|
||||||
|
@ -10,7 +10,6 @@ logging.basicConfig(
|
|||||||
|
|
||||||
import base64
|
import base64
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
from sys import exc_info
|
from sys import exc_info
|
||||||
from traceback import format_exception
|
from traceback import format_exception
|
||||||
|
|
||||||
@ -71,9 +70,9 @@ class EbicsXfer(models.TransientModel):
|
|||||||
date_from = fields.Date()
|
date_from = fields.Date()
|
||||||
date_to = fields.Date()
|
date_to = fields.Date()
|
||||||
upload_data = fields.Binary(string="File to Upload")
|
upload_data = fields.Binary(string="File to Upload")
|
||||||
upload_fname = fields.Char(default="")
|
upload_fname = fields.Char(string="Upload Filename", default="")
|
||||||
upload_fname_dummy = fields.Char(
|
upload_fname_dummy = fields.Char(
|
||||||
related="upload_fname", string="Upload Filename", readonly=True
|
related="upload_fname", string="Dummy Upload Filename", readonly=True
|
||||||
)
|
)
|
||||||
format_id = fields.Many2one(
|
format_id = fields.Many2one(
|
||||||
comodel_name="ebics.file.format",
|
comodel_name="ebics.file.format",
|
||||||
@ -193,7 +192,6 @@ class EbicsXfer(models.TransientModel):
|
|||||||
|
|
||||||
def ebics_download(self):
|
def ebics_download(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
self.ebics_config_id._check_ebics_files()
|
|
||||||
ctx = self.env.context.copy()
|
ctx = self.env.context.copy()
|
||||||
self.note = ""
|
self.note = ""
|
||||||
err_cnt = 0
|
err_cnt = 0
|
||||||
@ -532,43 +530,17 @@ class EbicsXfer(models.TransientModel):
|
|||||||
return ebics_files
|
return ebics_files
|
||||||
|
|
||||||
def _create_ebics_file(self, data, file_format, docname=None):
|
def _create_ebics_file(self, data, file_format, docname=None):
|
||||||
"""
|
|
||||||
Write the data as received over the EBICS connection
|
|
||||||
to a temporary file so that is is available for
|
|
||||||
analysis (e.g. in case formats are received that cannot
|
|
||||||
be handled in the current version of this module).
|
|
||||||
|
|
||||||
TODO: add code to clean-up /tmp on a regular basis.
|
|
||||||
|
|
||||||
After saving the data received we call the method to perform
|
|
||||||
file format specific processing.
|
|
||||||
"""
|
|
||||||
ebics_files_root = self.ebics_config_id.ebics_files
|
|
||||||
tmp_dir = os.path.normpath(ebics_files_root + "/tmp")
|
|
||||||
if not os.path.isdir(tmp_dir):
|
|
||||||
os.makedirs(tmp_dir, mode=0o700)
|
|
||||||
fn_parts = [self.ebics_config_id.ebics_host, self.ebics_config_id.ebics_partner]
|
fn_parts = [self.ebics_config_id.ebics_host, self.ebics_config_id.ebics_partner]
|
||||||
if docname:
|
if docname:
|
||||||
fn_parts.append(docname)
|
fn_parts.append(docname)
|
||||||
else:
|
else:
|
||||||
fn_date = self.date_to or fields.Date.today()
|
fn_date = self.date_to or fields.Date.today()
|
||||||
fn_parts.append(fn_date.isoformat())
|
fn_parts.append(fn_date.isoformat())
|
||||||
base_fn = "_".join(fn_parts)
|
fn = "_".join(fn_parts)
|
||||||
n = 1
|
|
||||||
full_tmp_fn = os.path.normpath(tmp_dir + "/" + base_fn)
|
|
||||||
while os.path.exists(full_tmp_fn):
|
|
||||||
n += 1
|
|
||||||
tmp_fn = base_fn + "_" + str(n).rjust(3, "0")
|
|
||||||
full_tmp_fn = os.path.normpath(tmp_dir + "/" + tmp_fn)
|
|
||||||
|
|
||||||
with open(full_tmp_fn, "wb") as f:
|
|
||||||
f.write(data)
|
|
||||||
|
|
||||||
ff_methods = self._file_format_methods()
|
ff_methods = self._file_format_methods()
|
||||||
if file_format.name in ff_methods:
|
if file_format.name in ff_methods:
|
||||||
data = ff_methods[file_format.name](data)
|
data = ff_methods[file_format.name](data)
|
||||||
|
|
||||||
fn = base_fn
|
|
||||||
suffix = file_format.suffix
|
suffix = file_format.suffix
|
||||||
if suffix and not fn.endswith(suffix):
|
if suffix and not fn.endswith(suffix):
|
||||||
fn = ".".join([fn, suffix])
|
fn = ".".join([fn, suffix])
|
||||||
|
@ -82,7 +82,7 @@
|
|||||||
<separator string="Select your file :" colspan="2" />
|
<separator string="Select your file :" colspan="2" />
|
||||||
<field name="upload_data" filename="upload_fname" required="1" />
|
<field name="upload_data" filename="upload_fname" required="1" />
|
||||||
<field name="upload_fname" invisible="1" />
|
<field name="upload_fname" invisible="1" />
|
||||||
<field name="upload_fname_dummy" />
|
<field name="upload_fname_dummy" string="Upload Filename" />
|
||||||
<field
|
<field
|
||||||
name="format_id"
|
name="format_id"
|
||||||
required="1"
|
required="1"
|
||||||
|
@ -46,15 +46,35 @@ class AccountStatementImport(models.TransientModel):
|
|||||||
show days without transactions via the bank statement list view.
|
show days without transactions via the bank statement list view.
|
||||||
"""
|
"""
|
||||||
if self.env.context.get("active_model") == "ebics.file":
|
if self.env.context.get("active_model") == "ebics.file":
|
||||||
|
messages = []
|
||||||
transactions = False
|
transactions = False
|
||||||
for st_vals in stmts_vals:
|
for st_vals in stmts_vals:
|
||||||
|
statement_ids = result["statement_ids"][:]
|
||||||
|
self._set_statement_name(st_vals)
|
||||||
if st_vals.get("transactions"):
|
if st_vals.get("transactions"):
|
||||||
transactions = True
|
transactions = True
|
||||||
break
|
super()._create_bank_statements([st_vals], result)
|
||||||
if not transactions:
|
if result["statement_ids"] == statement_ids:
|
||||||
message = _("This file doesn't contain any transaction.")
|
# no statement has been created, this is the case
|
||||||
st_line_ids = []
|
# when all transactions have been imported already
|
||||||
notifications = {"type": "warning", "message": message, "details": ""}
|
messages.append(
|
||||||
return st_line_ids, [notifications]
|
_(
|
||||||
|
"Statement %(st_name)s dated %(date)s "
|
||||||
|
"has already been imported.",
|
||||||
|
st_name=st_vals["name"],
|
||||||
|
date=st_vals["date"].strftime("%Y-%m-%d"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return super()._create_bank_statements(stmts_vals, result)
|
if not transactions:
|
||||||
|
messages.append(_("This file doesn't contain any transaction."))
|
||||||
|
if messages:
|
||||||
|
result["notifications"].append(
|
||||||
|
{"type": "warning", "message": "\n".join(messages)}
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
def _set_statement_name(self, st_vals):
|
||||||
|
"""
|
||||||
|
Inherit this method to set your own statement naming policy.
|
||||||
|
"""
|
||||||
|
@ -22,7 +22,3 @@ Usage
|
|||||||
Create your Payment Order and generate the bank file.
|
Create your Payment Order and generate the bank file.
|
||||||
Upload the generated file via the 'EBICS Upload' button on the payment order.
|
Upload the generated file via the 'EBICS Upload' button on the payment order.
|
||||||
|
|
||||||
Known issues / Roadmap
|
|
||||||
======================
|
|
||||||
|
|
||||||
* Add support for multiple EBICS connections.
|
|
||||||
|
@ -12,8 +12,6 @@
|
|||||||
"data": [
|
"data": [
|
||||||
"views/account_payment_order_views.xml",
|
"views/account_payment_order_views.xml",
|
||||||
],
|
],
|
||||||
# installable False unit OCA payment order becomes
|
|
||||||
# available for 16.0
|
|
||||||
"images": ["static/description/cover.png"],
|
"images": ["static/description/cover.png"],
|
||||||
"installable": False,
|
"installable": True,
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
../../../../account_ebics_payment_order
|
6
setup/account_ebics_payment_order/setup.py
Normal file
6
setup/account_ebics_payment_order/setup.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import setuptools
|
||||||
|
|
||||||
|
setuptools.setup(
|
||||||
|
setup_requires=['setuptools-odoo'],
|
||||||
|
odoo_addon=True,
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user