Merge PR #2016 into 18.0

Signed-off-by moylop260
This commit is contained in:
OCA-git-bot
2025-01-29 07:55:16 +00:00
43 changed files with 3928 additions and 0 deletions

10
.bandit Normal file
View File

@@ -0,0 +1,10 @@
[bandit]
exclude = tests
# B608: hardcoded_sql_expressions they are considered from pylint-odoo
# B313,B314,B315,B316,B317,B318,B319,B320,B405,B406,B407,B408,B409,B410: defusedxml checks
# Odoo's team via inbox twitter is not convinced to repair them and the entire Odoo is raising these checks
skips = B608,B313,B314,B315,B316,B317,B318,B319,B320,B405,B406,B407,B408,B409,B410
format = custom
msg-template={relpath}:{line}:{col} {msg} - [{test_id}] - ({severity})
quiet=True

243
.eslintrc.json Normal file
View File

@@ -0,0 +1,243 @@
{
"globals": {
"$": false,
"_": false,
"jQuery": false,
"moment": false,
"odoo": false,
"openerp": false,
"self": false
},
"env": {
"browser": true
},
"rules": {
"capitalized-comments": "off",
"no-alert": "off",
"no-array-constructor": "error",
"no-bitwise": "off",
"no-caller": "error",
"no-case-declarations": "off",
"no-catch-shadow": "error",
"no-class-assign": "error",
"no-cond-assign": "off",
"no-confusing-arrow": "error",
"no-console": "off",
"no-const-assign": "error",
"no-constant-condition": "off",
"no-continue": "off",
"no-control-regex": "error",
"no-debugger": "error",
"no-delete-var": "error",
"no-div-regex": "error",
"no-dupe-args": "error",
"no-dupe-class-members": "error",
"no-dupe-keys": "off",
"no-duplicate-case": "error",
"no-duplicate-imports": "error",
"no-else-return": "off",
"no-empty": "off",
"no-empty-character-class": "error",
"no-empty-function": "off",
"no-empty-pattern": "error",
"no-eq-null": "off",
"no-eval": "error",
"no-ex-assign": "error",
"no-extend-native": "off",
"no-extra-bind": "error",
"no-extra-boolean-cast": "error",
"no-extra-label": "error",
"no-extra-parens": "off",
"no-extra-semi": "off",
"no-fallthrough": "error",
"no-floating-decimal": "error",
"no-func-assign": "error",
"no-global-assign": "off",
"no-implicit-coercion": "off",
"no-implicit-globals": "off",
"no-implied-eval": "error",
"no-inline-comments": "off",
"no-inner-declarations": "off",
"no-invalid-regexp": "error",
"no-invalid-this": "off",
"no-irregular-whitespace": "error",
"no-iterator": "error",
"no-label-var": "error",
"no-labels": "error",
"no-lone-blocks": "error",
"no-lonely-if": "off",
"no-loop-func": "off",
"no-magic-numbers": "off",
"no-mixed-operators": "off",
"no-mixed-requires": "off",
"no-mixed-spaces-and-tabs": "error",
"no-multi-spaces": "off",
"no-multi-str": "error",
"no-multiple-empty-lines": "off",
"no-native-reassign": "off",
"no-negated-condition": "off",
"no-negated-in-lhs": "error",
"no-nested-ternary": "off",
"no-new": "error",
"no-new-func": "error",
"no-new-object": "error",
"no-new-require": "error",
"no-new-symbol": "error",
"no-new-wrappers": "error",
"no-obj-calls": "error",
"no-octal": "error",
"no-octal-escape": "error",
"no-param-reassign": "off",
"no-path-concat": "error",
"no-plusplus": "off",
"no-process-env": "error",
"no-process-exit": "error",
"no-proto": "error",
"no-prototype-builtins": "off",
"no-redeclare": "off",
"no-regex-spaces": "error",
"no-restricted-globals": "error",
"no-restricted-imports": "error",
"no-restricted-modules": "error",
"no-restricted-syntax": "error",
"no-return-assign": "off",
"no-script-url": "error",
"no-self-assign": "off",
"no-self-compare": "error",
"no-sequences": "off",
"no-shadow": "off",
"no-shadow-restricted-names": "error",
"no-whitespace-before-property": "error",
"no-spaced-func": "off",
"no-sparse-arrays": "error",
"no-sync": "error",
"no-tabs": "error",
"no-ternary": "off",
"no-trailing-spaces": "off",
"no-this-before-super": "error",
"no-throw-literal": "error",
"no-undef": "off",
"no-undef-init": "error",
"no-undefined": "off",
"no-unexpected-multiline": "error",
"no-underscore-dangle": "off",
"no-unmodified-loop-condition": "error",
"no-unneeded-ternary": "off",
"no-unreachable": "error",
"no-unsafe-finally": "error",
"no-unused-expressions": "off",
"no-unused-labels": "off",
"no-unused-vars": "off",
"no-use-before-define": "off",
"no-useless-call": "off",
"no-useless-computed-key": "error",
"no-useless-concat": "error",
"no-useless-constructor": "off",
"no-useless-escape": "off",
"no-useless-rename": "error",
"no-void": "error",
"no-var": "off",
"no-warning-comments": "off",
"no-with": "error",
"array-bracket-spacing": "off",
"array-callback-return": "error",
"arrow-body-style": "off",
"arrow-parens": "off",
"arrow-spacing": "off",
"accessor-pairs": "error",
"block-scoped-var": "off",
"block-spacing": "off",
"brace-style": "off",
"callback-return": "error",
"camelcase": "off",
"comma-dangle": "off",
"comma-spacing": "off",
"comma-style": "error",
"complexity": "off",
"computed-property-spacing": "off",
"consistent-return": "off",
"consistent-this": "off",
"constructor-super": "error",
"curly": "off",
"default-case": "off",
"dot-location": ["error", "property"],
"dot-notation": "off",
"eol-last": "error",
"eqeqeq": "off",
"func-names": "off",
"func-style": "off",
"generator-star-spacing": "off",
"global-require": "error",
"guard-for-in": "off",
"handle-callback-err": "error",
"id-blacklist": "error",
"id-length": "off",
"id-match": "error",
"indent": "off",
"init-declarations": "off",
"jsx-quotes": "error",
"key-spacing": "off",
"keyword-spacing": "off",
"linebreak-style": ["error", "unix"],
"lines-around-comment": "off",
"max-depth": "off",
"max-len": "off",
"max-lines": "off",
"max-nested-callbacks": "error",
"max-params": "off",
"max-statements": "off",
"max-statements-per-line": "off",
"multiline-ternary": "off",
"new-cap": "off",
"new-parens": "error",
"newline-after-var": "off",
"newline-before-return": "off",
"newline-per-chained-call": "off",
"object-curly-newline": "off",
"object-curly-spacing": "off",
"object-property-newline": "off",
"object-shorthand": "off",
"one-var": "off",
"one-var-declaration-per-line": "off",
"operator-assignment": "error",
"operator-linebreak": "off",
"padded-blocks": "off",
"prefer-arrow-callback": "off",
"prefer-const": "off",
"prefer-reflect": "off",
"prefer-rest-params": "off",
"prefer-spread": "off",
"prefer-template": "off",
"quote-props": "off",
"quotes": "off",
"radix": "off",
"require-jsdoc": "off",
"require-yield": "error",
"rest-spread-spacing": "off",
"semi": "off",
"semi-spacing": "off",
"sort-imports": "off",
"sort-vars": "off",
"space-before-blocks": "off",
"space-before-function-paren": "off",
"space-in-parens": "off",
"space-infix-ops": "off",
"space-unary-ops": "off",
"spaced-comment": "off",
"strict": "off",
"template-curly-spacing": "off",
"unicode-bom": "error",
"use-isnan": "error",
"valid-jsdoc": "off",
"valid-typeof": "error",
"vars-on-top": "off",
"wrap-iife": "off",
"wrap-regex": "off",
"yield-star-spacing": "off",
"yoda": "error"
},
"parserOptions": {
"ecmaVersion": 2022,
"sourceType": "module"
}
}

14
.flake8 Normal file
View File

@@ -0,0 +1,14 @@
[flake8]
# E123,E133,E226,E241,E242 are ignored by default by pep8 and flake8
# F811 is legal in odoo 8 when we implement 2 interfaces for a method
# F999 pylint support this case with expected tests
# W503 changed by W504 and OCA prefers allow both
# F401 is legal in odoo __init__.py files
# E203 (whitespace before ':') is handled by black and it doesn't consider black syntax like:
# E741 do not use variables named l, O, or I
# chunk = records[index : index + chunk_size]
ignore = E123,E133,E203,E226,E241,E242,E501,E741,F811,F601,W503,W504
max-line-length = 119
per-file-ignores=
__init__.py:F401
jobs = 0

23
.flake8-optional Normal file
View File

@@ -0,0 +1,23 @@
[flake8]
max-line-length = 119
jobs = 0
# B = bugbear
# B9 = bugbear opinionated (incl line length +10% tolerance)
select = C,E,F,W,B,B9
# E203 whitespace before ':' (black behaviour)
# E501: flake8 line length (covered by bugbear B950)
# W503: line break before binary operator (black behaviour)
# W504: line break after binary operator (black behaviour?)
# E241: E241 multiple spaces after ',' (Better autofixing)
# B023: Function definition does not bind loop variable 'item'. (Odoo needs it [self.filtered(lambda l: l.item_id == item) for item in items])
# E226: missing whitespace around arithmetic operator (Better autofixing)
# B904: Within an `except` clause, raise exceptions with `raise (It is valid for us)
# E123: closing bracket does not match indentation of opening bracket's line (Better autofixing)
# E275: missing whitespace after keyword (Better autofixing)
# B905: zip(strict=True) only works in python >= 3.10
# B907: We sometimes quote for stylistic purposes
# F401: imported but unused (like __init__ already works)
ignore = E203,E501,W503,W504,E241,B023,E226,B904,B905,B907,E123,E275
per-file-ignores =
__init__.py:F401
__manifest__.py:B018

14
.isort.cfg Normal file
View File

@@ -0,0 +1,14 @@
[settings]
; see https://github.com/psf/black
multi_line_output=3
include_trailing_comma=True
force_grid_wrap=0
combine_as_imports=True
use_parentheses=True
line_length=88
known_odoo=odoo
known_odoo_addons=odoo.addons
sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER
default_section=THIRDPARTY
ensure_newline_before_comments = True
skip_glob=**/__init__.py

2
.oca_hooks-autofix.cfg Normal file
View File

@@ -0,0 +1,2 @@
[MESSAGES_CONTROL]
enable=po-pretty-format

2
.oca_hooks.cfg Normal file
View File

@@ -0,0 +1,2 @@
[MESSAGES_CONTROL]
disable=xml-oe-structure-missing-id,po-pretty-format

View File

@@ -0,0 +1,99 @@
exclude: |
(?x)(
# NOT INSTALLABLE ADDONS
# END NOT INSTALLABLE ADDONS
# Files and folders generated by bots, to avoid loops
^setup/|/static/description/index\.html$|
# We don't want to mess with tool-generated files
.svg$|/tests/([^/]+/)?cassettes/|^.copier-answers.yml$|^.github/|
# Maybe reactivate this when all README files include prettier ignore tags?
^README\.md$|
# Library files can have extraneous formatting (even minimized)
/static/(src/)?lib/|
# Repos using Sphinx to generate docs don't need prettying
^docs/_templates/.*\.html$|
# You don't usually want a bot to modify your legal texts
(LICENSE.*|COPYING.*)|
(\.travis\.yml|\.gitlab\-ci\.yml|\.pre\-commit\-config*\.yaml)|
# It was ignored from original MQT since that there is not init file
(/migrations/1[0-4])|
(/doc/|/docs/)|
(/tests/data/)|
# Legacy modules absa: galaxy
(galaxy/)|
# External scripts
(scripts/)
)
default_language_version:
python: python3
node: "14.13.0"
repos:
- repo: https://github.com/psf/black-pre-commit-mirror.git
rev: 23.12.1
hooks:
- id: black
- repo: https://github.com/myint/autoflake
rev: v2.2.1
hooks:
- id: autoflake
args:
- --expand-star-imports
- --ignore-init-module-imports
- --in-place
- --remove-all-unused-imports
- --remove-duplicate-keys
- --remove-unused-variables
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.3
hooks:
- id: prettier
name: prettier (with plugin-xml)
additional_dependencies:
- "prettier@2.7.1"
- "@prettier/plugin-xml@2.2.0"
args:
- --plugin=@prettier/plugin-xml
files: \.(js|xml)$
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
# exclude autogenerated files
exclude: \.pot?$
- id: end-of-file-fixer
# exclude autogenerated files
exclude: \.pot?$
- id: fix-encoding-pragma
args: ["--remove"]
- id: mixed-line-ending
args: ["--fix=lf"]
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.0
hooks:
- id: pyupgrade
args: ["--keep-percent-format"]
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
hooks:
- id: isort
- repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.5.4
hooks:
- id: remove-tabs
exclude: (Makefile|debian/rules|.gitmodules|\.po|\.pot)(\.in)?$
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.51.0
hooks:
- id: eslint
name: javascript lints
args:
- --color
- --config=.eslintrc.json
- --fix
- repo: https://github.com/OCA/odoo-pre-commit-hooks
rev: v0.0.35
hooks:
- id: oca-checks-po
args:
- --config=.oca_hooks-autofix.cfg
- --fix

