diff --git a/.badges/maturity.svg b/.badges/maturity.svg index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_LmJhZGdlcy9tYXR1cml0eS5zdmc=..2251f1f9fd080dc533640d22b4a5962c91306c54_LmJhZGdlcy9tYXR1cml0eS5zdmc= 100644 --- a/.badges/maturity.svg +++ b/.badges/maturity.svg @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> -<svg xmlns="http://www.w3.org/2000/svg" width="177" height="20"> +<svg xmlns="http://www.w3.org/2000/svg" width="103" height="20"> <linearGradient id="b" x2="0" y2="100%"> <stop offset="0" stop-color="#bbb" stop-opacity=".1" /> <stop offset="1" stop-opacity=".1" /> </linearGradient> <mask id="anybadge_1"> @@ -3,9 +3,9 @@ <linearGradient id="b" x2="0" y2="100%"> <stop offset="0" stop-color="#bbb" stop-opacity=".1" /> <stop offset="1" stop-opacity=".1" /> </linearGradient> <mask id="anybadge_1"> - <rect width="177" height="20" rx="3" fill="#fff" /> + <rect width="103" height="20" rx="3" fill="#fff" /> </mask> <g mask="url(#anybadge_1)"> <path fill="#555" d="M0 0h61v20H0z" /> @@ -9,8 +9,8 @@ </mask> <g mask="url(#anybadge_1)"> <path fill="#555" d="M0 0h61v20H0z" /> - <path fill="#4c1" d="M61 0h116v20H61z" /> - <path fill="url(#b)" d="M0 0h177v20H0z" /> + <path fill="#e05d44" d="M61 0h42v20H61z" /> + <path fill="url(#b)" d="M0 0h103v20H0z" /> </g> <g fill="#fff" @@ -27,7 +27,7 @@ font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11" > - <text x="120.0" y="15" fill="#010101" fill-opacity=".3">Production/Stable</text> - <text x="119.0" y="14">Production/Stable</text> + <text x="83.0" y="15" fill="#010101" fill-opacity=".3">Alpha</text> + <text x="82.0" y="14">Alpha</text> </g> </svg> diff --git a/.editorconfig b/.editorconfig index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_LmVkaXRvcmNvbmZpZw==..2251f1f9fd080dc533640d22b4a5962c91306c54_LmVkaXRvcmNvbmZpZw== 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,7 +11,7 @@ indent_size = 2 # Do not configure editor for libs -[{*/static/{lib,src/lib}/**}] +[*/static/{lib,src/lib}/**] charset = unset end_of_line = unset indent_size = unset diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 0000000000000000000000000000000000000000..2251f1f9fd080dc533640d22b4a5962c91306c54_LmVzbGludHJjLnltbA== --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,187 @@ +env: + browser: true + es6: true + +# See https://github.com/OCA/odoo-community.org/issues/37#issuecomment-470686449 +parserOptions: + ecmaVersion: 2019 + +overrides: + - files: + - "**/*.esm.js" + parserOptions: + sourceType: module + +# Globals available in Odoo that shouldn't produce errorings +globals: + _: readonly + $: readonly + fuzzy: readonly + jQuery: readonly + moment: readonly + odoo: readonly + openerp: readonly + owl: readonly + +# Styling is handled by Prettier, so we only need to enable AST rules; +# see https://github.com/OCA/maintainer-quality-tools/pull/618#issuecomment-558576890 +rules: + accessor-pairs: warn + array-callback-return: warn + callback-return: warn + capitalized-comments: + - warn + - always + - ignoreConsecutiveComments: true + ignoreInlineComments: true + complexity: + - warn + - 15 + constructor-super: warn + dot-notation: warn + eqeqeq: warn + global-require: warn + handle-callback-err: warn + id-blacklist: warn + id-match: warn + init-declarations: error + max-depth: warn + max-nested-callbacks: warn + max-statements-per-line: warn + no-alert: warn + no-array-constructor: warn + no-caller: warn + no-case-declarations: warn + no-class-assign: warn + no-cond-assign: error + no-const-assign: error + no-constant-condition: warn + no-control-regex: warn + no-debugger: error + no-delete-var: warn + no-div-regex: warn + no-dupe-args: error + no-dupe-class-members: error + no-dupe-keys: error + no-duplicate-case: error + no-duplicate-imports: error + no-else-return: warn + no-empty-character-class: warn + no-empty-function: error + no-empty-pattern: error + no-empty: warn + no-eq-null: error + no-eval: error + no-ex-assign: error + no-extend-native: warn + no-extra-bind: warn + no-extra-boolean-cast: warn + no-extra-label: warn + no-fallthrough: warn + no-func-assign: error + no-global-assign: error + no-implicit-coercion: + - warn + - allow: ["~"] + no-implicit-globals: warn + no-implied-eval: warn + no-inline-comments: warn + no-inner-declarations: warn + no-invalid-regexp: warn + no-irregular-whitespace: warn + no-iterator: warn + no-label-var: warn + no-labels: warn + no-lone-blocks: warn + no-lonely-if: error + no-mixed-requires: error + no-multi-str: warn + no-native-reassign: error + no-negated-condition: warn + no-negated-in-lhs: error + no-new-func: warn + no-new-object: warn + no-new-require: warn + no-new-symbol: warn + no-new-wrappers: warn + no-new: warn + no-obj-calls: warn + no-octal-escape: warn + no-octal: warn + no-param-reassign: warn + no-path-concat: warn + no-process-env: warn + no-process-exit: warn + no-proto: warn + no-prototype-builtins: warn + no-redeclare: warn + no-regex-spaces: warn + no-restricted-globals: warn + no-restricted-imports: warn + no-restricted-modules: warn + no-restricted-syntax: warn + no-return-assign: error + no-script-url: warn + no-self-assign: warn + no-self-compare: warn + no-sequences: warn + no-shadow-restricted-names: warn + no-shadow: warn + no-sparse-arrays: warn + no-sync: warn + no-this-before-super: warn + no-throw-literal: warn + no-undef-init: warn + no-undef: error + no-unmodified-loop-condition: warn + no-unneeded-ternary: error + no-unreachable: error + no-unsafe-finally: error + no-unused-expressions: error + no-unused-labels: error + no-unused-vars: error + no-use-before-define: error + no-useless-call: warn + no-useless-computed-key: warn + no-useless-concat: warn + no-useless-constructor: warn + no-useless-escape: warn + no-useless-rename: warn + no-void: warn + no-with: warn + operator-assignment: [error, always] + prefer-const: warn + radix: warn + require-yield: warn + sort-imports: warn + spaced-comment: [error, always] + strict: [error, function] + use-isnan: error + valid-jsdoc: + - warn + - prefer: + arg: param + argument: param + augments: extends + constructor: class + exception: throws + func: function + method: function + prop: property + return: returns + virtual: abstract + yield: yields + preferType: + array: Array + bool: Boolean + boolean: Boolean + number: Number + object: Object + str: String + string: String + requireParamDescription: false + requireReturn: false + requireReturnDescription: false + requireReturnType: false + valid-typeof: warn + yoda: warn diff --git a/.flake8 b/.flake8 index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_LmZsYWtlOA==..2251f1f9fd080dc533640d22b4a5962c91306c54_LmZsYWtlOA== 100644 --- a/.flake8 +++ b/.flake8 @@ -1,4 +1,5 @@ [flake8] +max-line-length = 88 per-file-ignores= __init__.py:F401 __manifest__.py:B018 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_LmdpdGxhYi1jaS55bWw=..2251f1f9fd080dc533640d22b4a5962c91306c54_LmdpdGxhYi1jaS55bWw= 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,11 +1,3 @@ include: - project: xcg/ci-templates - file: /odoo/13.0/gitlab-ci.yaml - -variables: - CI_TEMPLATE_NO_INSTALL_MODULE: "" - -# Pylint does not like that the module is installed in a different path than odoo other addons -pylint: - variables: - CI_TEMPLATE_NO_INSTALL_MODULE: "no" + file: /odoo/16.0/gitlab-ci.yaml diff --git a/.hgignore b/.hgignore index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_LmhnaWdub3Jl..2251f1f9fd080dc533640d22b4a5962c91306c54_LmhnaWdub3Jl 100644 --- a/.hgignore +++ b/.hgignore @@ -1,3 +1,4 @@ syntax: glob ./doc/_build ./doc/autotodo +.project diff --git a/NEWS.rst b/NEWS.rst index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_TkVXUy5yc3Q=..2251f1f9fd080dc533640d22b4a5962c91306c54_TkVXUy5yc3Q= 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -2,6 +2,11 @@ Changelog ========= +16.0.1.0.0 +========== + +Port to Odoo 16. + 13.0.1.3.0 ========== diff --git a/__init__.py b/__init__.py index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_X19pbml0X18ucHk=..2251f1f9fd080dc533640d22b4a5962c91306c54_X19pbml0X18ucHk= 100644 --- a/__init__.py +++ b/__init__.py @@ -1,1 +1,1 @@ -from . import models, reports, wizards # noqa: F401 +from . import models, reports, wizards diff --git a/__manifest__.py b/__manifest__.py index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_X19tYW5pZmVzdF9fLnB5..2251f1f9fd080dc533640d22b4a5962c91306c54_X19tYW5pZmVzdF9fLnB5 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -21,7 +21,7 @@ { "name": "Accounting Reports", "license": "AGPL-3", - "version": "13.0.1.3.0", + "version": "16.0.1.0.0", "category": "Accounting/Accounting", "author": "XCG Consulting", "website": "https://orbeet.io/", @@ -31,4 +31,6 @@ "partner_codes", ], "data": [ + "security/ir.model.access.csv", + "data/account.consultation.type.xml", "menu.xml", @@ -34,3 +36,4 @@ "menu.xml", + "views/account_consultation_type.xml", "wizards/account_consultation.xml", "views/account_move_line.xml", @@ -35,4 +38,3 @@ "wizards/account_consultation.xml", "views/account_move_line.xml", - "views/account_report.xml", ], @@ -38,3 +40,8 @@ ], + "assets": { + "web.assets_backend": [ + "account_report/static/src/less/account_report.less", + ], + }, "installable": True, } diff --git a/data/account.consultation.type.xml b/data/account.consultation.type.xml new file mode 100644 index 0000000000000000000000000000000000000000..2251f1f9fd080dc533640d22b4a5962c91306c54_ZGF0YS9hY2NvdW50LmNvbnN1bHRhdGlvbi50eXBlLnhtbA== --- /dev/null +++ b/data/account.consultation.type.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8" ?> +<odoo noupdate="1"> + <function model="account.consultation.type" name="initialize_account_types" /> +</odoo> diff --git a/doc/autotodo.py b/doc/autotodo.py index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_ZG9jL2F1dG90b2RvLnB5..2251f1f9fd080dc533640d22b4a5962c91306c54_ZG9jL2F1dG90b2RvLnB5 100755 --- a/doc/autotodo.py +++ b/doc/autotodo.py @@ -2,7 +2,7 @@ ############################################################################## # # OpenERP, Open Source Management Solution -# Copyright © 2014, 2018, 2022 XCG Consulting +# Copyright © 2014, 2018, 2022, 2023 XCG Consulting # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -22,6 +22,7 @@ import os import os.path import sys +from collections.abc import Mapping def main(): @@ -33,5 +34,6 @@ exts = sys.argv[2].split(",") tags = sys.argv[3].split(",") todolist = {tag: [] for tag in tags} + path_file_length: Mapping[str, int] = {} for root, _dirs, files in os.walk(folder): @@ -36,6 +38,6 @@ for root, _dirs, files in os.walk(folder): - scan_folder((exts, tags, todolist), root, files) - create_autotodo(folder, todolist) + scan_folder((exts, tags, todolist, path_file_length), root, files) + create_autotodo(folder, todolist, path_file_length) @@ -40,6 +42,6 @@ -def write_info(f, infos, folder): +def write_info(f, infos, folder, path_file_length: Mapping[str, int]): # Check sphinx version for lineno-start support import sphinx @@ -52,10 +54,8 @@ for i in infos: path = i[0] line = i[1] - lines = (line - 3, line + 4) - class_name = ":class:`%s`" % os.path.basename( - os.path.splitext(path)[0] - ) + lines = (line - 3, min(line + 4, path_file_length[path])) + class_name = ":class:`%s`" % os.path.basename(os.path.splitext(path)[0]) f.write( "%s\n" "%s\n\n" @@ -71,7 +71,7 @@ path, lines[0], lines[1], - line, + 4, ) ) if lineno_start: @@ -79,7 +79,7 @@ f.write("\n") -def create_autotodo(folder, todolist): +def create_autotodo(folder, todolist, path_file_length: Mapping[str, int]): with open("autotodo", "w+") as f: for tag, info in list(todolist.items()): f.write("%s\n%s\n\n" % (tag, "=" * len(tag))) @@ -83,7 +83,7 @@ with open("autotodo", "w+") as f: for tag, info in list(todolist.items()): f.write("%s\n%s\n\n" % (tag, "=" * len(tag))) - write_info(f, info, folder) + write_info(f, info, folder, path_file_length) def scan_folder(data_tuple, dirname, names): @@ -87,8 +87,7 @@ def scan_folder(data_tuple, dirname, names): - (exts, tags, res) = data_tuple - file_info = {} + (exts, tags, res, path_file_length) = data_tuple for name in names: (root, ext) = os.path.splitext(name) if ext in exts: @@ -92,9 +91,11 @@ for name in names: (root, ext) = os.path.splitext(name) if ext in exts: - file_info = scan_file(os.path.join(dirname, name), tags) + path = os.path.join(dirname, name) + file_info, length = scan_file(path, tags) + path_file_length[path] = length for tag, info in list(file_info.items()): if info: res[tag].extend(info) @@ -96,12 +97,13 @@ for tag, info in list(file_info.items()): if info: res[tag].extend(info) -def scan_file(filename, tags): - res = {tag: [] for tag in tags} +def scan_file(filename, tags) -> tuple[dict[str, list[tuple[str, int, str]]], int]: + res: dict[str, list[tuple[str, int, str]]] = {tag: [] for tag in tags} + line_num: int = 0 with open(filename, "r") as f: for line_num, line in enumerate(f): for tag in tags: if tag in line: res[tag].append((filename, line_num, line[:-1].strip())) @@ -103,9 +105,9 @@ with open(filename, "r") as f: for line_num, line in enumerate(f): for tag in tags: if tag in line: res[tag].append((filename, line_num, line[:-1].strip())) - return res + return res, line_num if __name__ == "__main__": diff --git a/doc/conf.py b/doc/conf.py index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_ZG9jL2NvbmYucHk=..2251f1f9fd080dc533640d22b4a5962c91306c54_ZG9jL2NvbmYucHk= 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -11,6 +11,10 @@ import configparser import os import sys +from importlib.metadata import PackageNotFoundError +from importlib.metadata import version as import_version + +from odoo_scripts.config import Configuration import odoo @@ -14,8 +18,6 @@ import odoo -from odoo_scripts.config import Configuration - # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -33,7 +35,8 @@ "sphinx.ext.todo", "sphinx.ext.coverage", "sphinx.ext.graphviz", - "sphinxodoo.ext.autodoc", + "sphinx.ext.viewcode", + "sphinxodooautodoc.ext.autodoc", ] # Add any paths that contain templates here, relative to this directory. @@ -49,7 +52,16 @@ # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. -# + +# The full version, including alpha/beta/rc tags. +try: + release = import_version("odoo-addon-account-report") + # The short X.Y version. + version = ".".join(release.split(".")[:2]) +except PackageNotFoundError: + # No version number if not installed + pass + with open(os.path.join("..", "__manifest__.py"), "r") as f: read_data = f.read() d = ast.literal_eval(read_data) @@ -53,10 +65,6 @@ with open(os.path.join("..", "__manifest__.py"), "r") as f: read_data = f.read() d = ast.literal_eval(read_data) -# The full version, including alpha/beta/rc tags. -release = d["version"] -# The short X.Y version. -version = ".".join(release.split(".")[:4]) # General information about the project. project = d["name"] @@ -60,7 +68,7 @@ # General information about the project. project = d["name"] -copyright = "2021 XCG Consulting" +copyright = "2021, 2022, 2023, 2024 XCG Consulting" author = d["author"] module_nospace = project.replace(" ", "") module_description = d.get("summary", "") @@ -121,9 +129,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, module_lowercase, "%s Documentation" % project, [author], 1) -] +man_pages = [(master_doc, module_lowercase, "%s Documentation" % project, [author], 1)] # -- Options for Texinfo output ------------------------------------------- @@ -146,7 +152,5 @@ intersphinx_mapping = {"https://docs.python.org/3/": None} -# -# odoo-sphinx-autodoc -# +# -- Options for sphinx-odoo-autodoc extension ---------------------------- @@ -152,6 +156,6 @@ -# sphinxodoo_addons: List of addons name to load (if empty, no addon will be +# sphinxodooautodoc_addons : List of addons name to load (if empty, no addon will be # loaded) this_module = os.path.basename( os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ) @@ -154,9 +158,9 @@ # loaded) this_module = os.path.basename( os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ) -sphinxodoo_addons = [this_module] -# sphinxodoo_root_path : Path of the Odoo root directory -sphinxodoo_root_path = os.path.dirname( +sphinxodooautodoc_addons = [this_module] +# sphinxodooautodoc_root_path : Path of the Odoo root directory +sphinxodooautodoc_root_path = os.path.dirname( os.path.dirname(os.path.abspath(odoo.__file__)) ) @@ -161,6 +165,6 @@ os.path.dirname(os.path.abspath(odoo.__file__)) ) -# sphinxodoo_addons_path : List of paths were Odoo addons to load are located +# sphinxodooautodoc_addons_path : List of paths were Odoo addons to load are located c = None # find setup file of superproject, if any directory = os.path.dirname(os.getenv("PWD")) @@ -171,7 +175,7 @@ c.read(setup_path) if c.has_section("odoo_scripts"): # reload with odoo_scripts - c = Configuration(setup_path) + c = Configuration(directory) else: c = None if not c: @@ -180,9 +184,9 @@ else: directory = None -sphinxodoo_addons_path = [] +sphinxodooautodoc_addons_path = [] if c: addon_dirs = set(os.path.dirname(path) for path in c.modules) for line in addon_dirs: @@ -184,8 +188,8 @@ if c: addon_dirs = set(os.path.dirname(path) for path in c.modules) for line in addon_dirs: - sphinxodoo_addons_path.append(os.path.join(directory, line)) + sphinxodooautodoc_addons_path.append(os.path.join(directory, line)) else: # add this directory top dir @@ -190,8 +194,8 @@ else: # add this directory top dir - sphinxodoo_addons_path.append( + sphinxodooautodoc_addons_path.append( os.path.dirname(os.path.dirname(os.getenv("PWD"))) ) other_addons = os.getenv("ODOO_ADDONS_PATH", default=None) if other_addons: for addon_path in other_addons.split(","): @@ -193,6 +197,6 @@ os.path.dirname(os.path.dirname(os.getenv("PWD"))) ) other_addons = os.getenv("ODOO_ADDONS_PATH", default=None) if other_addons: for addon_path in other_addons.split(","): - sphinxodoo_addons_path.append(addon_path) + sphinxodooautodoc_addons_path.append(addon_path) diff --git a/doc/index.rst b/doc/index.rst index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_ZG9jL2luZGV4LnJzdA==..2251f1f9fd080dc533640d22b4a5962c91306c54_ZG9jL2luZGV4LnJzdA== 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,4 +1,4 @@ .. |coverage| image:: .badges/coverage.svg - :target: https://orus.io/xcg/odoo-modules/account_report/-/pipelines?ref=branch/13.0 + :target: https://orus.io/xcg/odoo-modules/account_report/-/pipelines?ref=branch/16.0 :alt: Coverage report .. |pylint| image:: .badges/pylint.svg @@ -3,6 +3,6 @@ :alt: Coverage report .. |pylint| image:: .badges/pylint.svg - :target: https://orus.io/xcg/odoo-modules/account_report/-/pipelines?ref=branch/13.0 + :target: https://orus.io/xcg/odoo-modules/account_report/-/pipelines?ref=branch/16.0 :alt: pylint score |coverage| |pylint| diff --git a/models/__init__.py b/models/__init__.py index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_bW9kZWxzL19faW5pdF9fLnB5..2251f1f9fd080dc533640d22b4a5962c91306c54_bW9kZWxzL19faW5pdF9fLnB5 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,1 +1,1 @@ -from . import account_move, account_move_line # noqa: F401 +from . import account_consultation_type, account_move, account_move_line diff --git a/models/account_consultation_type.py b/models/account_consultation_type.py new file mode 100644 index 0000000000000000000000000000000000000000..2251f1f9fd080dc533640d22b4a5962c91306c54_bW9kZWxzL2FjY291bnRfY29uc3VsdGF0aW9uX3R5cGUucHk= --- /dev/null +++ b/models/account_consultation_type.py @@ -0,0 +1,59 @@ +############################################################################## +# +# Accounting Reports, for Odoo +# Copyright © 2024 XCG Consulting <https://xcg-consulting.fr> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +from odoo import api, fields, models + + +class AccountConsultationType(models.Model): + """Dialog box entry corresponding to an account type.""" + + _name = "account.consultation.type" + _description = "Account consultation dialog box - Account type" + + _rec_name = "account_type" + + @api.model + def selection_account_type(self): + + return self.env["account.account"].fields_get( + allfields=["account_type"], attributes=["selection"] + )["account_type"]["selection"] + + account_type = fields.Selection( + selection=selection_account_type, + string="Account Type", + index=True, + required=True, + readonly=True, + ) + + _sql_constraints = [ + ("unique_account_type", "UNIQUE(account_type)", "The account type is unique.") + ] + + @api.model + def initialize_account_types(self): + + return self.sudo().create( + [ + {"account_type": account_type} + for account_type in dict(self.selection_account_type()).keys() + ] + ) diff --git a/models/account_move_line.py b/models/account_move_line.py index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_bW9kZWxzL2FjY291bnRfbW92ZV9saW5lLnB5..2251f1f9fd080dc533640d22b4a5962c91306c54_bW9kZWxzL2FjY291bnRfbW92ZV9saW5lLnB5 100644 --- a/models/account_move_line.py +++ b/models/account_move_line.py @@ -18,8 +18,7 @@ # ############################################################################## -from odoo import _, api, fields, models -from odoo.exceptions import UserError +from odoo import api, fields, models class AccountingEntry(models.Model): @@ -40,60 +39,3 @@ help="debit - credit", store=True, ) - - # XXX why is that done in this module? - def control_reconciliation_access(self): - """Correct the controls performed by Odoo on accounting entries - before reconciliation, for these contain errors. - """ - - if not self: - return True - - # Perform all checks on lines - company_ids = set() - all_accounts = [] - partners = set() - for line in self: - company_ids.add(line.company_id.id) - all_accounts.append(line.account_id) - partners.add(line.partner_id.id) - - if len(company_ids) > 1: - raise UserError( - _( - "To reconcile the entries company should be the same for " - "all entries!" - ) - ) - partners.discard(False) - if len(set(all_accounts)) > 1: - raise UserError(_("Entries are not of the same account!")) - if not ( - all_accounts[0].reconcile - or all_accounts[0].internal_type == "liquidity" - ): - raise UserError( - _( - "The account %(name)s (%(code)s) is not marked as " - "reconciliable!" - ) - % {"name": all_accounts[0].name, "code": all_accounts[0].code} - ) - - return True - - def reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False): - - self.control_reconciliation_access() - - return super().reconcile( - writeoff_acc_id=writeoff_acc_id, - writeoff_journal_id=writeoff_journal_id, - ) - - def force_full_reconcile(self): - - self.control_reconciliation_access() - - return super().force_full_reconcile() diff --git a/pyproject.toml b/pyproject.toml index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_cHlwcm9qZWN0LnRvbWw=..2251f1f9fd080dc533640d22b4a5962c91306c54_cHlwcm9qZWN0LnRvbWw= 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,10 +2,10 @@ name = "odoo-addon-account-report" dynamic = ["version"] readme = "README.rst" -requires-python = "~=3.8" +requires-python = "~=3.10" license = { file = "LICENSE", name = "GNU Affero General Public License v3" } keywords = ["odoo"] authors = [{ name = "XCG Consulting" }] classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", @@ -6,8 +6,9 @@ license = { file = "LICENSE", name = "GNU Affero General Public License v3" } keywords = ["odoo"] authors = [{ name = "XCG Consulting" }] classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Framework :: Odoo", @@ -13,5 +14,5 @@ "Framework :: Odoo", - "Framework :: Odoo :: 13.0", + "Framework :: Odoo :: 16.0", "License :: OSI Approved :: GNU Affero General Public License v3" ] dependencies = [ @@ -15,10 +16,10 @@ "License :: OSI Approved :: GNU Affero General Public License v3" ] dependencies = [ - "odoo ==13.0.*", - "odoo-addon-account_analytic_structure >=13.0.1.0.1,<13.0.2", - "odoo-addon-account_export >=13.0.1.0.1,<13.0.2", - "odoo-addon-partner_codes >=13.0.1.0.0,<13.0.2", + "odoo==16.0.*", + "odoo-addon-account-analytic-structure==16.0.*", + "odoo-addon-account_export <16.0.2", + "odoo-addon-partner_codes >=16.0.1.0.2,<16.0.2", ] [project.optional-dependencies] @@ -27,7 +28,7 @@ [project.urls] repository = "https://orus.io/xcg/odoo-modules/account_report" -changelog = "https://orus.io/xcg/odoo-modules/account_report/-/blob/branch/13.0/NEWS.rst" +changelog = "https://orus.io/xcg/odoo-modules/account_report/-/blob/branch/16.0/NEWS.rst" [build-system] requires = ["hatchling >=1.19", "hatch-vcs"] @@ -65,8 +66,7 @@ source = "vcs" [tool.black] -line-length = 79 -target-version = ["py38"] -required-version = "19.3b0" +target-version = ["py310", "py311"] +required-version = "22" [tool.isort] @@ -71,8 +71,7 @@ [tool.isort] -py_version = 38 -multi_line_output = 3 -include_trailing_comma = true -force_grid_wrap = 0 -combine_as_imports = true +py_version = 310 +profile = "black" +known_odoo = ['odoo'] +known_odoo_addons = ['odoo.addons'] sections = [ @@ -78,9 +77,9 @@ sections = [ - "FUTURE", - "STDLIB", - "SPECIAL_THIRD_PARTY", - "ODOO", - "ODOO_ADDONS", - "LOCALFOLDER", - "THIRDPARTY" + 'FUTURE', + 'STDLIB', + 'THIRDPARTY', + 'ODOO', + 'ODOO_ADDONS', + 'FIRSTPARTY', + 'LOCALFOLDER' ] @@ -86,12 +85,1 @@ ] -known_odoo = ["odoo"] -known_odoo_addons = ["odoo.addons"] -known_special_third_party = [ - "dateutil", - "lxml", - "mock", - "requests", - "minio", - "werkzeug" -] -default_section = "THIRDPARTY" diff --git a/reports/__init__.py b/reports/__init__.py index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_cmVwb3J0cy9fX2luaXRfXy5weQ==..2251f1f9fd080dc533640d22b4a5962c91306c54_cmVwb3J0cy9fX2luaXRfXy5weQ== 100644 --- a/reports/__init__.py +++ b/reports/__init__.py @@ -1,3 +1,1 @@ -# flake8: noqa - from . import account_statement diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv new file mode 100644 index 0000000000000000000000000000000000000000..2251f1f9fd080dc533640d22b4a5962c91306c54_c2VjdXJpdHkvaXIubW9kZWwuYWNjZXNzLmNzdg== --- /dev/null +++ b/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +account_consultation_type_user,account_consultation_type_user,model_account_consultation_type,base.group_user,1,0,0,0 +account_consultation_type_manager,account_consultation_type_manager,model_account_consultation_type,account.group_account_manager,1,1,1,1 +access_account_consultation,access_account_consultation,model_account_consultation,base.group_user,1,1,1,1 diff --git a/tests/__init__.py b/tests/__init__.py index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_dGVzdHMvX19pbml0X18ucHk=..2251f1f9fd080dc533640d22b4a5962c91306c54_dGVzdHMvX19pbml0X18ucHk= 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,3 +1,1 @@ -# flake8: noqa - from . import test_account_report diff --git a/tests/test_account_report.py b/tests/test_account_report.py index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_dGVzdHMvdGVzdF9hY2NvdW50X3JlcG9ydC5weQ==..2251f1f9fd080dc533640d22b4a5962c91306c54_dGVzdHMvdGVzdF9hY2NvdW50X3JlcG9ydC5weQ== 100644 --- a/tests/test_account_report.py +++ b/tests/test_account_report.py @@ -26,5 +26,6 @@ # Default amount set on accounting documents. AMOUNT = 42.0 +TAX_INCLUSIVE_AMOUNT = 48.3 # 15 % of taxes @@ -29,5 +30,4 @@ -@odoo.tests.common.at_install(False) -@odoo.tests.common.post_install(True) +@odoo.tests.tagged("-at_install", "post_install") class Test(odoo.tests.TransactionCase): @@ -33,5 +33,4 @@ class Test(odoo.tests.TransactionCase): - """Test this ``account_report`` module. - """ + """Test this ``account_report`` module.""" def setUp(self): @@ -36,7 +35,6 @@ def setUp(self): - """Set up elements we are going to use here. - """ + """Set up elements we are going to use here.""" super().setUp() @@ -50,11 +48,11 @@ ) self.assertTrue(self.pay_journal) - self.debit_account = self._createAccount("receivable") - self.credit_account = self._createAccount("other") + self.debit_account = self._createAccount("asset_receivable") + self.credit_account = self._createAccount("income_other") self.product = self.env.ref("product.product_product_1") self.a_period = self._create_account_period() def test_consult_account(self): @@ -55,11 +53,10 @@ self.product = self.env.ref("product.product_product_1") self.a_period = self._create_account_period() def test_consult_account(self): - """Consult an account. - """ + """Consult an account.""" account = self.debit_account @@ -86,7 +83,7 @@ self.assertEqual(self._consult_account(account), accentry.ids) # Ask for drafts only; should now find nothing to export. - with self.assertRaises(odoo.exceptions.Warning): + with self.assertRaises(odoo.exceptions.UserError): self._consult_account(account, transaction_state="draft") # Close the period; we should still find our entry as closed periods @@ -95,7 +92,7 @@ self.assertEqual(self._consult_account(account), accentry.ids) # Ask for open periods only; should now find nothing to export. - with self.assertRaises(odoo.exceptions.Warning): + with self.assertRaises(odoo.exceptions.UserError): self._consult_account(account, include_closed_periods=False) def test_invoice_paid_amount(self): @@ -99,8 +96,7 @@ self._consult_account(account, include_closed_periods=False) def test_invoice_paid_amount(self): - """Check the "Paid amount" field we have added into invoices. - """ + """Check the "Paid amount" field we have added into invoices.""" # Create an invoice. client = self._createPartner() @@ -110,9 +106,9 @@ # Validate the invoice. self.assertEqual(invoice.state, "draft") self._validateAccDoc(invoice) - invoice.refresh() + invoice.invalidate_model() self.assertEqual(invoice.state, "posted") self.assertEqual(invoice.paid_amount, 0.0) # No payments so far. # Partial payment. self._payInvoice(invoice, 2.0) @@ -114,11 +110,11 @@ self.assertEqual(invoice.state, "posted") self.assertEqual(invoice.paid_amount, 0.0) # No payments so far. # Partial payment. self._payInvoice(invoice, 2.0) - invoice.refresh() + invoice.invalidate_model() self.assertEqual(invoice.paid_amount, 2.0) self.assertEqual(invoice.state, "posted") # Partial payment. self._payInvoice(invoice, 4.0) @@ -120,10 +116,10 @@ self.assertEqual(invoice.paid_amount, 2.0) self.assertEqual(invoice.state, "posted") # Partial payment. self._payInvoice(invoice, 4.0) - invoice.refresh() + invoice.invalidate_model() self.assertEqual(invoice.paid_amount, 2.0 + 4.0) self.assertEqual(invoice.state, "posted") # Pay the rest. @@ -126,11 +122,11 @@ self.assertEqual(invoice.paid_amount, 2.0 + 4.0) self.assertEqual(invoice.state, "posted") # Pay the rest. - self._payInvoice(invoice, AMOUNT - 2.0 - 4.0) - invoice.refresh() - self.assertEqual(invoice.paid_amount, AMOUNT) - self.assertEqual(invoice.invoice_payment_state, "paid") + self._payInvoice(invoice, TAX_INCLUSIVE_AMOUNT - 2.0 - 4.0) + invoice.invalidate_model() + self.assertEqual(invoice.paid_amount, TAX_INCLUSIVE_AMOUNT) + self.assertEqual(invoice.payment_state, "paid") def _consult_account(self, account, **kwargs): """Consult the specified account; return extracted accounting entries. @@ -142,9 +138,7 @@ aconsult_dlg_values = {"account_id": account.id} aconsult_dlg_values.update(kwargs) - aconsult_dlg = self.env["account.consultation"].create( - aconsult_dlg_values - ) + aconsult_dlg = self.env["account.consultation"].create(aconsult_dlg_values) self.assertTrue(aconsult_dlg.account_type_ids) dlg_ret = aconsult_dlg.open_general_list() @@ -163,6 +157,5 @@ return self.env["account.account"].create( { "code": uuid.uuid4().hex[:8], - "internal_type": account_type, "name": uuid.uuid4().hex, "reconcile": True, @@ -167,10 +160,6 @@ "name": uuid.uuid4().hex, "reconcile": True, - "user_type_id": ( - self.env["account.account.type"] - .search([("type", "=", account_type)], limit=1) - .id - ), + "account_type": account_type, } ) @@ -244,7 +233,7 @@ return self.env["account.move"].create( { "partner_id": client.id, - "type": "out_invoice", + "move_type": "out_invoice", "invoice_line_ids": [ ( 0, # 0: Create. @@ -276,20 +265,15 @@ ) self.assertEqual(len(acc_entry_to_pay), 1) - self.env["account.payment"].create( - { - "amount": amount_to_pay, - "journal_id": self.pay_journal.id, - "partner_id": invoice.partner_id.id, - "partner_type": "customer", - "payment_method_id": self.env.ref( - "account.account_payment_method_manual_in" - ).id, - "payment_type": "inbound", - } - ).with_context( - payments_per_aml_id={acc_entry_to_pay.id: amount_to_pay} - ).post() + wizard_obj = self.env["account.payment.register"].with_context( + active_model="account.move.line", + active_ids=acc_entry_to_pay.ids, + ) + + wizard = wizard_obj.create({"amount": amount_to_pay}) + wizard.action_create_payments() + + return True def _validateAccDoc(self, accdoc): """Validate the specified accounting document. @@ -298,7 +282,7 @@ """ self.assertEqual(accdoc.state, "draft") - accdoc.post() + accdoc.action_post() self.assertEqual(accdoc.state, "posted") def _pay_partiallyAccDoc(self, accdoc): @@ -308,7 +292,7 @@ """ self.assertEqual(accdoc.state, "draft") - accdoc.post() + accdoc.action_post() self.assertEqual(accdoc.state, "posted") def _create_account_period(self): @@ -316,13 +300,13 @@ :rtype: Odoo "account.period" record. """ - # Create an account.fiscalyear - a_fiscalyear = self.env["account.fiscalyear"].create( - { - "name": "Fiscal Year X " + time.strftime("%Y"), - "code": "FY" + time.strftime("%Y"), - "company_id": self.env.ref("base.main_company").id, - "date_stop": time.strftime("%Y") + "-12-31", - } + + fiscalyear = self.env["account.fiscal.year"].search( + [ + ("date_from", "=", time.strftime("%Y") + "-01-01"), + ("date_to", "=", time.strftime("%Y") + "-12-31"), + ("company_id", "=", self.env.ref("base.main_company").id), + ], + limit=1, ) @@ -327,15 +311,30 @@ ) - # Create an account.period - a_period = self.env["account.period"].create( - { - "code": "FY" + time.strftime("%Y"), - "name": "Fiscal Year X " + time.strftime("%Y"), - "fiscalyear_id": a_fiscalyear.id, - "date_start": time.strftime("%Y") + "-01-01", - "date_stop": time.strftime("%Y") + "-12-31", - "company_id": self.env.ref("base.main_company").id, - } - ) + if not fiscalyear: + + # Create an account.fiscal.year + a_fiscalyear = self.env["account.fiscal.year"].create( + { + "name": "Fiscal Year X " + time.strftime("%Y"), + "company_id": self.env.ref("base.main_company").id, + "date_from": time.strftime("%Y") + "-01-01", + "date_to": time.strftime("%Y") + "-12-31", + } + ) + + # Create an account.period + a_period = self.env["account.period"].create( + { + "code": "FY" + time.strftime("%Y"), + "name": "Fiscal Year X " + time.strftime("%Y"), + "fiscalyear_id": a_fiscalyear.id, + "date_start": time.strftime("%Y") + "-01-01", + "date_stop": time.strftime("%Y") + "-12-31", + "company_id": self.env.ref("base.main_company").id, + } + ) + + else: + a_period = fiscalyear.period_ids[0] return a_period diff --git a/views/account_consultation_type.xml b/views/account_consultation_type.xml new file mode 100644 index 0000000000000000000000000000000000000000..2251f1f9fd080dc533640d22b4a5962c91306c54_dmlld3MvYWNjb3VudF9jb25zdWx0YXRpb25fdHlwZS54bWw= --- /dev/null +++ b/views/account_consultation_type.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8" ?> +<odoo> + <!-- Account type viewss, to be shown from the account consultation dialog. --> + + <record id="account_consultation_type_search" model="ir.ui.view"> + <field name="name">account_consultation_type_search</field> + <field name="model">account.consultation.type</field> + <field name="arch" type="xml"> + <search> + <field name="account_type" /> + </search> + </field> + </record> + + <record id="account_consultation_type_list" model="ir.ui.view"> + <field name="name">account_consultation_type_list</field> + <field name="model">account.consultation.type</field> + <field name="arch" type="xml"> + <tree> + <field name="account_type" /> + </tree> + </field> + </record> + + <record id="account_consultation_type_form" model="ir.ui.view"> + <field name="name">account_consultation_type_form</field> + <field name="model">account.consultation.type</field> + <field name="arch" type="xml"> + <form> + <sheet> + <group> + <field name="account_type" /> + </group> + </sheet> + </form> + </field> + </record> +</odoo> diff --git a/views/account_move_line.xml b/views/account_move_line.xml index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_dmlld3MvYWNjb3VudF9tb3ZlX2xpbmUueG1s..2251f1f9fd080dc533640d22b4a5962c91306c54_dmlld3MvYWNjb3VudF9tb3ZlX2xpbmUueG1s 100644 --- a/views/account_move_line.xml +++ b/views/account_move_line.xml @@ -14,7 +14,6 @@ <field name="move_id" /> <field name="period_id" /> <field name="date" /> - <field name="transaction_date" string="Transaction date" /> <field name="date_maturity" /> <field name="journal_id" /> <field name="partner_id" /> @@ -18,7 +17,6 @@ <field name="date_maturity" /> <field name="journal_id" /> <field name="partner_id" /> - <field name="partner_code" /> <field name="account_id" /> <field name="name" /> <field name="full_reconcile_id" /> @@ -41,7 +39,6 @@ <field name="move_id" /> <field name="period_id" /> <field name="partner_id" /> - <field name="partner_code" /> <field name="account_id" /> <field name="name" /> <field name="tax_line_id" /> diff --git a/views/account_report.xml b/views/account_report.xml deleted file mode 100644 index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_dmlld3MvYWNjb3VudF9yZXBvcnQueG1s..0000000000000000000000000000000000000000 --- a/views/account_report.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<odoo> - <template - id="assets_backend" - name="account report assets" - inherit_id="web.assets_backend" - > - <xpath expr="." position="inside"> - <link - rel="stylesheet" - href="/account_report/static/src/less/account_report.less" - /> - </xpath> - </template> -</odoo> diff --git a/wizards/__init__.py b/wizards/__init__.py index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_d2l6YXJkcy9fX2luaXRfXy5weQ==..2251f1f9fd080dc533640d22b4a5962c91306c54_d2l6YXJkcy9fX2luaXRfXy5weQ== 100644 --- a/wizards/__init__.py +++ b/wizards/__init__.py @@ -1,3 +1,1 @@ -# flake8: noqa - from . import account_consultation diff --git a/wizards/account_consultation.py b/wizards/account_consultation.py index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_d2l6YXJkcy9hY2NvdW50X2NvbnN1bHRhdGlvbi5weQ==..2251f1f9fd080dc533640d22b4a5962c91306c54_d2l6YXJkcy9hY2NvdW50X2NvbnN1bHRhdGlvbi5weQ== 100644 --- a/wizards/account_consultation.py +++ b/wizards/account_consultation.py @@ -37,5 +37,5 @@ ) account_type_ids = fields.Many2many( - comodel_name="account.account.type", + comodel_name="account.consultation.type", string="Account categories", @@ -41,5 +41,5 @@ string="Account categories", - default=lambda self: self.env["account.account.type"].search([]), + default=lambda self: self.env["account.consultation.type"].search([]), ondelete="cascade", help="Type used to filter selectable accounting accounts.", ) @@ -156,7 +156,9 @@ partner = self.partner_id - if self.account_id.user_type_id.id not in self.account_type_ids.ids: + if self.account_id.account_type not in self.account_type_ids.mapped( + "account_type" + ): self.account_id = self.env["account.account"] # Clear out. account_domain = ( @@ -160,7 +162,7 @@ self.account_id = self.env["account.account"] # Clear out. account_domain = ( - [("user_type_id", "in", self.account_type_ids.ids)] + [("account_type", "in", self.account_type_ids.mapped("account_type"))] if self.account_type_ids else [] ) @@ -246,7 +248,7 @@ amls = self.env["account.move.line"].search(self._build_aml_domain()) if not amls: - raise exceptions.Warning( + raise exceptions.UserError( _( "No accounting entry could be found with the specified " "parameters." @@ -272,8 +274,7 @@ } def open_general_list(self): - """Open the accounting entry list for the specified account. - """ + """Open the accounting entry list for the specified account.""" self.ensure_one() @@ -283,8 +284,7 @@ ) def open_analytic_list(self): - """Open the accounting entry list for the specified account. - """ + """Open the accounting entry list for the specified account.""" self.ensure_one() @@ -305,7 +305,7 @@ amls = self.env["account.move.line"].search(self._build_aml_domain()) if not amls: - raise exceptions.Warning( + raise exceptions.UserError( _( "No accounting entry could be found with the specified " "parameters." @@ -356,10 +356,12 @@ elif partner: aml_domain = [("partner_id", "=", partner.id)] - aml_domain.append(("account_id.user_type_id", "in", categories.ids)) + aml_domain.append( + ("account_id.account_type", "in", categories.mapped("account_type")) + ) period_states = ["draft"] if self.include_closed_periods: period_states.append("done") period_domain = [("state", "in", period_states)] if self.period_from_id: @@ -360,10 +362,8 @@ period_states = ["draft"] if self.include_closed_periods: period_states.append("done") period_domain = [("state", "in", period_states)] if self.period_from_id: - period_domain.append( - ("date_start", ">=", self.period_from_id.date_start) - ) + period_domain.append(("date_start", ">=", self.period_from_id.date_start)) if self.period_to_id: @@ -369,7 +369,5 @@ if self.period_to_id: - period_domain.append( - ("date_start", "<=", self.period_to_id.date_start) - ) + period_domain.append(("date_start", "<=", self.period_to_id.date_start)) periods = self.env["account.period"].search(period_domain) aml_domain.append(("period_id", "in", periods.ids)) @@ -421,9 +419,7 @@ self.norefs_matching_count = norefs_count if not ref_count: - self.refs_matching = _( - "Accounting entries found have no reference." - ) + self.refs_matching = _("Accounting entries found have no reference.") elif ref_count <= 6: self.refs_matching = ", ".join(aml_refs) @@ -432,6 +428,4 @@ # We only care abbout the first 3 and the last 3. first_3 = aml_refs[:3] last_3 = aml_refs[-3:] - self.refs_matching = ( - ", ".join(first_3) + ", [....], " + ", ".join(last_3) - ) + self.refs_matching = ", ".join(first_3) + ", [....], " + ", ".join(last_3) diff --git a/wizards/account_consultation.xml b/wizards/account_consultation.xml index ce4b3ae5b9f70b577e928f878c1786872fec9b9e_d2l6YXJkcy9hY2NvdW50X2NvbnN1bHRhdGlvbi54bWw=..2251f1f9fd080dc533640d22b4a5962c91306c54_d2l6YXJkcy9hY2NvdW50X2NvbnN1bHRhdGlvbi54bWw= 100644 --- a/wizards/account_consultation.xml +++ b/wizards/account_consultation.xml @@ -94,7 +94,7 @@ /> </div> - <label for="ref_filter" string="Reference: Filter" /> + <span class="o_form_label">Reference: Filter</span> <div class="fieldblock"> <span>from</span> <field name="ref_from" nolabel="1" class="oe_inline" /> @@ -112,8 +112,7 @@ </group> <group string="Matching references"> - <label - for="" - string="No accounting entry could be found with the specified parameters." + <span + class="o_form_label" attrs="{'invisible': [('matching_count', '!=', 0)]}" colspan="2" @@ -118,6 +117,9 @@ attrs="{'invisible': [('matching_count', '!=', 0)]}" colspan="2" - /> + > + No accounting entry could be found with the specified + parameters. + </span> <field name="matching_count" string="Matching entries"