[IMP] addons-repo-template v9.2.1

This commit is contained in:
Jérémy Didderen committed 2026-10-27 21:38:39 +01:00
1 parent e4c40c373e
commit 296695f97a
21 files changed
+188 -291

No files matched your search

+2 -1
View File
@@ -1,7 +1,8 @@
# Do NOT update manually; changes here will be overwritten by Copier # Do NOT update manually; changes here will be overwritten by Copier
_commit: v8.0.2 _commit: v9.1.0
_src_path: git@github.com:Noviat-CI/addons-repo-template.git _src_path: git@github.com:Noviat-CI/addons-repo-template.git
ci_disable_autoinstall_modules: account_asset ci_disable_autoinstall_modules: account_asset
community_excluded_modules: account_ebics_batch_payment,account_ebics_oe
odoo_test_flavor: Both odoo_test_flavor: Both
odoo_version: 18.0 odoo_version: 18.0
org_slug: Noviat org_slug: Noviat
+1 -1
View File
@@ -24,6 +24,6 @@ jobs:
- name: Add Noviat CI Tools to PATH - name: Add Noviat CI Tools to PATH
run: | run: |
git clone https://github.com/Noviat-CI/noviat-ci-tools.git /opt/noviat-ci-tools git clone https://github.com/Noviat-CI/noviat-ci-tools.git /opt/noviat-ci-tools
echo "/opt/noviat-ci-tools" >> $GITHUB_PATH echo "/opt/noviat-ci-tools" >> "$GITHUB_PATH"
- name: Check module version bump - name: Check module version bump
run: check_module_version_bump run: check_module_version_bump
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
cache: 'pip' cache: 'pip'
cache-dependency-path: '.pre-commit-config.yaml' cache-dependency-path: '.pre-commit-config.yaml'
- name: Get python version - name: Get python version
run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> "$GITHUB_ENV"
- uses: actions/cache@v6 - uses: actions/cache@v6
with: with:
path: ~/.cache/pre-commit path: ~/.cache/pre-commit
+32 -34
View File
@@ -20,11 +20,11 @@ jobs:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- run: | - run: |
for reqfile in requirements.txt test-requirements.txt ; do for reqfile in requirements.txt test-requirements.txt ; do
if [ -f ${reqfile} ] ; then if [ -f "${reqfile}" ] ; then
result=0 result=0
# reject non-comment lines that contain a / (i.e. URLs, relative paths) # reject non-comment lines that contain a / (i.e. URLs, relative paths)
# unless they have a "# SKIP TEST" comment # unless they have a "# SKIP TEST" comment
grep "^[^#].*/" ${reqfile} | grep -v "# SKIP TEST" || result=$? grep "^[^#].*/" "${reqfile}" | grep -v "# SKIP TEST" || result=$?
if [ $result -eq 0 ] ; then if [ $result -eq 0 ] ; then
echo "Unreleased dependencies found in ${reqfile}." echo "Unreleased dependencies found in ${reqfile}."
exit 1 exit 1
@@ -33,7 +33,11 @@ jobs:
done done
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: ${{ matrix.container }} container:
image: ${{ matrix.container }}
credentials:
username: ${{ github.actor }}
password: ${{ secrets.PRIVATE_TOKEN }}
name: ${{ matrix.name }} name: ${{ matrix.name }}
permissions: permissions:
pull-requests: write pull-requests: write
@@ -42,12 +46,12 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- container: ghcr.io/oca/oca-ci/py3.10-odoo18.0:latest - container: ghcr.io/noviat-ci/odoo-ci:18.0-stable
name: test with Odoo Community name: test with Odoo Community
odoo_enterprise : 0 odoo_enterprise : 0
exclude_modules : "account_asset" exclude_modules : "account_asset"
exclude: "account_ebics_batch_payment,account_ebics_oe" skip: "account_ebics_batch_payment,account_ebics_oe"
- container: ghcr.io/oca/oca-ci/py3.10-odoo18.0:latest - container: ghcr.io/noviat-ci/odoo-ci:18.0-stable
name: test with Odoo Enterprise name: test with Odoo Enterprise
odoo_enterprise : 1 odoo_enterprise : 1
exclude_modules : "account_asset" exclude_modules : "account_asset"
@@ -73,49 +77,43 @@ jobs:
TOKEN: ${{ secrets.PRIVATE_TOKEN }} TOKEN: ${{ secrets.PRIVATE_TOKEN }}
run: | run: |
git config --global http.https://github.com/.extraheader "AUTHORIZATION: basic $(printf 'x-access-token:%s' "$TOKEN" | base64 -w0)" git config --global http.https://github.com/.extraheader "AUTHORIZATION: basic $(printf 'x-access-token:%s' "$TOKEN" | base64 -w0)"
- name: Set pip constraints (pyOpenSSL / cryptography) - name: Show the CI image age
run: | run: |
cat > /tmp/constraints.txt <<'EOF' age=$(( ( $(date -u +%s) - $(date -u -d "$NOVIAT_CI_IMAGE_BUILD_DATE" +%s) ) / 86400 ))
pyOpenSSL == 22.1.0 echo "Image $NOVIAT_CI_ODOO_VERSION built $NOVIAT_CI_IMAGE_BUILD_DATE ($age days old)" | tee -a "$GITHUB_STEP_SUMMARY"
cryptography == 38.0.4
EOF
echo "PIP_CONSTRAINT=/tmp/constraints.txt" >> $GITHUB_ENV
- name: Upgrade pip in container
run: python -m pip install --upgrade pip
- name: Install Odoo upgrade util
run: python -m pip install git+https://github.com/odoo/upgrade-util@master
- name: Add Noviat CI Tools to PATH
run: |
git clone https://github.com/Noviat-CI/noviat-ci-tools.git /opt/noviat-ci-tools
echo "/opt/noviat-ci-tools" >> $GITHUB_PATH
- name: Clone Odoo Enterprise
if: ${{ matrix.odoo_enterprise == 1 }}
run : |
git clone https://github.com/odoo/enterprise.git --depth 1 -b 18.0 /opt/enterprise
- name: Clone Noviat Dependencies - name: Clone Noviat Dependencies
run: clone_oca_dependencies_by_noviat run: clone_oca_dependencies_by_noviat
- name: Create addons path - name: Create addons path
run: | run: |
mkdir -p ${{ env.INSTALL_ADDONS_DIR }}/setup mkdir -p "$INSTALL_ADDONS_DIR/setup"
find $(realpath $ADDONS_DIR) $(realpath ${{ env.ADDITIONAL_ADDONS_DIR }}) -name __manifest__.py -exec sh -c 'ln -s "$(dirname "$0")" "${{ env.INSTALL_ADDONS_DIR }}"' {} \; find "$(realpath "$ADDONS_DIR")" "$(realpath "$ADDITIONAL_ADDONS_DIR")" -name __manifest__.py -exec sh -c 'ln -s "$(dirname "$0")" "$1"' {} "$INSTALL_ADDONS_DIR" \;
find $(realpath $ADDONS_DIR) $(realpath ${{ env.ADDITIONAL_ADDONS_DIR }}) -iwholename '*/setup/*/setup.py' -exec sh -c 'ln -s "$(dirname "$0")" "${{ env.INSTALL_ADDONS_DIR }}/setup"' {} \; find "$(realpath "$ADDONS_DIR")" "$(realpath "$ADDITIONAL_ADDONS_DIR")" -iwholename '*/setup/*/setup.py' -exec sh -c 'ln -s "$(dirname "$0")" "$1/setup"' {} "$INSTALL_ADDONS_DIR" \;
if [ -n ${{ matrix.exclude}} ] # EXCLUDE (from matrix.exclude) only exists with rebel module groups
if [ -n "${EXCLUDE:-}" ]
then then
remove_excluded_modules ${{ env.INSTALL_ADDONS_DIR }} ${{ matrix.exclude }} remove_excluded_modules "$INSTALL_ADDONS_DIR" "$EXCLUDE"
fi fi
- name: Skip addons
if: ${{ matrix.skip }}
env:
SKIPPED: ${{ matrix.skip }}
run: |
remove_excluded_modules "$INSTALL_ADDONS_DIR" "$SKIPPED"
echo "EXCLUDE=${EXCLUDE:+$EXCLUDE,}$SKIPPED" >> "$GITHUB_ENV"
echo "Not installed nor tested with Odoo Community: $SKIPPED" | tee -a "$GITHUB_STEP_SUMMARY"
- name: Update addons path with enterprise - name: Update addons path with enterprise
if: ${{ matrix.odoo_enterprise == 1 }} if: ${{ matrix.odoo_enterprise == 1 }}
run: | run: |
echo "ADDONS_PATH=/opt/odoo/addons,/opt/enterprise,${{ env.INSTALL_ADDONS_DIR }}" >> $GITHUB_ENV echo "ADDONS_PATH=/opt/odoo/addons,/opt/enterprise,${{ env.INSTALL_ADDONS_DIR }}" >> "$GITHUB_ENV"
- name: Update addons path without enterprise - name: Update addons path without enterprise
if: ${{ matrix.odoo_enterprise == 0 }} if: ${{ matrix.odoo_enterprise == 0 }}
run: | run: |
echo "ADDONS_PATH=/opt/odoo/addons,${{ env.INSTALL_ADDONS_DIR }}" >> $GITHUB_ENV echo "ADDONS_PATH=/opt/odoo/addons,${{ env.INSTALL_ADDONS_DIR }}" >> "$GITHUB_ENV"
- name: Add addons to EXCLUDE PATH - name: Add addons to EXCLUDE PATH
if: ${{ matrix.EXCLUDE_MODULES }} if: ${{ matrix.EXCLUDE_MODULES }}
run: | run: |
echo "server_wide_modules = web,module_change_auto_install" >> ${ODOO_RC} echo "server_wide_modules = web,module_change_auto_install" >> "${ODOO_RC}"
echo "ODOO_MODULES_AUTO_INSTALL_DISABLED=${{ matrix.EXCLUDE_MODULES }}" >> $GITHUB_ENV echo "ODOO_MODULES_AUTO_INSTALL_DISABLED=${{ matrix.EXCLUDE_MODULES }}" >> "$GITHUB_ENV"
- name: Install addons and dependencies - name: Install addons and dependencies
run: | run: |
(ADDONS_PATH=${ADDONS_PATH} ADDONS_DIR=${INSTALL_ADDONS_DIR} oca_install_addons) (ADDONS_PATH=${ADDONS_PATH} ADDONS_DIR=${INSTALL_ADDONS_DIR} oca_install_addons)
@@ -149,7 +147,7 @@ jobs:
hide_complexity: true hide_complexity: true
- name: Write to Job Summary - name: Write to Job Summary
if: steps.coverage.outputs.report == 'true' if: steps.coverage.outputs.report == 'true'
run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY run: cat code-coverage-results.md >> "$GITHUB_STEP_SUMMARY"
- name: Update .pot files - name: Update .pot files
run: noviat_export_and_push_pot https://github.com/${{ github.repository }} run: noviat_export_and_push_pot https://github.com/${{ github.repository }}
if: ${{ matrix.makepot == 'true' && github.event_name == 'push' }} if: ${{ matrix.makepot == 'true' && github.event_name == 'push' }}
+2 -12
View File
@@ -6,7 +6,7 @@ exclude: |
^setup/|/static/description/index\.html$| ^setup/|/static/description/index\.html$|
# We don't want to mess with tool-generated files # We don't want to mess with tool-generated files
.svg$|/tests/([^/]+/)?cassettes/|^.copier-answers.yml$|^.github/|^eslint.config.cjs|^prettier.config.cjs| .svg$|/tests/([^/]+/)?cassettes/|^.copier-answers.yml$|^.github/|^eslint.config.cjs|^prettier.config.cjs|
# Maybe reactivate this when all README files include prettier ignore tags? # README.md holds the generated addons table
^README\.md$| ^README\.md$|
# Library files can have extraneous formatting (even minimized) # Library files can have extraneous formatting (even minimized)
/static/(src/)?lib/| /static/(src/)?lib/|
@@ -121,10 +121,6 @@ repos:
- id: check-xml - id: check-xml
- id: mixed-line-ending - id: mixed-line-ending
args: ["--fix=lf"] args: ["--fix=lf"]
- repo: https://github.com/asottile/pyupgrade
rev: v3.21.2
hooks:
- id: pyupgrade
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.16.9 rev: v0.16.9
hooks: hooks:
@@ -137,13 +133,7 @@ repos:
rev: v10.0.11 rev: v10.0.11
hooks: hooks:
- id: pylint_odoo - id: pylint_odoo
name: pylint (optional, core only) name: pylint-odoo
args:
- --rcfile=.pylintrc
- --exit-zero
verbose: true
- id: pylint_odoo
name: pylint-odoo (mandatory, blocking)
args: args:
- --disable=missing-readme - --disable=missing-readme
- --rcfile=.pylintrc-mandatory - --rcfile=.pylintrc-mandatory
-118
View File
@@ -1,118 +0,0 @@
[MASTER]
ignore-patterns=["^.*/TODO/.*$"]
load-plugins=pylint_odoo
score=n
[IMPORTS]
deprecated-modules=pdb,pudb,ipdb,bs4
[ODOOLINT]
#readme-template-url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst"
manifest-required-authors=Noviat
manifest-required-keys=license
manifest-deprecated-keys=description,active
license-allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3
valid-odoo-versions=18.0
[MESSAGES CONTROL]
disable=all
# This .pylintrc contains optional AND mandatory checks and is meant to be
# loaded in an IDE to have it check everything, in the hope this will make
# optional checks more visible to contributors who otherwise never look at a
# green travis to see optional checks that failed.
# .pylintrc-mandatory containing only mandatory checks is used the pre-commit
# config as a blocking check.
enable =
anomalous-backslash-in-string,
assignment-from-none,
attribute-deprecated,
dangerous-default-value,
deprecated-module,
development-status-allowed,
duplicate-key,
eval-used,
license-allowed,
manifest-author-string,
manifest-deprecated-key,
# manifest-required-author,
manifest-required-key,
manifest-version-format,
method-compute,
method-inverse,
method-required-super,
method-search,
pointless-statement,
pointless-string-statement,
print-used,
redundant-keyword-arg,
reimported,
return-in-init,
sql-injection,
too-few-format-args,
translation-field,
translation-required,
unreachable,
use-vim-comment,
# >= 15
attribute-string-redundant,
consider-merging-classes-inherited,
context-overridden,
except-pass,
invalid-commit,
manifest-external-assets,
manifest-maintainers-list,
missing-readme,
missing-return,
odoo-addons-relative-import,
odoo-exception-warning,
renamed-field-parameter,
resource-not-exist,
test-folder-imported,
translation-contains-variable,
translation-positional-used,
website-manifest-key-not-valid-uri,
# >= 16
external-request-timeout,
# >= 19
bad-builtin-groupby,
category-allowed,
deprecated-name-get,
deprecated-odoo-model-method,
deprecated-self-cr,
inheritable-method-lambda,
inheritable-method-string,
invalid-email,
manifest-behind-migrations,
manifest-data-duplicated,
missing-odoo-file,
no-raise-unlink,
no-search-all,
no-wizard-in-models,
prohibited-method-override,
prefer-env-translation,
translation-format-interpolation,
translation-format-truncated,
translation-injection,
translation-fstring-interpolation,
translation-not-lazy,
translation-too-few-args,
translation-too-many-args,
translation-unsupported-format,
no-write-in-compute,
odoolint,
# >= 20
manifest-summary-multiline,
# messages that do not cause the lint step to fail
consider-merging-classes-inherited,
invalid-commit,
missing-readme,
odoo-addons-relative-import,
redefined-builtin
[REPORTS]
msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg}
output-format=colorized
reports=no
+1 -2
View File
@@ -11,12 +11,11 @@ deprecated-modules=pdb,pudb,ipdb,bs4
manifest-required-authors=Noviat manifest-required-authors=Noviat
manifest-required-keys=license manifest-required-keys=license
manifest-deprecated-keys=description,active manifest-deprecated-keys=description,active
license-allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3 license-allowed=AGPL-3,LGPL-3,Other proprietary
valid-odoo-versions=18.0 valid-odoo-versions=18.0
[MESSAGES CONTROL] [MESSAGES CONTROL]
disable=all disable=all
enable = enable =
anomalous-backslash-in-string, anomalous-backslash-in-string,
assignment-from-none, assignment-from-none,
+34
View File
@@ -16,6 +16,40 @@ select = [
"E501", # line too long (default 88) "E501", # line too long (default 88)
"I", # isort "I", # isort
"UP", # pyupgrade "UP", # pyupgrade
# Autofixed
"RET501",
"RET502",
"RET505",
"RET506",
"RET507",
"RET508",
"PLR1711",
"PLR5501",
"RUF019",
"RUF021",
"RUF100",
"SIM114",
"ISC001",
"C4",
"PIE",
"FLY",
"FURB",
# Few and real: logging format, performance, security
"G",
"PERF",
"S102",
"S105",
"S106",
"S107",
"S108",
"S301",
"S603",
"S607",
"S704",
# Commented-out code, blind except, unused unpacked variables
"ERA001",
"BLE001",
"RUF059",
] ]
extend-safe-fixes = ["UP008"] extend-safe-fixes = ["UP008"]
exclude = ["setup/*"] exclude = ["setup/*"]
+1 -1
View File
@@ -8,7 +8,7 @@ _logger = logging.getLogger(__name__)
try: try:
from . import models from . import models
from . import wizards from . import wizards
except Exception: except Exception: # noqa: BLE001 - the module must load without fintech
_logger.warning("Import Error, check if fintech lib has been installed") _logger.warning("Import Error, check if fintech lib has been installed")
@@ -25,7 +25,7 @@ def _ebics_config_upgrade(env, version):
if os.path.isfile(keys_fn_old): if os.path.isfile(keys_fn_old):
keys_fn = ebics_keys_root + "/" + cfg_data["ebics_user"] + "_keys" keys_fn = ebics_keys_root + "/" + cfg_data["ebics_user"] + "_keys"
os.rename(keys_fn_old, keys_fn) os.rename(keys_fn_old, keys_fn)
state = cfg_data["state"] == "active" and "confirm" or "draft" state = (cfg_data["state"] == "active" and "confirm") or "draft"
cfg.write( cfg.write(
{ {
"company_ids": [(6, 0, [cfg_data["company_id"]])], "company_ids": [(6, 0, [cfg_data["company_id"]])],
+3 -4
View File
@@ -127,7 +127,7 @@ class EbicsConfig(models.Model):
@api.model @api.model
def _default_ebics_keys(self): def _default_ebics_keys(self):
return "/".join(["/etc/odoo/ebics_keys", self._cr.dbname]) return f"/etc/odoo/ebics_keys/{self._cr.dbname}"
@api.constrains("ebics_key_bitlength") @api.constrains("ebics_key_bitlength")
def _check_ebics_key_bitlength(self): def _check_ebics_key_bitlength(self):
@@ -185,9 +185,8 @@ class EbicsConfig(models.Model):
if c == "Z": if c == "Z":
o_list[-i] = "0" o_list[-i] = "0"
continue continue
else: o_list[-i] = chr(ord(c) + 1)
o_list[-i] = chr(ord(c) + 1) break
break
next_order_number = "".join(o_list) next_order_number = "".join(o_list)
if next_order_number == "ZZZZ": if next_order_number == "ZZZZ":
next_order_number = "A000" next_order_number = "A000"
+10 -11
View File
@@ -225,9 +225,8 @@ class EbicsFile(models.Model):
journal_currency = jrnl.currency_id or jrnl.company_id.currency_id journal_currency = jrnl.currency_id or jrnl.company_id.currency_id
if journal_currency != currency: if journal_currency != currency:
continue continue
else: journal = jrnl
journal = jrnl break
break
if not journal: if not journal:
message = self.env._( message = self.env._(
@@ -301,7 +300,7 @@ class EbicsFile(models.Model):
self.note_process += self.env._( self.note_process += self.env._(
"%(st_cnt)s bank statement%(sp)s been imported: ", "%(st_cnt)s bank statement%(sp)s been imported: ",
st_cnt=st_cnt, st_cnt=st_cnt,
sp=st_cnt == 1 and self.env._(" has") or self.env._("s have"), sp=(st_cnt == 1 and self.env._(" has")) or self.env._("s have"),
) )
self.note_process += "\n" self.note_process += "\n"
for statement in statements: for statement in statements:
@@ -392,9 +391,7 @@ class EbicsFile(models.Model):
res["notifications"].append({"type": "error", "message": message}) res["notifications"].append({"type": "error", "message": message})
return datas return datas
lines = [] lines = [file_data[i : i + 120] for i in range(0, len(file_data), 120)]
for i in range(0, len(file_data), 120):
lines.append(file_data[i : i + 120])
st_lines = b"" st_lines = b""
transactions = False transactions = False
@@ -488,11 +485,12 @@ class EbicsFile(models.Model):
try: try:
with self.env.cr.savepoint(): with self.env.cr.savepoint():
self._create_bank_statement_oca(res, st_data) self._create_bank_statement_oca(res, st_data)
except UserError as e: except UserError as e: # noqa: PERF203
res["notifications"].append( res["notifications"].append(
{"type": "error", "message": "".join(e.args)} {"type": "error", "message": "".join(e.args)}
) )
except Exception: # Each statement fails alone; the traceback is reported to the user
except Exception: # noqa: BLE001
tb = "".join(format_exception(*exc_info())) tb = "".join(format_exception(*exc_info()))
res["notifications"].append({"type": "error", "message": tb}) res["notifications"].append({"type": "error", "message": tb})
@@ -514,7 +512,7 @@ class EbicsFile(models.Model):
try: try:
self._create_bank_statement_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: # noqa: PERF203
msg = "".join(e.args) msg = "".join(e.args)
msg += "\n" msg += "\n"
msg += self.env._( msg += self.env._(
@@ -522,7 +520,8 @@ class EbicsFile(models.Model):
nr=st_data["acc_number"], nr=st_data["acc_number"],
) )
res["notifications"].append({"type": "error", "message": msg}) res["notifications"].append({"type": "error", "message": msg})
except Exception: # Each statement fails alone; the traceback is reported to the user
except Exception: # noqa: BLE001
tb = "".join(format_exception(*exc_info())) tb = "".join(format_exception(*exc_info()))
res["notifications"].append({"type": "error", "message": tb}) res["notifications"].append({"type": "error", "message": tb})
+1 -1
View File
@@ -113,7 +113,7 @@ class EbicsFileFormat(models.Model):
@api.depends("ebics_version", "name", "btf_message", "description") @api.depends("ebics_version", "name", "btf_message", "description")
def _compute_display_name(self): def _compute_display_name(self):
for rec in self: for rec in self:
name = rec.ebics_version == "2" and rec.name or rec.btf_message name = (rec.ebics_version == "2" and rec.name) or rec.btf_message
if rec.description: if rec.description:
name += " - " + rec.description name += " - " + rec.description
rec.display_name = name rec.display_name = name
+8 -10
View File
@@ -38,7 +38,7 @@ class EbicsBank(EbicsBank):
The OrderID must be a string between 'A000' and 'ZZZZ' and The OrderID must be a string between 'A000' and 'ZZZZ' and
unique for each partner id. unique for each partner id.
""" """
return hasattr(self, "_order_number") and self._order_number or "A000" return (hasattr(self, "_order_number") and self._order_number) or "A000"
class EbicsUserID(models.Model): class EbicsUserID(models.Model):
@@ -216,7 +216,9 @@ class EbicsUserID(models.Model):
rec.ebics_passphrase_store_readonly = True rec.ebics_passphrase_store_readonly = True
if rec.state == "draft": if rec.state == "draft":
rec.ebics_passphrase_required = True rec.ebics_passphrase_required = True
rec.ebics_passphrase_invisible = rec.ebics_keys_found and True or False rec.ebics_passphrase_invisible = (
rec.ebics_keys_found and True
) or False
rec.ebics_passphrase_store_readonly = False rec.ebics_passphrase_store_readonly = False
elif rec.state == "init": elif rec.state == "init":
rec.ebics_passphrase_required = False rec.ebics_passphrase_required = False
@@ -289,9 +291,8 @@ class EbicsUserID(models.Model):
keyring["#USER"] keyring["#USER"]
except ValueError as err: # noqa: F841 except ValueError as err: # noqa: F841
raise UserError(self.env._("Passphrase mismatch.")) # noqa: B904 raise UserError(self.env._("Passphrase mismatch.")) # noqa: B904
else: elif self.state != "draft":
if self.state != "draft": self.ebics_passphrase = False
self.ebics_passphrase = False
@api.onchange("swift_3skey") @api.onchange("swift_3skey")
def _onchange_swift_3skey(self): def _onchange_swift_3skey(self):
@@ -479,7 +480,7 @@ class EbicsUserID(models.Model):
lang = lang.lower() lang = lang.lower()
lang = lang if lang in INI_LETTER_LANGS else "en" lang = lang if lang in INI_LETTER_LANGS else "en"
fn_date = fields.Date.today().isoformat() fn_date = fields.Date.today().isoformat()
fn = "_".join([self.ebics_config_id.ebics_host, "ini_letter", fn_date]) + ".pdf" fn = f"{self.ebics_config_id.ebics_host}_ini_letter_{fn_date}" + ".pdf"
letter = user.create_ini_letter(bankname=ebics_config_bank.name, lang=lang) letter = user.create_ini_letter(bankname=ebics_config_bank.name, lang=lang)
vals = { vals = {
"ebics_ini_letter": base64.encodebytes(letter), "ebics_ini_letter": base64.encodebytes(letter),
@@ -548,10 +549,7 @@ class EbicsUserID(models.Model):
public_bank_keys = public_bank_keys.encode() public_bank_keys = public_bank_keys.encode()
fn_date = fields.Date.today().isoformat() fn_date = fields.Date.today().isoformat()
fn = ( fn = f"{self.ebics_config_id.ebics_host}_public_bank_keys_{fn_date}" + ".txt"
"_".join([self.ebics_config_id.ebics_host, "public_bank_keys", fn_date])
+ ".txt"
)
vals = { vals = {
"ebics_public_bank_keys": base64.encodebytes(public_bank_keys), "ebics_public_bank_keys": base64.encodebytes(public_bank_keys),
"ebics_public_bank_keys_fn": fn, "ebics_public_bank_keys_fn": fn,
@@ -24,8 +24,7 @@ try:
fintech_register_users = ( fintech_register_users = (
fintech_register_users fintech_register_users
and [x.strip() for x in fintech_register_users.split(",")] and [x.strip() for x in fintech_register_users.split(",")]
or None ) or None
)
fintech.cryptolib = "cryptography" fintech.cryptolib = "cryptography"
fintech.register( fintech.register(
name=fintech_register_name, name=fintech_register_name,
@@ -38,7 +37,7 @@ except RuntimeError as e:
else: else:
_logger.error(str(e)) _logger.error(str(e))
fintech.register() fintech.register()
except Exception: except Exception: # noqa: BLE001 - logged with the traceback, then retried
msg = "fintech.register error" msg = "fintech.register error"
tb = "".join(format_exception(*exc_info())) tb = "".join(format_exception(*exc_info()))
msg += f"\n{tb}" msg += f"\n{tb}"
@@ -70,8 +70,7 @@ class EbicsChangePassphrase(models.TransientModel):
passphrase = ( passphrase = (
self.ebics_userid_id.ebics_passphrase_store self.ebics_userid_id.ebics_passphrase_store
and self.ebics_userid_id.ebics_passphrase and self.ebics_userid_id.ebics_passphrase
or self.old_pass ) or self.old_pass
)
try: try:
keyring_params = { keyring_params = {
"keys": self.ebics_userid_id.ebics_keys_fn, "keys": self.ebics_userid_id.ebics_keys_fn,
+16 -15
View File
@@ -37,7 +37,7 @@ class EbicsBank(EbicsBank):
The OrderID must be a string between 'A000' and 'ZZZZ' and The OrderID must be a string between 'A000' and 'ZZZZ' and
unique for each partner id. unique for each partner id.
""" """
return hasattr(self, "_order_number") and self._order_number or "A000" return (hasattr(self, "_order_number") and self._order_number) or "A000"
class EbicsXfer(models.TransientModel): class EbicsXfer(models.TransientModel):
@@ -109,8 +109,7 @@ class EbicsXfer(models.TransientModel):
) )
if cfg and len(cfg) == 1: if cfg and len(cfg) == 1:
return cfg return cfg
else: return cfg_mod
return cfg_mod
def _compute_ebics_sig_passphrase_invisible(self): def _compute_ebics_sig_passphrase_invisible(self):
for rec in self: for rec in self:
@@ -236,8 +235,8 @@ class EbicsXfer(models.TransientModel):
) )
) )
ebics_files = self.env["ebics.file"] ebics_files = self.env["ebics.file"]
date_from = self.date_from and self.date_from.isoformat() or None date_from = (self.date_from and self.date_from.isoformat()) or None
date_to = self.date_to and self.date_to.isoformat() or None date_to = (self.date_to and self.date_to.isoformat()) or None
for df in download_formats: for df in download_formats:
success = False success = False
try: try:
@@ -314,7 +313,8 @@ class EbicsXfer(models.TransientModel):
) )
self.note += "\n" self.note += "\n"
self.note += " ".join(e.args) self.note += " ".join(e.args)
except Exception: # Reported to the user in the note
except Exception: # noqa: BLE001
err_cnt += 1 err_cnt += 1
self.note += "\n" self.note += "\n"
self.note += self.env._( self.note += self.env._(
@@ -412,7 +412,7 @@ class EbicsXfer(models.TransientModel):
suffix = self.format_id.suffix suffix = self.format_id.suffix
fn = self.upload_fname fn = self.upload_fname
if suffix and not fn.endswith(suffix): if suffix and not fn.endswith(suffix):
fn = ".".join([fn, suffix]) fn = f"{fn}.{suffix}"
ef_vals = { ef_vals = {
"name": self.upload_fname, "name": self.upload_fname,
"data": self.upload_data, "data": self.upload_data,
@@ -449,7 +449,8 @@ class EbicsXfer(models.TransientModel):
self.note += self.env._("EBICS Verification Error:") self.note += self.env._("EBICS Verification Error:")
self.note += "\n" self.note += "\n"
self.note += self.env._("The EBICS response could not be verified.") self.note += self.env._("The EBICS response could not be verified.")
except Exception: # Reported to the user in the note, with the traceback
except Exception: # noqa: BLE001
self.note += "\n" self.note += "\n"
self.note += self.env._("Unknown Error") self.note += self.env._("Unknown Error")
tb = "".join(format_exception(*exc_info())) tb = "".join(format_exception(*exc_info()))
@@ -523,7 +524,7 @@ class EbicsXfer(models.TransientModel):
# manual_approval replaced by transport_only class param in fintech 7.4 # manual_approval replaced by transport_only class param in fintech 7.4
fintech74 = hasattr(EbicsUser, "transport_only") fintech74 = hasattr(EbicsUser, "transport_only")
if fintech74: if fintech74:
user_params["transport_only"] = signature_class == "T" and True or False user_params["transport_only"] = (signature_class == "T" and True) or False
try: try:
user = EbicsUser(**user_params) user = EbicsUser(**user_params)
except ValueError as err: except ValueError as err:
@@ -543,7 +544,8 @@ class EbicsXfer(models.TransientModel):
try: try:
client = EbicsClient(bank, user, version=self.ebics_config_id.ebics_version) client = EbicsClient(bank, user, version=self.ebics_config_id.ebics_version)
except Exception: # Reported to the user in the note, with the traceback
except Exception: # noqa: BLE001
self.note += "\n" self.note += "\n"
self.note += self.env._("Unknown Error") self.note += self.env._("Unknown Error")
tb = "".join(format_exception(*exc_info())) tb = "".join(format_exception(*exc_info()))
@@ -608,7 +610,7 @@ class EbicsXfer(models.TransientModel):
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 = f"{fn}.{suffix}"
dups = self._check_duplicate_ebics_file(fn, file_format) dups = self._check_duplicate_ebics_file(fn, file_format)
if dups: if dups:
raise UserError( raise UserError(
@@ -627,7 +629,7 @@ class EbicsXfer(models.TransientModel):
"date_from": self.date_from, "date_from": self.date_from,
"date_to": self.date_to, "date_to": self.date_to,
"format_id": file_format.id, "format_id": file_format.id,
"state": file_format.download_process_method and "draft" or "done", "state": (file_format.download_process_method and "draft") or "done",
"user_id": self._uid, "user_id": self._uid,
"ebics_userid_id": self.ebics_userid_id.id, "ebics_userid_id": self.ebics_userid_id.id,
"company_ids": self.ebics_config_id.company_ids.ids, "company_ids": self.ebics_config_id.company_ids.ids,
@@ -656,9 +658,8 @@ class EbicsXfer(models.TransientModel):
break break
if c == "Z": if c == "Z":
continue continue
else: o_list[-i] = chr(ord(c) + 1)
o_list[-i] = chr(ord(c) + 1) break
break
next_nr = "".join(o_list) next_nr = "".join(o_list)
if next_nr == "ZZZZ": if next_nr == "ZZZZ":
next_nr = "A000" next_nr = "A000"
@@ -120,7 +120,8 @@ class EbicsBatchLog(models.Model):
) )
except UserError as e: except UserError as e:
import_dict["errors"].append(err_msg + " ".join(e.args)) import_dict["errors"].append(err_msg + " ".join(e.args))
except Exception: # Each configuration fails alone; reported in the batch log
except Exception: # noqa: BLE001
tb = "".join(format_exception(*exc_info())) tb = "".join(format_exception(*exc_info()))
import_dict["errors"].append(err_msg + tb) import_dict["errors"].append(err_msg + tb)
log.file_ids = [(6, 0, ebics_file_ids)] log.file_ids = [(6, 0, ebics_file_ids)]
@@ -128,14 +129,15 @@ class EbicsBatchLog(models.Model):
log._ebics_process(import_dict) log._ebics_process(import_dict)
except UserError as e: except UserError as e:
import_dict["errors"].append(err_msg + " ".join(e.args)) import_dict["errors"].append(err_msg + " ".join(e.args))
except Exception: # Reported in the batch log with the traceback
except Exception: # noqa: BLE001
tb = "".join(format_exception(*exc_info())) tb = "".join(format_exception(*exc_info()))
import_dict["errors"].append(err_msg + tb) import_dict["errors"].append(err_msg + tb)
log._finalise_processing(import_dict) log._finalise_processing(import_dict)
def _finalise_processing(self, import_dict): def _finalise_processing(self, import_dict):
log_item_model = self.env["ebics.batch.log.item"] log_item_model = self.env["ebics.batch.log.item"]
state = self.has_draft_files and "draft" or "done" state = (self.has_draft_files and "draft") or "done"
note = "" note = ""
error_count = 0 error_count = 0
if import_dict["errors"]: if import_dict["errors"]:
@@ -157,7 +159,7 @@ class EbicsBatchLog(models.Model):
lambda r: r.ebics_passphrase_store lambda r: r.ebics_passphrase_store
) )
t_userids = ebics_userids.filtered(lambda r: r.signature_class == "T") t_userids = ebics_userids.filtered(lambda r: r.signature_class == "T")
ebics_userid = t_userids and t_userids[0] or ebics_userids[0] ebics_userid = (t_userids and t_userids[0]) or ebics_userids[0]
xfer_wiz = ( xfer_wiz = (
self.env["ebics.xfer"] self.env["ebics.xfer"]
.with_context(ebics_download=True) .with_context(ebics_download=True)
@@ -4,7 +4,7 @@
{ {
"name": "EBICS - Bank Statement Import (OCA)", "name": "EBICS - Bank Statement Import (OCA)",
"summary": "Process EBICS statement files with the OCA bank statement import", "summary": "Process EBICS statement files with the OCA bank statement import",
"version": "18.0.1.0.2", "version": "18.0.1.0.3",
"author": "Noviat", "author": "Noviat",
"website": "https://www.noviat.com/", "website": "https://www.noviat.com/",
"category": "Hidden", "category": "Hidden",
@@ -64,7 +64,7 @@ class AccountStatementImport(models.TransientModel):
+ message + message
) )
_logger.warning(log_msg) _logger.warning(log_msg)
return return None
return super()._check_parsed_data(stmts_vals) return super()._check_parsed_data(stmts_vals)
def _create_bank_statements(self, stmts_vals, result): def _create_bank_statements(self, stmts_vals, result):
@@ -79,42 +79,39 @@ class AccountStatementImport(models.TransientModel):
""" """
if self.env.context.get("active_model") != "ebics.file": if self.env.context.get("active_model") != "ebics.file":
return super()._create_bank_statements(stmts_vals, result) return super()._create_bank_statements(stmts_vals, result)
else: messages = []
messages = [] transactions = False
transactions = False for st_vals in stmts_vals:
for st_vals in stmts_vals: statement_ids = result["statement_ids"][:]
statement_ids = result["statement_ids"][:] self._set_statement_name(st_vals)
self._set_statement_name(st_vals) if st_vals.get("transactions"):
if st_vals.get("transactions"): transactions = True
transactions = True super()._create_bank_statements([st_vals], result)
super()._create_bank_statements([st_vals], result) if result["statement_ids"] == statement_ids:
if result["statement_ids"] == statement_ids: # no statement has been created, this is the case
# no statement has been created, this is the case # when all transactions have been imported already
# when all transactions have been imported already if isinstance(st_vals["date"], date) or isinstance(
if isinstance(st_vals["date"], date) or isinstance( st_vals["date"], datetime
st_vals["date"], datetime ):
): st_date = st_vals["date"].strftime("%Y-%m-%d")
st_date = st_vals["date"].strftime("%Y-%m-%d") else:
else: st_date = st_vals["date"]
st_date = st_vals["date"] messages.append(
messages.append( self.env._(
self.env._( "Statement %(st_name)s dated %(date)s "
"Statement %(st_name)s dated %(date)s " "has already been imported.",
"has already been imported.", st_name=st_vals["name"],
st_name=st_vals["name"], date=st_date,
date=st_date,
)
) )
)
if not transactions: if not transactions:
messages.append( messages.append(self.env._("This file doesn't contain any transaction."))
self.env._("This file doesn't contain any transaction.") if messages:
) result["notifications"].append(
if messages: {"type": "warning", "message": "\n".join(messages)}
result["notifications"].append( )
{"type": "warning", "message": "\n".join(messages)} return None
)
return
def _set_statement_name(self, st_vals): def _set_statement_name(self, st_vals):
""" """
@@ -59,7 +59,7 @@ class AccountPaymentOrder(models.Model):
"\nPlease generate first the Payment Order file." "\nPlease generate first the Payment Order file."
) )
) )
elif len(attach) > 1: if len(attach) > 1:
raise UserError( raise UserError(
self.env._( self.env._(
"This payment order contains multiple attachments." "This payment order contains multiple attachments."
@@ -68,39 +68,38 @@ class AccountPaymentOrder(models.Model):
"EBICS Processing > EBICS Upload menu" "EBICS Processing > EBICS Upload menu"
) )
) )
else: origin = self.env._("Payment Order") + ": " + self.name
origin = self.env._("Payment Order") + ": " + self.name if not self.journal_id.ebics_config_id:
if not self.journal_id.ebics_config_id: raise UserError(
raise UserError( self.env._(
self.env._( "No active EBICS configuration available for the selected bank."
"No active EBICS configuration available for the selected bank."
)
) )
ctx.update(
{
"default_ebics_config_id": self.journal_id.ebics_config_id.id,
"default_upload_data": attach.datas,
"default_upload_fname": attach.name,
"origin": origin,
}
) )
ebics_xfer = ( ctx.update(
self.env["ebics.xfer"] {
.with_company(self.company_id) "default_ebics_config_id": self.journal_id.ebics_config_id.id,
.with_context(**ctx) "default_upload_data": attach.datas,
.create({}) "default_upload_fname": attach.name,
) "origin": origin,
ebics_xfer._onchange_ebics_config_id()
ebics_xfer._onchange_upload_data()
view = self.env.ref("account_ebics.ebics_xfer_view_form_upload")
act = {
"name": self.env._("EBICS Upload"),
"view_mode": "form",
"res_model": "ebics.xfer",
"view_id": view.id,
"res_id": ebics_xfer.id,
"type": "ir.actions.act_window",
"target": "new",
"context": ctx,
} }
return act )
ebics_xfer = (
self.env["ebics.xfer"]
.with_company(self.company_id)
.with_context(**ctx)
.create({})
)
ebics_xfer._onchange_ebics_config_id()
ebics_xfer._onchange_upload_data()
view = self.env.ref("account_ebics.ebics_xfer_view_form_upload")
act = {
"name": self.env._("EBICS Upload"),
"view_mode": "form",
"res_model": "ebics.xfer",
"view_id": view.id,
"res_id": ebics_xfer.id,
"type": "ir.actions.act_window",
"target": "new",
"context": ctx,
}
return act