View File

@@ -0,0 +1,68 @@
exclude: |
(?x)(
# NOT INSTALLABLE ADDONS
# END NOT INSTALLABLE ADDONS
# Files and folders generated by bots, to avoid loops
^setup/|/static/description/index\.html$|
# We don't want to mess with tool-generated files
.svg$|/tests/([^/]+/)?cassettes/|
# Maybe reactivate this when all README files include prettier ignore tags?
^README\.md$|
# Library files can have extraneous formatting (even minimized)
/static/(src/)?lib/|
# Repos using Sphinx to generate docs don't need prettying
^docs/_templates/.*\.html$|
# You don't usually want a bot to modify your legal texts
(LICENSE.*|COPYING.*)|
(\.travis\.yml|\.gitlab\-ci\.yml|\.pre\-commit\-config*\.yaml)|
# It was ignored from original MQT since that there is not init file
(/migrations/1[0-4])|
(/doc/|/docs/)|
# Legacy modules absa: galaxy
(galaxy/)|
# External scripts
(scripts/)
)
default_language_version:
python: python3
node: "14.13.0"
repos:
- repo: https://github.com/OCA/pylint-odoo
rev: v9.3.2
hooks:
- id: pylint_odoo
name: pylint optional checks
args:
- --rcfile=.pylintrc-optional
# External scripts
# uncomment after fix https://github.com/OCA/pylint-odoo/pull/512
# - --jobs=0 # 0 will auto-detect the number of processors available to use
- repo: https://github.com/OCA/odoo-pre-commit-hooks
rev: v0.0.35
hooks:
- id: oca-checks-odoo-module
- id: oca-checks-po
- repo: https://github.com/PyCQA/doc8
rev: v1.1.1
hooks:
- id: doc8
name: RST lint
- repo: https://github.com/PyCQA/flake8
rev: 6.1.0
hooks:
- id: flake8
name: flake8 + bugbear optional checks
additional_dependencies: ["flake8-bugbear==23.9.16"]
args:
- --config=.flake8-optional
- repo: https://github.com/PyCQA/bandit
rev: 1.7.5
hooks:
- id: bandit
name: bandit EXPERIMENTAL (Won't affect CI status)!
verbose: True
args:
- --exit-zero
- --ini=.bandit
# reduce the INFO logger
require_serial: true

8
.prettierrc.yml Normal file
View File

@@ -0,0 +1,8 @@
# Prettier will complete this with settings from .editorconfig file.
bracketSpacing: false
printWidth: 119
proseWrap: always
semi: true
trailingComma: "es5"
xmlWhitespaceSensitivity: "strict"
xmlSelfClosingSpace: true

118
.pylintrc-optional Normal file
View File

@@ -0,0 +1,118 @@
[MAIN]
ignore=CVS,.git,scenarios,.bzr
persistent=yes
load-plugins=pylint.extensions.docstyle, pylint.extensions.mccabe, pylint_odoo
# External scripts main replace
py-version=3.12
[ODOOLINT]
readme-template-url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst"
license-allowed=AGPL-3,GPL-2,GPL-2 or any later version,
GPL-3,GPL-3 or any later version,LGPL-3,
Other OSI approved licence,Other proprietary,
OEEL-1, OPL-1,
manifest-required-authors=Vauxoo,
Odoo Community Association (OCA),
Jarsa,
manifest-required-keys=license
manifest-deprecated-keys=description,active
# External scripts odoo_lint replace
valid-odoo-version=18.0
[MESSAGES CONTROL]
disable=all,unknown-option-value
enable=attribute-deprecated,
attribute-string-redundant,
bad-builtin-groupby,
bad-docstring-quotes,
consider-merging-classes-inherited,
consider-using-generator,
context-overridden,
deprecated-method,
deprecated-odoo-model-method,
deprecated-pragma,
docstring-first-line-empty,
eval-used,
except-pass,
external-request-timeout,
implicit-str-concat,
invalid-commit,
license-allowed,
manifest-author-string,
manifest-data-duplicated,
manifest-deprecated-key,
manifest-maintainers-list,
manifest-required-author,
manifest-required-key,
manifest-version-format,
method-compute,
method-inverse,
method-required-super,
method-search,
missing-readme,
missing-return,
odoo-addons-relative-import,
odoo-exception-warning,
print-used,
redundant-u-string-prefix,
renamed-field-parameter,
resource-not-exist,
sql-injection,
too-complex,
translation-contains-variable,
translation-field,
translation-required,
translation-not-lazy,
translation-format-interpolation,
translation-format-truncated,
translation-fstring-interpolation,
translation-too-few-args,
translation-too-many-args,
translation-unsupported-format,
use-dict-literal,
use-implicit-booleaness-not-comparison-to-string,
use-implicit-booleaness-not-comparison-to-zero,
use-symbolic-message-instead,
use-vim-comment,
useless-parent-delegation,
website-manifest-key-not-valid-uri
[REPORTS]
msg-template={path}:{line}:{column}: ({symbol}) {msg}
output-format=colorized
reports=no
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
score=no
[FORMAT]
indent-string=' '
expected-line-ending-format=LF
[BASIC]
class-rgx=[A-Z_][a-zA-Z0-9]{2,59}
module-rgx=.*
const-rgx=.*
function-rgx=.*
method-rgx=.*
attr-rgx=.*
argument-rgx=.*
variable-rgx=.*
inlinevar-rgx=.*
[SIMILARITIES]
ignore-comments=yes
ignore-docstrings=yes
[MISCELLANEOUS]
notes=FIXME,TODO
[IMPORTS]
deprecated-modules=openerp.osv
[DESIGN]
# McCabe complexity cyclomatic threshold for too-complex check
# Value definied from https://en.wikipedia.org/wiki/Cyclomatic_complexity
# - The figure of 10 had received substantial corroborating evidence,
#  but that in some circumstances it may be appropriate to relax the restriction
# and permit modules with a complexity as high as 15
max-complexity=15

View File

@@ -0,0 +1,159 @@
============================
Account Move Number Sequence
============================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:03b876b8607b5c34b0f49870637439187af2b3913e3af50d384838d28ef870ee
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--financial--tools-lightgray.png?logo=github
:target: https://github.com/OCA/account-financial-tools/tree/18.0/account_move_name_sequence
:alt: OCA/account-financial-tools
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/account-financial-tools-18-0/account-financial-tools-18-0-account_move_name_sequence
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/account-financial-tools&target_branch=18.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
In Odoo version 13.0 and previous versions, the number of journal
entries was generated from a sequence configured on the journal.
In Odoo version 14.0, the number of journal entries can be manually set
by the user. Then, the number attributed for the next journal entries in
the same journal is computed by a complex piece of code that guesses the
format of the journal entry number from the number of the journal entry
which was manually entered by the user. It has several drawbacks:
- the available options for the sequence are limited,
- it is not possible to configure the sequence in advance before the
deployment in production,
- as it is error-prone, they added a *Resequence* wizard to re-generate
the journal entry numbers, which can be considered as illegal in many
countries,
- the `piece of
code <https://github.com/odoo/odoo/blob/14.0/addons/account/models/sequence_mixin.py>`__
that handles this is not easy to understand and quite difficult to
debug.
Using this module, you can configure what kind of documents the gap
sequence may be relaxed And even if you must use no-gap in your company
or country it will reduce the concurrency issues since the module is
using an extra table (ir_sequence) instead of locking the last record
For those like me who think that the implementation before Odoo v14.0
was much better, for the accountants who think it should not be possible
to manually enter the sequence of a customer invoice, for the auditor
who considers that resequencing journal entries is prohibited by law,
this module may be a solution to get out of the nightmare.
The field names used in this module to configure the sequence on the
journal are exactly the same as in Odoo version 13.0 and previous
versions. That way, if you migrate to Odoo version 14.0 and you install
this module immediately after the migration, you should keep the
previous behavior and the same sequences will continue to be used.
The module removes access to the *Resequence* wizard on journal entries.
**Table of contents**
.. contents::
:local:
Configuration
=============
On the form view of an account journal, in the first tab, there is a
many2one link to the sequence. When you create a new journal, you can
keep this field empty and a new sequence will be automatically created
when you save the journal.
On sale and purchase journals, you have an additional option to have
another sequence dedicated to refunds.
Upon module installation, all existing journals will be updated with a
journal entry sequence (and also a credit note sequence for sale and
purchase journals). You should update the configuration of the sequences
to fit your needs. You can uncheck the option *Dedicated Credit Note
Sequence* on existing sale and purchase journals if you don't want it.
For the journals which already have journal entries, you should update
the sequence configuration to avoid a discontinuity in the numbering for
the next journal entry.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/account-financial-tools/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/account-financial-tools/issues/new?body=module:%20account_move_name_sequence%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
-------
* Akretion
* Vauxoo
Contributors
------------
- `Akretion <https://www.akretion.com>`__:
- Alexis de Lattre <alexis.delattre@akretion.com>
- `Vauxoo <https://www.vauxoo.com>`__:
- Moisés López <moylop260@vauxoo.com>
- Francisco Luna <fluna@vauxoo.com>
- `Factor Libre <https://www.factorlibre.com>`__:
- Rodrigo Bonilla Martinez <rodrigo.bonilla@factorlibre.com>
Maintainers
-----------
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
.. |maintainer-alexis-via| image:: https://github.com/alexis-via.png?size=40px
:target: https://github.com/alexis-via
:alt: alexis-via
.. |maintainer-moylop260| image:: https://github.com/moylop260.png?size=40px
:target: https://github.com/moylop260
:alt: moylop260
.. |maintainer-luisg123v| image:: https://github.com/luisg123v.png?size=40px
:target: https://github.com/luisg123v
:alt: luisg123v
Current `maintainers <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-alexis-via| |maintainer-moylop260| |maintainer-luisg123v|
This module is part of the `OCA/account-financial-tools <https://github.com/OCA/account-financial-tools/tree/18.0/account_move_name_sequence>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -0,0 +1,2 @@
from .hooks import post_init_hook
from . import models

View File

@@ -0,0 +1,31 @@
# Copyright 2021 Akretion France (http://www.akretion.com/)
# Copyright 2022 Vauxoo (https://www.vauxoo.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# @author: Moisés López <moylop260@vauxoo.com>
# @author: Francisco Luna <fluna@vauxoo.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Account Move Number Sequence",
"version": "18.0.1.0.0",
"category": "Accounting",
"license": "AGPL-3",
"summary": "Generate journal entry number from sequence",
"author": "Akretion,Vauxoo,Odoo Community Association (OCA)",
"maintainers": ["alexis-via", "moylop260", "luisg123v"],
"website": "https://github.com/OCA/account-financial-tools",
"depends": [
"account",
],
"demo": [
"demo/ir_sequence_demo.xml",
"demo/account_journal_demo.xml",
],
"data": [
"views/account_journal_views.xml",
"views/account_move_views.xml",
"security/ir.model.access.csv",
],
"post_init_hook": "post_init_hook",
"installable": True,
}

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="journal_sale_std_demo" model="account.journal">
<field name="name">Standard Sale Journal Demo</field>
<field name="code">SSJD</field>
<field name="type">sale</field>
<field name="refund_sequence" eval="True" />
<field name="company_id" ref="base.main_company" />
<field name="sequence_id" ref="seq_sale_std_demo" />
</record>
<record id="journal_cash_std_demo" model="account.journal">
<field name="name">Standard Cash Journal Demo</field>
<field name="code">SCJD</field>
<field name="type">cash</field>
<field name="refund_sequence" eval="True" />
<field name="company_id" ref="base.main_company" />
<field name="sequence_id" ref="seq_cash_std_demo" />
</record>
</odoo>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="seq_sale_std_demo" model="ir.sequence">
<field name="name">Standard Sale Sequence Demo</field>
<field name="prefix">SSS_demo/%(range_year)s/</field>
<field name="use_date_range" eval="True" />
<field name="number_next" eval="1" />
<field name="number_increment" eval="1" />
<field name="company_id" ref="base.main_company" />
<field name="implementation">standard</field>
</record>
<record id="seq_cash_std_demo" model="ir.sequence">
<field name="name">Standard Cash Sequence Demo</field>
<field name="prefix">SCS_demo/%(range_year)s/</field>
<field name="use_date_range" eval="True" />
<field name="number_next" eval="1" />
<field name="number_increment" eval="1" />
<field name="company_id" ref="base.main_company" />
<field name="implementation">standard</field>
</record>
</odoo>

View File

@@ -0,0 +1,35 @@
# Copyright 2021 Akretion France (http://www.akretion.com/)
# Copyright 2022 Vauxoo (https://www.vauxoo.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# @author: Moisés López <moylop260@vauxoo.com>
# @author: Francisco Luna <fluna@vauxoo.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import tools
def post_init_hook(env):
create_journal_sequences(env)
@tools.mute_logger("odoo.addons.account_move_name_sequence.models.account_journal")
def create_journal_sequences(env):
journals = (
env["account.journal"]
.with_context(active_test=False)
.search([("sequence_id", "=", False)])
)
for journal in journals:
journal_vals = {
"code": journal.code,
"name": journal.name,
"company_id": journal.company_id.id,
}
seq_vals = journal._prepare_sequence(journal_vals)
seq_vals.update(journal._prepare_sequence_current_moves())
vals = {"sequence_id": env["ir.sequence"].create(seq_vals).id}
if journal.type in ("sale", "purchase") and journal.refund_sequence:
rseq_vals = journal._prepare_sequence(journal_vals, refund=True)
rseq_vals.update(journal._prepare_sequence_current_moves(refund=True))
vals["refund_sequence_id"] = env["ir.sequence"].create(rseq_vals).id
journal.write(vals)
return

View File

@@ -0,0 +1,156 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_move_name_sequence
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 17.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: account_move_name_sequence
#: model:ir.model.constraint,message:account_move_name_sequence.constraint_account_move_name_state_diagonal
msgid ""
"A move can not be posted with name \"/\" or empty value\n"
"Check the journal sequence, please"
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__refund_sequence
msgid ""
"Check this box if you don't want to share the same sequence for invoices and"
" credit notes made from this journal"
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__refund_sequence_id
msgid "Credit Note Entry Sequence"
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__refund_sequence
msgid "Dedicated Credit Note Sequence"
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__sequence_id
msgid "Entry Sequence"
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__has_sequence_holes
msgid "Has Sequence Holes"
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__highest_name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__highest_name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__highest_name
msgid "Highest Name"
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model,name:account_move_name_sequence.model_account_journal
msgid "Journal"
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model,name:account_move_name_sequence.model_account_move
msgid "Journal Entry"
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__made_sequence_hole
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__made_sequence_hole
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__made_sequence_hole
msgid "Made Sequence Hole"
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__name
msgid "Number"
msgstr ""
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"On journal '%s', the same sequence is used as Entry Sequence and Credit Note"
" Entry Sequence."
msgstr ""
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid "Refund"
msgstr ""
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#: model:ir.model,name:account_move_name_sequence.model_ir_sequence
#, python-format
msgid "Sequence"
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__sequence_number
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__sequence_number
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__sequence_number
msgid "Sequence Number"
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__sequence_prefix
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__sequence_prefix
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__sequence_prefix
msgid "Sequence Prefix"
msgstr ""
#. module: account_move_name_sequence
#: model:account.journal,name:account_move_name_sequence.journal_cash_std_demo
msgid "Standard Cash Journal Demo"
msgstr ""
#. module: account_move_name_sequence
#: model:account.journal,name:account_move_name_sequence.journal_sale_std_demo
msgid "Standard Sale Journal Demo"
msgstr ""
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"The company is not set on sequence '%(sequence)s' configured as credit note "
"sequence of journal '%(journal)s'."
msgstr ""
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"The company is not set on sequence '%(sequence)s' configured on journal "
"'%(journal)s'."
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__refund_sequence_id
msgid ""
"This sequence will be used to generate the journal entry number for refunds."
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__sequence_id
msgid "This sequence will be used to generate the journal entry number."
msgstr ""

View File

@@ -0,0 +1,164 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_move_name_sequence
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-09-02 21:55+0000\n"
"Last-Translator: Hussain Hammad <hussain.hammad@greeenboard.com>\n"
"Language-Team: none\n"
"Language: ar\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
"&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n"
"X-Generator: Weblate 4.17\n"
#. module: account_move_name_sequence
#: model:ir.model.constraint,message:account_move_name_sequence.constraint_account_move_name_state_diagonal
msgid ""
"A move can not be posted with name \"/\" or empty value\n"
"Check the journal sequence, please"
msgstr ""
"لا يمكن ترحيل قيد و اسمه \"/\" او فارغ\n"
"الرجاء التأكد من تسلسل اليومية"
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__refund_sequence
msgid ""
"Check this box if you don't want to share the same sequence for invoices and "
"credit notes made from this journal"
msgstr ""
"استخدم هذه الخاصية ان كنت لا ترغب باستخدام نفس التسلسل للفواتير و اشعارات "
"الدائن"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__refund_sequence_id
msgid "Credit Note Entry Sequence"
msgstr "تسلسل ادخال اشعار دائن"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__refund_sequence
msgid "Dedicated Credit Note Sequence"
msgstr "تسلسل خاص باشعار دائن"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__sequence_id
msgid "Entry Sequence"
msgstr "تسلسل ادخال"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__has_sequence_holes
msgid "Has Sequence Holes"
msgstr "التسلسل يحتوي على فراغات"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__highest_name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__highest_name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__highest_name
msgid "Highest Name"
msgstr "ارفع اسم"
#. module: account_move_name_sequence
#: model:ir.model,name:account_move_name_sequence.model_account_journal
msgid "Journal"
msgstr "يومية"
#. module: account_move_name_sequence
#: model:ir.model,name:account_move_name_sequence.model_account_move
msgid "Journal Entry"
msgstr "ادخال يومية"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__made_sequence_hole
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__made_sequence_hole
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__made_sequence_hole
msgid "Made Sequence Hole"
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__name
msgid "Number"
msgstr "رقم"
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"On journal '%s', the same sequence is used as Entry Sequence and Credit Note "
"Entry Sequence."
msgstr ""
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid "Refund"
msgstr "مرتجع"
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#: model:ir.model,name:account_move_name_sequence.model_ir_sequence
#, python-format
msgid "Sequence"
msgstr "تسلسل"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__sequence_number
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__sequence_number
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__sequence_number
msgid "Sequence Number"
msgstr "رقم التسلسل"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__sequence_prefix
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__sequence_prefix
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__sequence_prefix
msgid "Sequence Prefix"
msgstr "قبل التسلسل"
#. module: account_move_name_sequence
#: model:account.journal,name:account_move_name_sequence.journal_cash_std_demo
msgid "Standard Cash Journal Demo"
msgstr ""
#. module: account_move_name_sequence
#: model:account.journal,name:account_move_name_sequence.journal_sale_std_demo
msgid "Standard Sale Journal Demo"
msgstr ""
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"The company is not set on sequence '%(sequence)s' configured as credit note "
"sequence of journal '%(journal)s'."
msgstr ""
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"The company is not set on sequence '%(sequence)s' configured on journal "
"'%(journal)s'."
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__refund_sequence_id
msgid ""
"This sequence will be used to generate the journal entry number for refunds."
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__sequence_id
msgid "This sequence will be used to generate the journal entry number."
msgstr ""

View File

@@ -0,0 +1,173 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_move_name_sequence
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-04-24 11:34+0000\n"
"Last-Translator: Ivorra78 <informatica@totmaterial.es>\n"
"Language-Team: none\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: account_move_name_sequence
#: model:ir.model.constraint,message:account_move_name_sequence.constraint_account_move_name_state_diagonal
msgid ""
"A move can not be posted with name \"/\" or empty value\n"
"Check the journal sequence, please"
msgstr ""
"Un asiento no puede ser publicado con el nombre \"/\" o vacío \n"
"Comprueba la secuencia de diario, por favor"
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__refund_sequence
msgid ""
"Check this box if you don't want to share the same sequence for invoices and "
"credit notes made from this journal"
msgstr ""
"Marca esta casilla si no quieres compartir la misma secuencia para las "
"facturas y facturas rectificativas hechas en este diario"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__refund_sequence_id
msgid "Credit Note Entry Sequence"
msgstr "Secuencia de facturas rectificativas"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__refund_sequence
msgid "Dedicated Credit Note Sequence"
msgstr "Secuencia de facturas rectificativas dedicada"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__sequence_id
msgid "Entry Sequence"
msgstr "Secuencia de asiento"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__has_sequence_holes
msgid "Has Sequence Holes"
msgstr "Hay agujeros en la secuencia"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__highest_name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__highest_name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__highest_name
msgid "Highest Name"
msgstr "Número más alto"
#. module: account_move_name_sequence
#: model:ir.model,name:account_move_name_sequence.model_account_journal
msgid "Journal"
msgstr "Dario"
#. module: account_move_name_sequence
#: model:ir.model,name:account_move_name_sequence.model_account_move
msgid "Journal Entry"
msgstr "Asiento"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__made_sequence_hole
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__made_sequence_hole
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__made_sequence_hole
msgid "Made Sequence Hole"
msgstr "Creado agujero en secuencia"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__name
msgid "Number"
msgstr "Número"
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"On journal '%s', the same sequence is used as Entry Sequence and Credit Note "
"Entry Sequence."
msgstr ""
"En el diario '%s', se ha usado la misma secuencia para la Secuencia de "
"asiento y la Secuencia de facturas rectificativas."
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid "Refund"
msgstr "Rectificativa"
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#: model:ir.model,name:account_move_name_sequence.model_ir_sequence
#, python-format
msgid "Sequence"
msgstr "Secuencia"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__sequence_number
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__sequence_number
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__sequence_number
msgid "Sequence Number"
msgstr "Número de secuencia"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__sequence_prefix
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__sequence_prefix
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__sequence_prefix
msgid "Sequence Prefix"
msgstr "Prefijo de la secuencia"
#. module: account_move_name_sequence
#: model:account.journal,name:account_move_name_sequence.journal_cash_std_demo
msgid "Standard Cash Journal Demo"
msgstr "Demostración del Libro de Caja Estándar"
#. module: account_move_name_sequence
#: model:account.journal,name:account_move_name_sequence.journal_sale_std_demo
msgid "Standard Sale Journal Demo"
msgstr "Demostración del Diario de Venta Estándar"
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"The company is not set on sequence '%(sequence)s' configured as credit note "
"sequence of journal '%(journal)s'."
msgstr ""
"La compañía no tiene establecida la secuencia '%(sequence)s' configurada "
"como secuencia de facturas rectificativas del diario '%(journal)s'."
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"The company is not set on sequence '%(sequence)s' configured on journal "
"'%(journal)s'."
msgstr ""
"La compañía no tiene establecida la secuencia '%(sequence)s' configurada en "
"el diario '%(journal)s'."
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__refund_sequence_id
msgid ""
"This sequence will be used to generate the journal entry number for refunds."
msgstr ""
"Esta secuencia se utilizará para generar el número de asientos para "
"rectificaciones."
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__sequence_id
msgid "This sequence will be used to generate the journal entry number."
msgstr ""
"Esta secuencia se usará para establecer para generar el número de asiento "
"contable."

View File

@@ -0,0 +1,171 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_move_name_sequence
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-06-09 00:10+0000\n"
"Last-Translator: Alexis de Lattre <alexis@via.ecp.fr>\n"
"Language-Team: none\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: account_move_name_sequence
#: model:ir.model.constraint,message:account_move_name_sequence.constraint_account_move_name_state_diagonal
msgid ""
"A move can not be posted with name \"/\" or empty value\n"
"Check the journal sequence, please"
msgstr ""
"Une pièce comptable ne peut pas être comptabilisé car elle n'a pas de numéro "
"attribué.\n"
"Vérifiez la configuration de la séquence sur le journal concerné."
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__refund_sequence
msgid ""
"Check this box if you don't want to share the same sequence for invoices and "
"credit notes made from this journal"
msgstr ""
"Cocher cette case si vous souhaitez créer 2 séquences distinctes pour les "
"factures et avoirs pour ce journal"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__refund_sequence_id
msgid "Credit Note Entry Sequence"
msgstr "Séquence pour les avoirs"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__refund_sequence
msgid "Dedicated Credit Note Sequence"
msgstr "Séquence dédiée pour les avoirs"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__sequence_id
msgid "Entry Sequence"
msgstr "Séquence des pièces comptables"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__has_sequence_holes
msgid "Has Sequence Holes"
msgstr "A des trous dans la séquence"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__highest_name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__highest_name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__highest_name
msgid "Highest Name"
msgstr "Plus grand numéro de séquence"
#. module: account_move_name_sequence
#: model:ir.model,name:account_move_name_sequence.model_account_journal
msgid "Journal"
msgstr "Journal"
#. module: account_move_name_sequence
#: model:ir.model,name:account_move_name_sequence.model_account_move
msgid "Journal Entry"
msgstr "Pièce comptable"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__made_sequence_hole
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__made_sequence_hole
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__made_sequence_hole
msgid "Made Sequence Hole"
msgstr "Cause un trou dans la séquence"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__name
msgid "Number"
msgstr "Nombre"
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"On journal '%s', the same sequence is used as Entry Sequence and Credit Note "
"Entry Sequence."
msgstr ""
"Pour le journal '%s', la même séquence est utilisée pour les factures et les "
"avoirs."
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid "Refund"
msgstr "Avoir"
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#: model:ir.model,name:account_move_name_sequence.model_ir_sequence
#, python-format
msgid "Sequence"
msgstr "Séquence"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__sequence_number
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__sequence_number
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__sequence_number
msgid "Sequence Number"
msgstr "Nombre de la séquence"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__sequence_prefix
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__sequence_prefix
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__sequence_prefix
msgid "Sequence Prefix"
msgstr "Préfixe de la séquence"
#. module: account_move_name_sequence
#: model:account.journal,name:account_move_name_sequence.journal_cash_std_demo
msgid "Standard Cash Journal Demo"
msgstr ""
#. module: account_move_name_sequence
#: model:account.journal,name:account_move_name_sequence.journal_sale_std_demo
msgid "Standard Sale Journal Demo"
msgstr ""
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"The company is not set on sequence '%(sequence)s' configured as credit note "
"sequence of journal '%(journal)s'."
msgstr ""
"Aucune société n'est configurée sur la séquence '%(sequence)s' utilisée "
"comme séquence pour les avoirs sur le journal '%(journal)s'."
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"The company is not set on sequence '%(sequence)s' configured on journal "
"'%(journal)s'."
msgstr ""
"Aucune société n'est configurée sur la séquence '%(sequence)s' configurée "
"sur le journal '%(journal)s'."
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__refund_sequence_id
msgid ""
"This sequence will be used to generate the journal entry number for refunds."
msgstr "Cette séquence sera utilisée pour générer les numéros des avoirs."
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__sequence_id
msgid "This sequence will be used to generate the journal entry number."
msgstr ""
"Cette séquence sera utilisée pour générer les numéros des pièces comptables."

View File

@@ -0,0 +1,172 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_move_name_sequence
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-02-16 10:09+0000\n"
"Last-Translator: Bole <bole@dajmi5.com>\n"
"Language-Team: none\n"
"Language: hr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 4.14.1\n"
#. module: account_move_name_sequence
#: model:ir.model.constraint,message:account_move_name_sequence.constraint_account_move_name_state_diagonal
msgid ""
"A move can not be posted with name \"/\" or empty value\n"
"Check the journal sequence, please"
msgstr ""
"Temeljnicu nije moguće potvrditi sa nazivom \"/\" ili praznim poljem \n"
"Provjerite sekvencu dnevnika, molimo"
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__refund_sequence
msgid ""
"Check this box if you don't want to share the same sequence for invoices and "
"credit notes made from this journal"
msgstr ""
"Označite ovu kućicu ako ne želite dijeliti istu sekvencu za račune i "
"odobrenja/storna napravljena iz ovog dnevnika"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__refund_sequence_id
msgid "Credit Note Entry Sequence"
msgstr "Ulazna sekvenca za odobrenja"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__refund_sequence
msgid "Dedicated Credit Note Sequence"
msgstr "Dedicirana sekvenca za odobrenja"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__sequence_id
msgid "Entry Sequence"
msgstr "Ulazna sekvenca"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__has_sequence_holes
msgid "Has Sequence Holes"
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__highest_name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__highest_name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__highest_name
msgid "Highest Name"
msgstr "Najviši broj"
#. module: account_move_name_sequence
#: model:ir.model,name:account_move_name_sequence.model_account_journal
msgid "Journal"
msgstr "Dnevnik"
#. module: account_move_name_sequence
#: model:ir.model,name:account_move_name_sequence.model_account_move
msgid "Journal Entry"
msgstr "Stavka dnevnika"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__made_sequence_hole
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__made_sequence_hole
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__made_sequence_hole
msgid "Made Sequence Hole"
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__name
msgid "Number"
msgstr "Broj"
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"On journal '%s', the same sequence is used as Entry Sequence and Credit Note "
"Entry Sequence."
msgstr ""
"Na dnevniku '%s', ista sekvenca se koristi za regularna i storno knjiženja."
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid "Refund"
msgstr "Storno/Odobrenje"
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#: model:ir.model,name:account_move_name_sequence.model_ir_sequence
#, python-format
msgid "Sequence"
msgstr "Sekvenca"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__sequence_number
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__sequence_number
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__sequence_number
msgid "Sequence Number"
msgstr "Broj sekvence"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__sequence_prefix
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__sequence_prefix
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__sequence_prefix
msgid "Sequence Prefix"
msgstr "Prefiks sekvence"
#. module: account_move_name_sequence
#: model:account.journal,name:account_move_name_sequence.journal_cash_std_demo
msgid "Standard Cash Journal Demo"
msgstr ""
#. module: account_move_name_sequence
#: model:account.journal,name:account_move_name_sequence.journal_sale_std_demo
msgid "Standard Sale Journal Demo"
msgstr ""
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"The company is not set on sequence '%(sequence)s' configured as credit note "
"sequence of journal '%(journal)s'."
msgstr ""
"Tvrtka nije postavljena na sekvenci '%(sequence)s' postavljenoj kao sekvenca "
"za storno/odobrenja na dnevniku '%(journal)s'."
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"The company is not set on sequence '%(sequence)s' configured on journal "
"'%(journal)s'."
msgstr ""
"Tvrtka nije postavljena na sekvenci '%(sequence)s' postavljenoj na dnevniku "
"'%(journal)s'."
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__refund_sequence_id
msgid ""
"This sequence will be used to generate the journal entry number for refunds."
msgstr ""
"Ova sekvenca će biti korištena za generiranje broja knjiženja za odobrenja/"
"storno."
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__sequence_id
msgid "This sequence will be used to generate the journal entry number."
msgstr ""
"Ova sekvenca će biti korištena za generiranje broja knjiženja dnevnika."

View File

@@ -0,0 +1,173 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_move_name_sequence
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 17.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-04-23 08:34+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: account_move_name_sequence
#: model:ir.model.constraint,message:account_move_name_sequence.constraint_account_move_name_state_diagonal
msgid ""
"A move can not be posted with name \"/\" or empty value\n"
"Check the journal sequence, please"
msgstr ""
"Un movimento non può essere inserito conconem \"/\" o un valore vuoto\n"
"Controllare la sequenza del registro"
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__refund_sequence
msgid ""
"Check this box if you don't want to share the same sequence for invoices and "
"credit notes made from this journal"
msgstr ""
"Spuntare questa opzione se non si vuole condividere la stessa sequenza per "
"le fatture e le note di credito fatte per questo registro"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__refund_sequence_id
msgid "Credit Note Entry Sequence"
msgstr "Sequenza registrazione nota di credito"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__refund_sequence
msgid "Dedicated Credit Note Sequence"
msgstr "Sequenza nota di credito dedicata"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__sequence_id
msgid "Entry Sequence"
msgstr "Sequenza registrazione"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__has_sequence_holes
msgid "Has Sequence Holes"
msgstr "Ha mancanze nella sequenza"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__highest_name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__highest_name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__highest_name
msgid "Highest Name"
msgstr "Primo nome"
#. module: account_move_name_sequence
#: model:ir.model,name:account_move_name_sequence.model_account_journal
msgid "Journal"
msgstr "Registro"
#. module: account_move_name_sequence
#: model:ir.model,name:account_move_name_sequence.model_account_move
msgid "Journal Entry"
msgstr "Registrazione contabile"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__made_sequence_hole
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__made_sequence_hole
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__made_sequence_hole
msgid "Made Sequence Hole"
msgstr "Generata discontinuità nella sequenza"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__name
msgid "Number"
msgstr "Numero"
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"On journal '%s', the same sequence is used as Entry Sequence and Credit Note "
"Entry Sequence."
msgstr ""
"Nel registro '%s', la stessa sequenza è utilizzata come sequenza di "
"registrazione e sequenza di registrazione nota di credito."
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid "Refund"
msgstr "Rimborso"
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#: model:ir.model,name:account_move_name_sequence.model_ir_sequence
#, python-format
msgid "Sequence"
msgstr "Sequenza"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__sequence_number
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__sequence_number
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__sequence_number
msgid "Sequence Number"
msgstr "Numero sequenza"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__sequence_prefix
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__sequence_prefix
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__sequence_prefix
msgid "Sequence Prefix"
msgstr "Prefisso sequenza"
#. module: account_move_name_sequence
#: model:account.journal,name:account_move_name_sequence.journal_cash_std_demo
msgid "Standard Cash Journal Demo"
msgstr "Demo registro contanti standard"
#. module: account_move_name_sequence
#: model:account.journal,name:account_move_name_sequence.journal_sale_std_demo
msgid "Standard Sale Journal Demo"
msgstr "Demo registro vendite standard"
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"The company is not set on sequence '%(sequence)s' configured as credit note "
"sequence of journal '%(journal)s'."
msgstr ""
"L'azienda non è impostata per la sequenza '%(sequence)s' configurata come "
"sequenza nota di credito del registro '%(journal)s'."
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"The company is not set on sequence '%(sequence)s' configured on journal "
"'%(journal)s'."
msgstr ""
"L'azienda non è impostata per la sequenza '%(sequence)s' configurata nel "
"registro '%(journal)s'."
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__refund_sequence_id
msgid ""
"This sequence will be used to generate the journal entry number for refunds."
msgstr ""
"Questa sequenza verrà utilizzata per generare il numero della registrazione "
"contabile per i rimborsi."
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__sequence_id
msgid "This sequence will be used to generate the journal entry number."
msgstr ""
"Questa sequenza verrà utilizzata per generare il numero della registrazione "
"contabile."

View File

@@ -0,0 +1,185 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_move_name_sequence
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-03-30 12:22+0000\n"
"Last-Translator: Matjaz Mozetic <matjaz@luxim.si>\n"
"Language-Team: none\n"
"Language: sl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n"
"%100==4 ? 2 : 3;\n"
"X-Generator: Weblate 4.14.1\n"
#. module: account_move_name_sequence
#: model:ir.model.constraint,message:account_move_name_sequence.constraint_account_move_name_state_diagonal
msgid ""
"A move can not be posted with name \"/\" or empty value\n"
"Check the journal sequence, please"
msgstr ""
"Temeljnice ni mogoče knjižiti z nazivom \"/\" ali prazno vrednostjo\n"
"Prosimo, da preverite zaporedje v dnevniku"
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__refund_sequence
msgid ""
"Check this box if you don't want to share the same sequence for invoices and "
"credit notes made from this journal"
msgstr ""
"Označite, če želite v tem dnevniku uporabljati isto zaporedje za račune in "
"dobropise"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__refund_sequence_id
msgid "Credit Note Entry Sequence"
msgstr "Zaporedje za dobropise"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__refund_sequence
msgid "Dedicated Credit Note Sequence"
msgstr "Ločeno zaporedje za dobropise"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__sequence_id
msgid "Entry Sequence"
msgstr "Zaporedje vnosa"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_journal__has_sequence_holes
msgid "Has Sequence Holes"
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__highest_name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__highest_name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__highest_name
msgid "Highest Name"
msgstr "Najvišji naziv"
#. module: account_move_name_sequence
#: model:ir.model,name:account_move_name_sequence.model_account_journal
msgid "Journal"
msgstr "Dnevnik"
#. module: account_move_name_sequence
#: model:ir.model,name:account_move_name_sequence.model_account_move
msgid "Journal Entry"
msgstr "Dnevniški vnos"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__made_sequence_hole
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__made_sequence_hole
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__made_sequence_hole
msgid "Made Sequence Hole"
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__name
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__name
msgid "Number"
msgstr "Številka"
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"On journal '%s', the same sequence is used as Entry Sequence and Credit Note "
"Entry Sequence."
msgstr "V dnevniku '%s' se uporablja isto zaporedje za vnose in dobropise."
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid "Refund"
msgstr "Povračilo"
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#: model:ir.model,name:account_move_name_sequence.model_ir_sequence
#, python-format
msgid "Sequence"
msgstr "Zaporedje"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__sequence_number
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__sequence_number
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__sequence_number
msgid "Sequence Number"
msgstr "Številka zaporedja"
#. module: account_move_name_sequence
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_bank_statement_line__sequence_prefix
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_move__sequence_prefix
#: model:ir.model.fields,field_description:account_move_name_sequence.field_account_payment__sequence_prefix
msgid "Sequence Prefix"
msgstr "Predpona zaporedja"
#. module: account_move_name_sequence
#: model:account.journal,name:account_move_name_sequence.journal_cash_std_demo
msgid "Standard Cash Journal Demo"
msgstr ""
#. module: account_move_name_sequence
#: model:account.journal,name:account_move_name_sequence.journal_sale_std_demo
msgid "Standard Sale Journal Demo"
msgstr ""
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"The company is not set on sequence '%(sequence)s' configured as credit note "
"sequence of journal '%(journal)s'."
msgstr ""
#. module: account_move_name_sequence
#. odoo-python
#: code:addons/account_move_name_sequence/models/account_journal.py:0
#, python-format
msgid ""
"The company is not set on sequence '%(sequence)s' configured on journal "
"'%(journal)s'."
msgstr ""
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__refund_sequence_id
msgid ""
"This sequence will be used to generate the journal entry number for refunds."
msgstr "To zaporedje bo v rabi za knjigovodske vnose dobropisov."
#. module: account_move_name_sequence
#: model:ir.model.fields,help:account_move_name_sequence.field_account_journal__sequence_id
msgid "This sequence will be used to generate the journal entry number."
msgstr "To zaporedje bo v rabi za knjigovodske vnose dobropisov."
#~ msgid "Display Name"
#~ msgstr "Prikazani naziv"
#~ msgid "ID"
#~ msgstr "ID"
#~ msgid "Last Modified on"
#~ msgstr "Zadnjič spremenjeno"
#, python-format
#~ msgid ""
#~ "The company is not set on sequence '%s' configured as credit note "
#~ "sequence of journal '%s'."
#~ msgstr ""
#~ "Pri zaporedju '%s' za dobropise v dnevniku '%s' ni nastavljena družba."
#, python-format
#~ msgid "The company is not set on sequence '%s' configured on journal '%s'."
#~ msgstr ""
#~ "Pri zaporedju '%s' nastavljenem v dnevniku '%s' ni nastavljena družba."

View File

@@ -0,0 +1,3 @@
from . import account_journal
from . import account_move
from . import ir_sequence

View File

@@ -0,0 +1,250 @@
# Copyright 2021 Akretion France (http://www.akretion.com/)
# Copyright 2022 Vauxoo (https://www.vauxoo.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# @author: Moisés López <moylop260@vauxoo.com>
# @author: Francisco Luna <fluna@vauxoo.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from odoo import Command, api, fields, models
from odoo.exceptions import ValidationError
_logger = logging.getLogger(__name__)
class AccountJournal(models.Model):
_inherit = "account.journal"
sequence_id = fields.Many2one(
"ir.sequence",
string="Entry Sequence",
copy=False,
check_company=True,
domain="[('company_id', '=', company_id)]",
help="This sequence will be used to generate the journal entry number.",
)
refund_sequence_id = fields.Many2one(
"ir.sequence",
string="Credit Note Entry Sequence",
copy=False,
check_company=True,
domain="[('company_id', '=', company_id)]",
help="This sequence will be used to generate the journal entry number for "
"refunds.",
)
# Redefine the default to True as <=v13.0
refund_sequence = fields.Boolean(default=True)
# has_sequence_holes is not relevant anymore (since based on sequence_prefix/number)
# -> compute=False to improve perf and to avoid displaying warning
has_sequence_holes = fields.Boolean(compute=False)
@api.constrains("refund_sequence_id", "sequence_id")
def _check_journal_sequence(self):
for journal in self:
if (
journal.refund_sequence_id
and journal.sequence_id
and journal.refund_sequence_id == journal.sequence_id
):
raise ValidationError(
self.env._(
"On journal '%s', the same sequence is used as "
"Entry Sequence and Credit Note Entry Sequence.",
journal.display_name,
)
)
if journal.sequence_id and not journal.sequence_id.company_id:
msg = self.env._(
"The company is not set on sequence '%(sequence)s' configured on "
"journal '%(journal)s'.",
sequence=journal.sequence_id.display_name,
journal=journal.display_name,
)
raise ValidationError(msg)
if journal.refund_sequence_id and not journal.refund_sequence_id.company_id:
msg = self.env._(
"The company is not set on sequence '%(sequence)s' configured as "
"credit note sequence of journal '%(journal)s'.",
sequence=journal.refund_sequence_id.display_name,
journal=journal.display_name,
)
raise ValidationError(msg)
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
if not vals.get("sequence_id"):
vals["sequence_id"] = self._create_sequence(vals).id
if (
vals.get("type") in ("sale", "purchase")
and vals.get("refund_sequence", True)
and not vals.get("refund_sequence_id")
):
vals["refund_sequence_id"] = self._create_sequence(vals, refund=True).id
return super().create(vals_list)
@api.model
def _prepare_sequence(self, vals, refund=False):
code = vals.get("code") and vals["code"].upper() or ""
prefix = "{}{}/%(range_year)s/".format(refund and "R" or "", code)
seq_vals = {
"name": "{}{}".format(
vals.get("name", self.env._("Sequence")),
refund and " " + self.env._("Refund") or "",
),
"company_id": vals.get("company_id") or self.env.company.id,
"implementation": "no_gap",
"prefix": prefix,
"padding": 4,
"use_date_range": True,
}
return seq_vals
@api.model
def _create_sequence(self, vals, refund=False):
seq_vals = self._prepare_sequence(vals, refund=refund)
domain = [(key, "=", value) for key, value in seq_vals.items()]
existing = self.env["ir.sequence"].search(domain, limit=1)
if existing:
return existing
return self.env["ir.sequence"].sudo().create(seq_vals)
def _prepare_sequence_current_moves(self, refund=False):
"""Get sequence dict values the journal based on current moves"""
self.ensure_one()
move_domain = [
("journal_id", "=", self.id),
("name", "!=", "/"),
]
if self.refund_sequence:
#  Based on original Odoo behavior
if refund:
move_domain.append(("move_type", "in", ("out_refund", "in_refund")))
else:
move_domain.append(("move_type", "not in", ("out_refund", "in_refund")))
last_move = self.env["account.move"].search(
move_domain, limit=1, order="id DESC"
)
msg_err = (
"Journal {} could not get sequence {} values based on current moves. "
"Using default values.".format(self.id, refund and "refund" or "")
)
if not last_move:
_logger.warning("%s %s", msg_err, "No moves found")
return {}
try:
with self.env.cr.savepoint():
# get the current sequence values could be buggy to get
# But even we can use the default values
# or do manual changes instead of raising errors
last_sequence = last_move._get_last_sequence()
if not last_sequence:
last_sequence = (
last_move._get_last_sequence(relaxed=True)
or last_move._get_starting_sequence()
)
__, seq_format_values = last_move._get_sequence_format_param(
last_sequence
)
prefix1 = seq_format_values["prefix1"]
prefix = prefix1
if seq_format_values["year_length"] == 4:
prefix += "%(range_year)s"
elif seq_format_values["year_length"] == 2:
prefix += "%(range_y)s"
else:
# If there is not year so current values are valid
seq_vals = {
"padding": seq_format_values["seq_length"],
"suffix": seq_format_values["suffix"],
"prefix": prefix,
"date_range_ids": [],
"use_date_range": False,
"number_next_actual": seq_format_values["seq"] + 1,
}
return seq_vals
prefix2 = seq_format_values.get("prefix2") or ""
prefix += prefix2
month = seq_format_values.get("month") # It is 0 if only have year
if month:
prefix += "%(range_month)s"
prefix3 = seq_format_values.get("prefix3") or ""
where_name_value = "{}{}{}{}{}%".format(
prefix1,
"_" * seq_format_values["year_length"],
prefix2,
"_" * bool(month) * 2,
prefix3,
)
prefixes = prefix1 + prefix2
select_year = (
f"split_part(name, '{prefix2}', {prefixes.count(prefix2)})"
if prefix2
else "''"
)
prefixes += prefix3
select_month = (
f"split_part(name, '{prefix3}', {prefixes.count(prefix3)})"
if prefix3
else "''"
)
select_max_number = (
f"MAX(split_part(name, '{prefixes[-1]}', "
f"{prefixes.count(prefixes[-1]) + 1}):"
f":INTEGER) AS max_number"
)
query = (
f"SELECT {select_year}, {select_month}, "
f"{select_max_number} FROM account_move "
f"WHERE name LIKE %s AND journal_id=%s GROUP BY 1,2"
)
# It is not using user input
# pylint: disable=sql-injection
self.env.cr.execute(query, (where_name_value, self.id))
res = self.env.cr.fetchall()
prefix += prefix3
seq_vals = {
"padding": seq_format_values["seq_length"],
"suffix": seq_format_values["suffix"],
"prefix": prefix,
"date_range_ids": [],
"use_date_range": True,
}
for year, month, max_number in res:
if not year and not month:
seq_vals.update(
{
"use_date_range": False,
"number_next_actual": max_number + 1,
}
)
continue
if len(year) == 2:
# Year >=50 will be considered as last century 1950
# Year <=49 will be considered as current century 2049
if int(year) >= 50:
year = "19" + year
else:
year = "20" + year
if month:
date_from = fields.Date.to_date(f"{year}-{month}-1")
date_to = fields.Date.end_of(date_from, "month")
else:
date_from = fields.Date.to_date(f"{year}-1-1")
date_to = fields.Date.to_date(f"{year}-12-31")
seq_vals["date_range_ids"].append(
Command.create(
{
"date_from": date_from,
"date_to": date_to,
"number_next_actual": max_number + 1,
}
)
)
return seq_vals
except Exception as e:
_logger.warning("%s %s", msg_err, e)
return {}

View File

@@ -0,0 +1,99 @@
# Copyright 2021 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
class AccountMove(models.Model):
_inherit = "account.move"
name = fields.Char(compute="_compute_name_by_sequence")
# highest_name, sequence_prefix, sequence_number are not needed any more
# -> compute=False to improve perf
highest_name = fields.Char(compute=False)
sequence_prefix = fields.Char(compute=False)
sequence_number = fields.Integer(compute=False)
# made_sequence_hole is not relevant anymore (since based on sequence_prefix/number)
# -> compute=False to improve perf and to avoid displaying warning
made_sequence_hole = fields.Boolean(compute=False)
_sql_constraints = [
(
"name_state_diagonal",
"CHECK(COALESCE(name, '') NOT IN ('/', '') OR state!='posted')",
'A move can not be posted with name "/" or empty value\n'
"Check the journal sequence, please",
),
]
@api.depends("state", "journal_id", "date")
def _compute_name_by_sequence(self):
for move in self:
name = move.name or "/"
# I can't use posted_before in this IF because
# posted_before is set to True in _post() at the same
# time as state is set to "posted"
if (
move.state == "posted"
and (not move.name or move.name == "/")
and move.journal_id
and move.journal_id.sequence_id
):
if (
move.move_type in ("out_refund", "in_refund")
and move.journal_id.type in ("sale", "purchase")
and move.journal_id.refund_sequence
and move.journal_id.refund_sequence_id
):
seq = move.journal_id.refund_sequence_id
else:
seq = move.journal_id.sequence_id
# next_by_id(date) only applies on ir.sequence.date_range selection
# => we use with_context(ir_sequence_date=date).next_by_id()
# which applies on ir.sequence.date_range selection AND prefix
name = seq.with_context(ir_sequence_date=move.date).next_by_id()
move.name = name
self._inverse_name()
# We must by-pass this constraint of sequence.mixin
def _constrains_date_sequence(self):
return True
def _is_end_of_seq_chain(self):
invoices_no_gap_sequences = self.filtered(
lambda inv: inv.journal_id.sequence_id.implementation == "no_gap"
)
invoices_other_sequences = self - invoices_no_gap_sequences
if not invoices_other_sequences and invoices_no_gap_sequences:
return False
return super(AccountMove, invoices_other_sequences)._is_end_of_seq_chain()
def _fetch_duplicate_reference(self, matching_states=("draft", "posted")):
moves = self.filtered(
lambda m: m.is_sale_document() or m.is_purchase_document() and m.ref
)
if moves:
self.flush_model(["name", "journal_id", "move_type", "state"])
return super()._fetch_duplicate_reference(matching_states=matching_states)
def _get_last_sequence(self, relaxed=False, with_prefix=None):
return super()._get_last_sequence(relaxed, None)
@api.onchange("journal_id")
def _onchange_journal_id(self):
if not self.quick_edit_mode:
self.name = "/"
self._compute_name_by_sequence()
def _post(self, soft=True):
self.flush_recordset()
return super()._post(soft=soft)
@api.depends()
def _compute_name(self):
"""Overwrite account module method in order to
avoid side effect if legacy code call it directly
like when creating entry from email.
"""
return self._compute_name_by_sequence()

View File

@@ -0,0 +1,52 @@
from odoo import fields, models
class IrSequence(models.Model):
_inherit = "ir.sequence"
def _create_date_range_seq(self, date):
# Fix issue creating new date range for future dates
# It assigns more than one month
# TODO: Remove if odoo merge the following PR:
# https://github.com/odoo/odoo/pull/91019
date_obj = fields.Date.from_string(date)
sequence_range = self.env["ir.sequence.date_range"]
prefix_suffix = f"{self.prefix} {self.suffix}"
if "%(range_day)s" in prefix_suffix:
date_from = date_obj
date_to = date_obj
elif "%(range_month)s" in prefix_suffix:
date_from = fields.Date.start_of(date_obj, "month")
date_to = fields.Date.end_of(date_obj, "month")
else:
date_from = fields.Date.start_of(date_obj, "year")
date_to = fields.Date.end_of(date_obj, "year")
date_range = sequence_range.search(
[
("sequence_id", "=", self.id),
("date_from", ">=", date),
("date_from", "<=", date_to),
],
order="date_from desc",
limit=1,
)
if date_range:
date_to = fields.Date.subtract(date_range.date_from, days=1)
date_range = sequence_range.search(
[
("sequence_id", "=", self.id),
("date_to", ">=", date_from),
("date_to", "<=", date),
],
order="date_to desc",
limit=1,
)
if date_range:
date_to = fields.Date.add(date_range.date_to, days=1)
sequence_range_vals = {
"date_from": date_from,
"date_to": date_to,
"sequence_id": self.id,
}
seq_date_range = sequence_range.sudo().create(sequence_range_vals)
return seq_date_range

View File

@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"

View File

@@ -0,0 +1,16 @@
On the form view of an account journal, in the first tab, there is a
many2one link to the sequence. When you create a new journal, you can
keep this field empty and a new sequence will be automatically created
when you save the journal.
On sale and purchase journals, you have an additional option to have
another sequence dedicated to refunds.
Upon module installation, all existing journals will be updated with a
journal entry sequence (and also a credit note sequence for sale and
purchase journals). You should update the configuration of the sequences
to fit your needs. You can uncheck the option *Dedicated Credit Note
Sequence* on existing sale and purchase journals if you don't want it.
For the journals which already have journal entries, you should update
the sequence configuration to avoid a discontinuity in the numbering for
the next journal entry.

View File

@@ -0,0 +1,7 @@
- [Akretion](https://www.akretion.com):
- Alexis de Lattre \<<alexis.delattre@akretion.com>\>
- [Vauxoo](https://www.vauxoo.com):
- Moisés López \<<moylop260@vauxoo.com>\>
- Francisco Luna \<<fluna@vauxoo.com>\>
- [Factor Libre](https://www.factorlibre.com):
- Rodrigo Bonilla Martinez \<<rodrigo.bonilla@factorlibre.com>\>

View File

@@ -0,0 +1,38 @@
In Odoo version 13.0 and previous versions, the number of journal
entries was generated from a sequence configured on the journal.
In Odoo version 14.0, the number of journal entries can be manually set
by the user. Then, the number attributed for the next journal entries in
the same journal is computed by a complex piece of code that guesses the
format of the journal entry number from the number of the journal entry
which was manually entered by the user. It has several drawbacks:
- the available options for the sequence are limited,
- it is not possible to configure the sequence in advance before the
deployment in production,
- as it is error-prone, they added a *Resequence* wizard to re-generate
the journal entry numbers, which can be considered as illegal in many
countries,
- the [piece of
code](https://github.com/odoo/odoo/blob/14.0/addons/account/models/sequence_mixin.py)
that handles this is not easy to understand and quite difficult to
debug.
Using this module, you can configure what kind of documents the gap
sequence may be relaxed And even if you must use no-gap in your company
or country it will reduce the concurrency issues since the module is
using an extra table (ir_sequence) instead of locking the last record
For those like me who think that the implementation before Odoo v14.0
was much better, for the accountants who think it should not be possible
to manually enter the sequence of a customer invoice, for the auditor
who considers that resequencing journal entries is prohibited by law,
this module may be a solution to get out of the nightmare.
The field names used in this module to configure the sequence on the
journal are exactly the same as in Odoo version 13.0 and previous
versions. That way, if you migrate to Odoo version 14.0 and you install
this module immediately after the migration, you should keep the
previous behavior and the same sequences will continue to be used.
The module removes access to the *Resequence* wizard on journal entries.

View File

@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
account.access_account_resequence,Remove rights on account.resequence.wizard,account.model_account_resequence_wizard,account.group_account_manager,0,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 account.access_account_resequence Remove rights on account.resequence.wizard account.model_account_resequence_wizard account.group_account_manager 0 0 0 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1,489 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>Account Move Number Sequence</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="account-move-number-sequence">
<h1 class="title">Account Move Number Sequence</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:03b876b8607b5c34b0f49870637439187af2b3913e3af50d384838d28ef870ee
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/account-financial-tools/tree/18.0/account_move_name_sequence"><img alt="OCA/account-financial-tools" src="https://img.shields.io/badge/github-OCA%2Faccount--financial--tools-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/account-financial-tools-18-0/account-financial-tools-18-0-account_move_name_sequence"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/account-financial-tools&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>In Odoo version 13.0 and previous versions, the number of journal
entries was generated from a sequence configured on the journal.</p>
<p>In Odoo version 14.0, the number of journal entries can be manually set
by the user. Then, the number attributed for the next journal entries in
the same journal is computed by a complex piece of code that guesses the
format of the journal entry number from the number of the journal entry
which was manually entered by the user. It has several drawbacks:</p>
<ul class="simple">
<li>the available options for the sequence are limited,</li>
<li>it is not possible to configure the sequence in advance before the
deployment in production,</li>
<li>as it is error-prone, they added a <em>Resequence</em> wizard to re-generate
the journal entry numbers, which can be considered as illegal in many
countries,</li>
<li>the <a class="reference external" href="https://github.com/odoo/odoo/blob/14.0/addons/account/models/sequence_mixin.py">piece of
code</a>
that handles this is not easy to understand and quite difficult to
debug.</li>
</ul>
<p>Using this module, you can configure what kind of documents the gap
sequence may be relaxed And even if you must use no-gap in your company
or country it will reduce the concurrency issues since the module is
using an extra table (ir_sequence) instead of locking the last record</p>
<p>For those like me who think that the implementation before Odoo v14.0
was much better, for the accountants who think it should not be possible
to manually enter the sequence of a customer invoice, for the auditor
who considers that resequencing journal entries is prohibited by law,
this module may be a solution to get out of the nightmare.</p>
<p>The field names used in this module to configure the sequence on the
journal are exactly the same as in Odoo version 13.0 and previous
versions. That way, if you migrate to Odoo version 14.0 and you install
this module immediately after the migration, you should keep the
previous behavior and the same sequences will continue to be used.</p>
<p>The module removes access to the <em>Resequence</em> wizard on journal entries.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-6">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#toc-entry-1">Configuration</a></h1>
<p>On the form view of an account journal, in the first tab, there is a
many2one link to the sequence. When you create a new journal, you can
keep this field empty and a new sequence will be automatically created
when you save the journal.</p>
<p>On sale and purchase journals, you have an additional option to have
another sequence dedicated to refunds.</p>
<p>Upon module installation, all existing journals will be updated with a
journal entry sequence (and also a credit note sequence for sale and
purchase journals). You should update the configuration of the sequences
to fit your needs. You can uncheck the option <em>Dedicated Credit Note
Sequence</em> on existing sale and purchase journals if you dont want it.
For the journals which already have journal entries, you should update
the sequence configuration to avoid a discontinuity in the numbering for
the next journal entry.</p>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/account-financial-tools/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/account-financial-tools/issues/new?body=module:%20account_move_name_sequence%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#toc-entry-3">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-4">Authors</a></h2>
<ul class="simple">
<li>Akretion</li>
<li>Vauxoo</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-5">Contributors</a></h2>
<ul class="simple">
<li><a class="reference external" href="https://www.akretion.com">Akretion</a>:<ul>
<li>Alexis de Lattre &lt;<a class="reference external" href="mailto:alexis.delattre&#64;akretion.com">alexis.delattre&#64;akretion.com</a>&gt;</li>
</ul>
</li>
<li><a class="reference external" href="https://www.vauxoo.com">Vauxoo</a>:<ul>
<li>Moisés López &lt;<a class="reference external" href="mailto:moylop260&#64;vauxoo.com">moylop260&#64;vauxoo.com</a>&gt;</li>
<li>Francisco Luna &lt;<a class="reference external" href="mailto:fluna&#64;vauxoo.com">fluna&#64;vauxoo.com</a>&gt;</li>
</ul>
</li>
<li><a class="reference external" href="https://www.factorlibre.com">Factor Libre</a>:<ul>
<li>Rodrigo Bonilla Martinez &lt;<a class="reference external" href="mailto:rodrigo.bonilla&#64;factorlibre.com">rodrigo.bonilla&#64;factorlibre.com</a>&gt;</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainers</a>:</p>
<p><a class="reference external image-reference" href="https://github.com/alexis-via"><img alt="alexis-via" src="https://github.com/alexis-via.png?size=40px" /></a> <a class="reference external image-reference" href="https://github.com/moylop260"><img alt="moylop260" src="https://github.com/moylop260.png?size=40px" /></a> <a class="reference external image-reference" href="https://github.com/luisg123v"><img alt="luisg123v" src="https://github.com/luisg123v.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/account-financial-tools/tree/18.0/account_move_name_sequence">OCA/account-financial-tools</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,3 @@
from . import test_account_move_name_seq
from . import test_sequence_concurrency
from . import test_account_incoming_supplier_invoice

View File

@@ -0,0 +1,96 @@
import json
from odoo.tests import tagged
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
@tagged("post_install", "-at_install")
class TestAccountIncomingSupplierInvoice(AccountTestInvoicingCommon):
"""Testing creating account move fetching mail.alias"""
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env["ir.config_parameter"].sudo().set_param(
"mail.catchall.domain", "test-company.odoo.com"
)
cls.internal_user = cls.env["res.users"].create(
{
"name": "Internal User",
"login": "internal.user@test.odoo.com",
"email": "internal.user@test.odoo.com",
}
)
cls.supplier_partner = cls.env["res.partner"].create(
{
"name": "Your Supplier",
"email": "supplier@other.company.com",
"supplier_rank": 10,
}
)
cls.journal = cls.company_data["default_journal_purchase"]
journal_alias = cls.env["mail.alias"].create(
{
"alias_name": "test-bill",
"alias_model_id": cls.env.ref("account.model_account_move").id,
"alias_defaults": json.dumps(
{
"move_type": "in_invoice",
"company_id": cls.env.user.company_id.id,
"journal_id": cls.journal.id,
}
),
}
)
cls.journal.write({"alias_id": journal_alias.id})
def test_supplier_invoice_mailed_from_supplier(self):
"""this test is mainly inspired from
addons.account.tests.test_account_incoming_supplier_invoice
python module but we make sure account move is draft without
name
"""
message_parsed = {
"message_id": "message-id-dead-beef",
"subject": "Incoming bill",
"from": f"{self.supplier_partner.name} <{self.supplier_partner.email}>",
"to": f"{self.journal.alias_id.alias_name}@"
f"{self.journal.alias_id.alias_domain}",
"body": "You know, that thing that you bought.",
"attachments": [b"Hello, invoice"],
}
invoice = (
self.env["account.move"]
.with_context(
tracking_disable=False,
mail_create_nolog=False,
mail_create_nosubscribe=False,
mail_notrack=False,
)
.message_new(
message_parsed,
{"move_type": "in_invoice", "journal_id": self.journal.id},
)
)
message_ids = invoice.message_ids
self.assertEqual(
len(message_ids), 1, "Only one message should be posted in the chatter"
)
self.assertEqual(
message_ids.body,
"<p>Vendor Bill Created</p>",
"Only the invoice creation should be posted",
)
following_partners = invoice.message_follower_ids.mapped("partner_id")
self.assertEqual(following_partners, self.env.user.partner_id)
self.assertEqual(invoice.state, "draft")
self.assertEqual(invoice.name, "/")

View File

@@ -0,0 +1,388 @@
# Copyright 2021 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# @author: Moisés López <moylop260@vauxoo.com>
# @author: Francisco Luna <fluna@vauxoo.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from datetime import datetime
from unittest.mock import patch
from freezegun import freeze_time
from odoo import Command, fields
from odoo.exceptions import UserError, ValidationError
from odoo.tests import Form, TransactionCase, tagged
@tagged("post_install", "-at_install")
class TestAccountMoveNameSequence(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.company = cls.env.ref("base.main_company")
cls.partner = cls.env.ref("base.res_partner_3")
cls.misc_journal = cls.env["account.journal"].create(
{
"name": "Test Journal Move name seq",
"code": "ADLM",
"type": "general",
"company_id": cls.company.id,
}
)
cls.sales_seq = cls.env["ir.sequence"].create(
{
"name": "TB2C",
"implementation": "no_gap",
"prefix": "TB2CSEQ/%(range_year)s/",
"use_date_range": True,
"number_increment": 1,
"padding": 4,
"company_id": cls.company.id,
}
)
cls.sales_journal = cls.env["account.journal"].create(
{
"name": "TB2C",
"code": "TB2C",
"type": "sale",
"company_id": cls.company.id,
"refund_sequence": True,
"sequence_id": cls.sales_seq.id,
}
)
cls.purchase_journal = cls.env["account.journal"].create(
{
"name": "Test Purchase Journal Move name seq",
"code": "ADLP",
"type": "purchase",
"company_id": cls.company.id,
"refund_sequence": True,
}
)
cls.accounts = cls.env["account.account"].search(
[("company_ids", "=", cls.company.id)], limit=2
)
cls.account1 = cls.accounts[0]
cls.account2 = cls.accounts[1]
cls.date = datetime.now()
cls.purchase_journal2 = cls.purchase_journal.copy()
cls.journals = (
cls.misc_journal
| cls.purchase_journal
| cls.sales_journal
| cls.purchase_journal2
)
# This patch was added to avoid test failures in the CI pipeline caused by the
# `account_journal_restrict_mode` module. It prevents a validation error when
# disabling restrict mode on journals used in the test, allowing moves to be
# set to draft and deleted.
with patch("odoo.models.BaseModel._validate_fields"):
cls.journals.restrict_mode_hash_table = False
cls.lines = [
Command.create({"account_id": cls.account1.id, "debit": 10}),
Command.create({"account_id": cls.account2.id, "credit": 10}),
]
cls.invoice_line = [
Command.create(
{
"account_id": cls.account1.id,
"price_unit": 42.0,
"quantity": 12,
},
)
]
def test_seq_creation(self):
self.assertTrue(self.misc_journal.sequence_id)
seq = self.misc_journal.sequence_id
self.assertEqual(seq.company_id, self.company)
self.assertEqual(seq.implementation, "no_gap")
self.assertEqual(seq.padding, 4)
self.assertTrue(seq.use_date_range)
self.assertTrue(self.purchase_journal.sequence_id)
self.assertTrue(self.purchase_journal.refund_sequence_id)
seq = self.purchase_journal.refund_sequence_id
self.assertEqual(seq.company_id, self.company)
self.assertEqual(seq.implementation, "no_gap")
self.assertEqual(seq.padding, 4)
self.assertTrue(seq.use_date_range)
def test_misc_move_name(self):
move = self.env["account.move"].create(
{
"date": self.date,
"journal_id": self.misc_journal.id,
"line_ids": self.lines,
}
)
self.assertEqual(move.name, "/")
move.action_post()
seq = self.misc_journal.sequence_id
move_name = "{}{}".format(seq.prefix, "1".zfill(seq.padding))
move_name = move_name.replace("%(range_year)s", str(self.date.year))
self.assertEqual(move.name, move_name)
self.assertTrue(seq.date_range_ids)
drange_count = self.env["ir.sequence.date_range"].search_count(
[
("sequence_id", "=", seq.id),
("date_from", "=", fields.Date.add(self.date, month=1, day=1)),
]
)
self.assertEqual(drange_count, 1)
move.button_draft()
move.action_post()
self.assertEqual(move.name, move_name)
def test_prefix_move_name_use_move_date(self):
seq = self.misc_journal.sequence_id
seq.prefix = "TEST-%(year)s-%(month)s-"
self.env["ir.sequence.date_range"].sudo().create(
{
"date_from": "2021-07-01",
"date_to": "2022-06-30",
"sequence_id": seq.id,
}
)
with freeze_time("2022-01-01"):
move = self.env["account.move"].create(
{
"date": "2021-12-31",
"journal_id": self.misc_journal.id,
"line_ids": self.lines,
}
)
move.action_post()
self.assertEqual(move.name, "TEST-2021-12-0001")
with freeze_time("2022-01-01"):
move = self.env["account.move"].create(
{
"date": "2022-06-30",
"journal_id": self.misc_journal.id,
"line_ids": self.lines,
}
)
move.action_post()
self.assertEqual(move.name, "TEST-2022-06-0002")
with freeze_time("2022-01-01"):
move = self.env["account.move"].create(
{
"date": "2022-07-01",
"journal_id": self.misc_journal.id,
"line_ids": self.lines,
}
)
move.action_post()
self.assertEqual(move.name, "TEST-2022-07-0001")
def test_prefix_move_name_use_move_date_2(self):
seq = self.misc_journal.sequence_id
seq.prefix = "TEST-%(range_month)s-"
with freeze_time("2022-01-01"):
move = self.env["account.move"].create(
{
"date": "2022-06-30",
"journal_id": self.misc_journal.id,
"line_ids": self.lines,
}
)
move.action_post()
self.assertEqual(move.name, "TEST-06-0001")
def test_prefix_move_name_use_move_date_3(self):
seq = self.misc_journal.sequence_id
seq.prefix = "TEST-%(range_day)s-"
with freeze_time("2022-01-01"):
move = self.env["account.move"].create(
{
"date": "2022-01-01",
"journal_id": self.misc_journal.id,
"line_ids": self.lines,
}
)
move.action_post()
self.assertEqual(move.name, "TEST-01-0001")
def test_in_invoice_and_refund(self):
in_invoice = self.env["account.move"].create(
{
"journal_id": self.purchase_journal.id,
"invoice_date": self.date,
"partner_id": self.env.ref("base.res_partner_3").id,
"move_type": "in_invoice",
"invoice_line_ids": self.invoice_line
+ [
Command.create(
{
"account_id": self.account1.id,
"price_unit": 48.0,
"quantity": 10,
}
)
],
}
)
self.assertEqual(in_invoice.name, "/")
in_invoice.action_post()
in_invoice = in_invoice.copy(
{
"invoice_date": self.date,
}
)
in_invoice.action_post()
move_reversal = self.env["account.move.reversal"].create(
{
"move_ids": in_invoice.ids,
"journal_id": in_invoice.journal_id.id,
"reason": "no reason",
}
)
reversal = move_reversal.modify_moves()
draft_invoice = self.env["account.move"].browse(reversal["res_id"])
self.assertTrue(draft_invoice)
self.assertEqual(draft_invoice.state, "draft")
self.assertEqual(draft_invoice.move_type, "in_invoice")
in_invoice = in_invoice.copy(
{
"invoice_date": self.date,
}
)
in_invoice.action_post()
move_reversal = self.env["account.move.reversal"].create(
{
"move_ids": in_invoice.ids,
"journal_id": in_invoice.journal_id.id,
"reason": "no reason",
}
)
reversal = move_reversal.refund_moves()
draft_reversed_move = self.env["account.move"].browse(reversal["res_id"])
self.assertTrue(draft_reversed_move)
self.assertEqual(draft_reversed_move.state, "draft")
self.assertEqual(draft_reversed_move.move_type, "in_refund")
def test_in_refund(self):
in_refund_invoice = self.env["account.move"].create(
{
"journal_id": self.purchase_journal.id,
"invoice_date": self.date,
"partner_id": self.env.ref("base.res_partner_3").id,
"move_type": "in_refund",
"invoice_line_ids": self.invoice_line,
}
)
self.assertEqual(in_refund_invoice.name, "/")
in_refund_invoice.action_post()
seq = self.purchase_journal.refund_sequence_id
move_name = "{}{}".format(seq.prefix, "1".zfill(seq.padding))
move_name = move_name.replace("%(range_year)s", str(self.date.year))
self.assertEqual(in_refund_invoice.name, move_name)
in_refund_invoice.button_draft()
in_refund_invoice.action_post()
self.assertEqual(in_refund_invoice.name, move_name)
def test_remove_invoice_error_secuence_no_grap(self):
invoice = self.env["account.move"].create(
{
"date": self.date,
"journal_id": self.misc_journal.id,
"line_ids": self.lines,
}
)
self.assertEqual(invoice.name, "/")
invoice.action_post()
error_msg = (
"You can't delete a posted journal item. "
"Dont play games with your accounting records; "
"reset the journal entry to draft before deleting it."
)
with self.assertRaisesRegex(UserError, error_msg):
invoice.unlink()
invoice.button_draft()
invoice.button_cancel()
invoice.unlink()
def test_remove_invoice_error_secuence_standard(self):
implementation = {"implementation": "standard"}
self.purchase_journal.sequence_id.write(implementation)
self.purchase_journal.refund_sequence_id.write(implementation)
in_refund_invoice = self.env["account.move"].create(
{
"journal_id": self.purchase_journal.id,
"invoice_date": self.date,
"partner_id": self.env.ref("base.res_partner_3").id,
"move_type": "in_refund",
"invoice_line_ids": self.invoice_line,
}
)
self.assertEqual(in_refund_invoice.name, "/")
in_refund_invoice.action_post()
error_msg = (
"You can't delete a posted journal item. "
"Dont play games with your accounting records; "
"reset the journal entry to draft before deleting it."
)
with self.assertRaisesRegex(UserError, error_msg):
in_refund_invoice.unlink()
in_refund_invoice.button_draft()
in_refund_invoice.button_cancel()
self.assertTrue(in_refund_invoice.unlink())
def test_journal_check_journal_sequence(self):
new_journal = self.purchase_journal2
# same sequence_id and refund_sequence_id
with self.assertRaises(ValidationError):
new_journal.write({"refund_sequence_id": new_journal.sequence_id})
# company_id in sequence_id or refund_sequence_id to False
new_sequence_id = new_journal.sequence_id.copy({"company_id": False})
new_refund_sequence_id = new_journal.refund_sequence_id.copy(
{"company_id": False}
)
with self.assertRaises(ValidationError):
new_journal.write({"sequence_id": new_sequence_id.id})
with self.assertRaises(ValidationError):
new_journal.write({"refund_sequence_id": new_refund_sequence_id.id})
def test_constrains_date_sequence_true(self):
self.assertTrue(self.env["account.move"]._constrains_date_sequence())
def test_prefix_move_name_journal_onchange(self):
product = self.env["product.product"].create({"name": "Product"})
with Form(
self.env["account.move"].with_context(default_move_type="out_invoice")
) as invoice_form:
invoice_form.invoice_date = fields.Date.today()
invoice_form.partner_id = self.partner
with invoice_form.invoice_line_ids.new() as line_form:
line_form.product_id = product
invoice = invoice_form.save()
self.assertEqual(invoice.name, "/")
invoice.journal_id = self.sales_journal
self.assertEqual(invoice.name, "/", "name based on journal instead of sequence")
invoice.action_post()
self.assertIn("TB2CSEQ/", invoice.name, "name was not based on sequence")
def test_is_end_of_seq_chain(self):
self.env.user.groups_id -= self.env.ref("account.group_account_manager")
invoice = self.env["account.move"].create(
{
"date": self.date,
"journal_id": self.misc_journal.id,
"line_ids": self.lines,
}
)
invoice.action_post()
error_msg = (
"You cannot delete this entry, as it has already consumed "
"a sequence number and is not the last one in the chain. "
"You should probably revert it instead."
)
with self.assertRaisesRegex(UserError, error_msg):
invoice._unlink_forbid_parts_of_chain()

View File

@@ -0,0 +1,333 @@
import logging
import threading
import time
from unittest.mock import patch
import psycopg2
from odoo import SUPERUSER_ID, api, fields, tools
from odoo.tests import Form, TransactionCase, tagged
_logger = logging.getLogger(__name__)
class ThreadRaiseJoin(threading.Thread):
"""Custom Thread Class to raise the exception to main thread in the join"""
def run(self, *args, **kwargs):
self.exc = None
try:
return super().run(*args, **kwargs)
except BaseException as e:
self.exc = e
def join(self, *args, **kwargs):
res = super().join(*args, **kwargs)
# Wait for the thread finishes
while self.is_alive():
pass
# raise exception in the join
# to raise it in the main thread
if self.exc:
raise self.exc
return res
@tagged("post_install", "-at_install", "test_move_sequence")
class TestSequenceConcurrency(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.product = cls.env.ref("product.product_delivery_01")
cls.partner = cls.env.ref("base.res_partner_12")
cls.partner2 = cls.env.ref("base.res_partner_1")
cls.date = fields.Date.to_date("1985-04-14")
cls.journal_sale_std = cls.env.ref(
"account_move_name_sequence.journal_sale_std_demo"
)
cls.journal_cash_std = cls.env.ref(
"account_move_name_sequence.journal_cash_std_demo"
)
cls.cr0 = cls.cursor(cls)
cls.env0 = api.Environment(cls.cr0, SUPERUSER_ID, {})
cls.cr1 = cls.cursor(cls)
cls.env1 = api.Environment(cls.cr1, SUPERUSER_ID, {})
cls.cr2 = cls.cursor(cls)
cls.env2 = api.Environment(cls.cr2, SUPERUSER_ID, {})
for cr in [cls.cr0, cls.cr1, cls.cr2]:
# Set a 10-second timeout to avoid waiting too long for release locks
cr.execute("SET LOCAL statement_timeout = '10s'")
cls.registry.enter_test_mode(cls.cursor(cls))
cls.last_existing_move = cls.env["account.move"].search(
[], limit=1, order="id desc"
)
@classmethod
def _clean_moves_and_payments(cls, last_move):
"""Delete moves and payments created after finish test."""
moves = (
cls.env["account.move"]
.with_context(force_delete=True)
.search([("id", ">=", last_move)])
)
payments = moves.payment_ids
moves_without_payments = moves - payments.move_id
if payments:
payments.action_draft()
payments.unlink()
if moves_without_payments:
moves_without_payments.filtered(
lambda move: move.state != "draft"
).button_draft()
moves_without_payments.unlink()
def _commit_crs(self, *envs):
for env in envs:
env.cr.commit()
def _create_invoice_form(
self, env, post=True, partner=None, ir_sequence_standard=False
):
ctx = {"default_move_type": "out_invoice"}
with Form(env["account.move"].with_context(**ctx)) as invoice_form:
# Use another partner to bypass "increase_rank" lock error
invoice_form.partner_id = partner or self.partner
invoice_form.invoice_date = self.date
with invoice_form.invoice_line_ids.new() as line_form:
line_form.product_id = self.product
line_form.price_unit = 100.0
line_form.tax_ids.clear()
invoice = invoice_form.save()
if ir_sequence_standard:
invoice.journal_id = self.journal_sale_std
if post:
# This patch was added to avoid test failures in the CI pipeline caused by
# the `account_journal_restrict_mode` module. It avoids errors when setting
# posted moves to draft and deleting them by bypassing the method that
# writes the hash field used for validation.
with patch(
"odoo.addons.account.models.account_move.AccountMove._hash_moves"
):
invoice.action_post()
return invoice
def _create_payment_form(self, env, partner=None, ir_sequence_standard=False):
with Form(
env["account.payment"].with_context(
default_payment_type="inbound",
default_partner_type="customer",
default_move_journal_types=("bank", "cash"),
)
) as payment_form:
payment_form.partner_id = partner or self.partner
payment_form.amount = 100
payment_form.date = self.date
if ir_sequence_standard:
payment_form.journal_id = self.journal_cash_std
payment = payment_form.save()
# This patch was added to avoid test failures in the CI pipeline caused by
# the `account_journal_restrict_mode` module. It avoids errors when setting
# posted moves to draft and deleting them by bypassing the method that
# writes the hash field used for validation.
with patch("odoo.addons.account.models.account_move.AccountMove._hash_moves"):
payment.action_post()
return payment
def _create_invoice_payment(
self, deadlock_timeout, payment_first=False, ir_sequence_standard=False
):
with self.cursor() as cr, cr.savepoint():
env = api.Environment(cr, SUPERUSER_ID, {})
cr_pid = cr.connection.get_backend_pid()
# Avoid waiting for a long time and it needs to be less than deadlock
cr.execute("SET LOCAL statement_timeout = '%ss'", (deadlock_timeout + 10,))
if payment_first:
_logger.info("Creating payment cr %s", cr_pid)
self._create_payment_form(
env, ir_sequence_standard=ir_sequence_standard
)
_logger.info("Creating invoice cr %s", cr_pid)
self._create_invoice_form(
env, ir_sequence_standard=ir_sequence_standard
)
else:
_logger.info("Creating invoice cr %s", cr_pid)
self._create_invoice_form(
env, ir_sequence_standard=ir_sequence_standard
)
_logger.info("Creating payment cr %s", cr_pid)
self._create_payment_form(
env, ir_sequence_standard=ir_sequence_standard
)
# sleep in order to avoid release the locks too faster
# It could be many methods called after creating these
# kind of records e.g. reconcile
_logger.info("Finishing waiting %s", (deadlock_timeout + 12))
time.sleep(deadlock_timeout + 12)
def test_sequence_concurrency_10_draft_invoices(self):
"""Creating 2 DRAFT invoices not should raises errors"""
# Create "last move" to lock
self._create_invoice_form(self.env0)
self.cr0.commit()
with self.cr1.savepoint(), self.cr2.savepoint():
invoice1 = self._create_invoice_form(self.env1, post=False)
self.assertEqual(invoice1.state, "draft")
invoice2 = self._create_invoice_form(self.env2, post=False)
self.assertEqual(invoice2.state, "draft")
self._commit_crs(self.env0, self.env1, self.env2)
def test_sequence_concurrency_20_editing_last_invoice(self):
"""Edit last invoice and create a new invoice
should not raises errors"""
# Create "last move" to lock
invoice = self._create_invoice_form(self.env0)
self.cr0.commit()
with self.cr0.savepoint(), self.cr1.savepoint():
# Edit something in "last move"
invoice.write({"write_uid": self.env0.uid})
self.env0.flush_all()
self._create_invoice_form(self.env1)
self._commit_crs(self.env0, self.env1)
def test_sequence_concurrency_30_editing_last_payment(self):
"""Edit last payment and create a new payment
should not raises errors"""
# Create "last move" to lock
payment = self._create_payment_form(self.env0)
payment_move = payment.move_id
self.cr0.commit()
with self.cr0.savepoint(), self.cr1.savepoint():
# Edit something in "last move"
payment_move.write({"write_uid": self.env0.uid})
self.env0.flush_all()
self._create_payment_form(self.env1)
self._commit_crs(self.env0, self.env1)
@tools.mute_logger("odoo.sql_db")
def test_sequence_concurrency_40_reconciling_last_invoice(self):
"""Reconcile last invoice and create a new one
should not raises errors"""
# Create "last move" to lock
invoice = self._create_invoice_form(self.env0)
payment = self._create_payment_form(self.env0)
payment_move = payment.move_id
self.cr0.commit()
lines2reconcile = (
(payment_move | invoice)
.mapped("line_ids")
.filtered(lambda line: line.account_id.account_type == "asset_receivable")
)
with self.cr0.savepoint(), self.cr1.savepoint():
# Reconciling "last move"
# reconcile a payment with many invoices spend a lot so it could
# lock records too many time
lines2reconcile.reconcile()
# Many pieces of code call flush directly
self.env0.flush_all()
self._create_invoice_form(self.env1)
self._commit_crs(self.env0, self.env1)
def test_sequence_concurrency_50_reconciling_last_payment(self):
"""Reconcile last payment and create a new one
should not raises errors"""
# Create "last move" to lock
invoice = self._create_invoice_form(self.env0)
payment = self._create_payment_form(self.env0)
payment_move = payment.move_id
self.cr0.commit()
lines2reconcile = (
(payment_move | invoice)
.mapped("line_ids")
.filtered(lambda line: line.account_id.account_type == "asset_receivable")
)
with self.cr0.savepoint(), self.cr1.savepoint():
# Reconciling "last move"
# reconcile a payment with many invoices spend a lot so it could
# lock records too many time
lines2reconcile.reconcile()
# Many pieces of code call flush directly
self.env0.flush_all()
self._create_payment_form(self.env1)
self._commit_crs(self.env0, self.env1)
def test_sequence_concurrency_90_payments(self):
"""Creating concurrent payments should not raises errors"""
# Create "last move" to lock
self._create_payment_form(self.env0, ir_sequence_standard=True)
self.cr0.commit()
with self.cr1.savepoint(), self.cr2.savepoint():
self._create_payment_form(self.env1, ir_sequence_standard=True)
self._create_payment_form(self.env2, ir_sequence_standard=True)
self._commit_crs(self.env0, self.env1, self.env2)
def test_sequence_concurrency_92_invoices(self):
"""Creating concurrent invoices should not raises errors"""
# Create "last move" to lock
self._create_invoice_form(self.env0, ir_sequence_standard=True)
self.cr0.commit()
with self.cr1.savepoint(), self.cr2.savepoint():
self._create_invoice_form(self.env1, ir_sequence_standard=True)
# Using another partner to bypass "increase_rank" lock error
self._create_invoice_form(
self.env2, partner=self.partner2, ir_sequence_standard=True
)
self._commit_crs(self.env0, self.env1, self.env2)
@tools.mute_logger("odoo.sql_db")
def test_sequence_concurrency_95_pay2inv_inv2pay(self):
"""Creating concurrent payment then invoice and invoice then payment
should not raises errors
It raises deadlock sometimes"""
# Create "last move" to lock
self._create_invoice_form(self.env0)
# Create "last move" to lock
self._create_payment_form(self.env0)
self.cr0.commit()
self.cr0.execute(
"SELECT setting FROM pg_settings WHERE name = 'deadlock_timeout'"
)
deadlock_timeout = int(self.cr0.fetchone()[0]) # ms
# You could not have permission to set this parameter
# psycopg2.errors.InsufficientPrivilege
self.assertTrue(
deadlock_timeout,
"You need to configure PG parameter deadlock_timeout='1s'",
)
deadlock_timeout = int(deadlock_timeout / 1000) # s
t_pay_inv = ThreadRaiseJoin(
target=self._create_invoice_payment,
args=(deadlock_timeout, True, True),
name="Thread payment invoice",
)
t_inv_pay = ThreadRaiseJoin(
target=self._create_invoice_payment,
args=(deadlock_timeout, False, True),
name="Thread invoice payment",
)
t_pay_inv.start()
t_inv_pay.start()
# the thread could raise the error before to wait for it so disable coverage
self._thread_join(t_pay_inv, deadlock_timeout + 15)
self._thread_join(t_inv_pay, deadlock_timeout + 15)
def _thread_join(self, thread_obj, timeout):
try:
thread_obj.join(timeout=timeout) # pragma: no cover
self.assertFalse(
thread_obj.is_alive(),
"The thread wait is over. but the cursor may still be in use!",
)
except psycopg2.OperationalError as e:
if e.pgcode in [
psycopg2.errorcodes.SERIALIZATION_FAILURE,
psycopg2.errorcodes.LOCK_NOT_AVAILABLE,
]: # pragma: no cover
# Concurrency error is expected but not deadlock so ok
pass
elif e.pgcode == psycopg2.errorcodes.DEADLOCK_DETECTED: # pragma: no cover
self.assertFalse(True, "Deadlock detected.")
else: # pragma: no cover
raise

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2021 Akretion France (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
@author: Moisés López <moylop260@vauxoo.com>
@author: Francisco Luna <fluna@vauxoo.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_account_journal_form" model="ir.ui.view">
<field name="model">account.journal</field>
<field name="inherit_id" ref="account.view_account_journal_form" />
<field name="arch" type="xml">
<field name="refund_sequence" position="before">
<field
name="sequence_id"
required="1"
context="{
'default_name': name,
'default_company_id': company_id,
'default_implementation': 'no_gap',
'default_padding': 4,
'default_use_date_range': True,
'default_prefix': (code or 'UNKNOWN') + '/%%(range_year)s/'
}"
/>
</field>
<field name="refund_sequence" position="after">
<field
name="refund_sequence_id"
invisible="type not in ('sale', 'purchase') or not refund_sequence"
required="type in ('sale', 'purchase') and refund_sequence"
context="{
'default_name': name,
'default_company_id': company_id,
'default_implementation': 'no_gap',
'default_padding': 4,
'default_use_date_range': True,
'default_prefix': 'R' + (code or 'UNKNOWN') + '/%%(range_year)s/'
}"
/>
</field>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2021 Akretion France (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_move_form" model="ir.ui.view">
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_move_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='highest_name']/.." position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<xpath
expr="//div[hasclass('oe_title')]//field[@name='name']"
position="attributes"
>
<attribute name="invisible">name == '/'</attribute>
<attribute name="readonly">1</attribute>
</xpath>
<xpath expr="//div[hasclass('oe_title')]//h1//span" position="attributes">
<attribute name="invisible">state != 'draft' or name != '/'</attribute>
</xpath>
</field>
</record>
</odoo>

8
doc8.ini Normal file
View File

@@ -0,0 +1,8 @@
[doc8]
file-encoding="UTF-8"
# Ignore
# D001: Line too long
ignore=D001
allow-long-titles=True
sphinx=True

3
pyproject.toml Normal file
View File

@@ -0,0 +1,3 @@
[tool.black]
line-length=119
skip-string-normalization=false