From 1a1d816c7b0c13ad0c8af3b65d7cf6d6f139489c Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Mon, 23 May 2022 10:23:58 +0100 Subject: [PATCH] [ADD] account_journal_general_sequence: account move entry sequence Under some legislations, account moves must follow a single sequence. Since Odoo removed this sequence number in recent versions, this information was lost. With this module, you can force your account moves to follow a separate sequence. The sequence is automatic when a move is posted. Includes a wizard to reorder those numbers in the sequence. @moduon MT-676 --- account_journal_general_sequence/README.rst | 121 +++++ account_journal_general_sequence/__init__.py | 2 + .../__manifest__.py | 22 + .../i18n/account_journal_general_sequence.pot | 188 +++++++ account_journal_general_sequence/i18n/es.po | 193 ++++++++ .../models/__init__.py | 3 + .../models/account_journal.py | 39 ++ .../models/account_move.py | 56 +++ .../models/account_move_line.py | 10 + .../readme/CONFIGURE.rst | 13 + .../readme/CONTRIBUTORS.rst | 2 + .../readme/DESCRIPTION.rst | 5 + .../readme/USAGE.rst | 22 + .../security/ir.model.access.csv | 2 + .../static/description/icon.png | Bin 0 -> 28942 bytes .../static/description/index.html | 468 ++++++++++++++++++ .../tests/__init__.py | 1 + .../tests/test_numbering.py | 46 ++ .../views/account_journal.xml | 28 ++ .../views/account_move.xml | 42 ++ .../views/account_move_line.xml | 39 ++ .../wizards/__init__.py | 1 + .../wizards/account_move_renumber_wizard.py | 82 +++ .../account_move_renumber_wizard_views.xml | 46 ++ 24 files changed, 1431 insertions(+) create mode 100644 account_journal_general_sequence/README.rst create mode 100644 account_journal_general_sequence/__init__.py create mode 100644 account_journal_general_sequence/__manifest__.py create mode 100644 account_journal_general_sequence/i18n/account_journal_general_sequence.pot create mode 100644 account_journal_general_sequence/i18n/es.po create mode 100644 account_journal_general_sequence/models/__init__.py create mode 100644 account_journal_general_sequence/models/account_journal.py create mode 100644 account_journal_general_sequence/models/account_move.py create mode 100644 account_journal_general_sequence/models/account_move_line.py create mode 100644 account_journal_general_sequence/readme/CONFIGURE.rst create mode 100644 account_journal_general_sequence/readme/CONTRIBUTORS.rst create mode 100644 account_journal_general_sequence/readme/DESCRIPTION.rst create mode 100644 account_journal_general_sequence/readme/USAGE.rst create mode 100644 account_journal_general_sequence/security/ir.model.access.csv create mode 100644 account_journal_general_sequence/static/description/icon.png create mode 100644 account_journal_general_sequence/static/description/index.html create mode 100644 account_journal_general_sequence/tests/__init__.py create mode 100644 account_journal_general_sequence/tests/test_numbering.py create mode 100644 account_journal_general_sequence/views/account_journal.xml create mode 100644 account_journal_general_sequence/views/account_move.xml create mode 100644 account_journal_general_sequence/views/account_move_line.xml create mode 100644 account_journal_general_sequence/wizards/__init__.py create mode 100644 account_journal_general_sequence/wizards/account_move_renumber_wizard.py create mode 100644 account_journal_general_sequence/wizards/account_move_renumber_wizard_views.xml diff --git a/account_journal_general_sequence/README.rst b/account_journal_general_sequence/README.rst new file mode 100644 index 00000000..fa0297bd --- /dev/null +++ b/account_journal_general_sequence/README.rst @@ -0,0 +1,121 @@ +==================================== +General sequence in account journals +==================================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-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/14.0/account_journal_general_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-14-0/account-financial-tools-14-0-account_journal_general_sequence + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/92/14.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module extends the functionality of account to support sequenced entry +numbers and to allow you to renumber them if needed, on demand. + +It adds a new field called *Entry number*. This is independent from the *Number* +that Odoo adds by default, and has different purpose. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To configure journal sequences: + +#. Have full accounting permissions. +#. Go to *Invoicing > Configuration > Accounting > Journals*. +#. Select or create the journal you want to configure. +#. Use *Account entry number sequence* to configure a sequence. + +Note that: + +* Various journals can share the same sequence. +* All journals get a shared default sequence, per company. +* Entry numbers must be unique per company. So, if you use different sequences + per journal, make sure they don't produce colliding results. + +Usage +===== + +To see journal entry numbers: + +#. Go to *Invoicing > Accounting > Miscellaneous > Journal Entries*. +#. Notice the new field *Entry Number*. Only posted moves get an entry number. + +Note that: + +* You can use that new field in quick searches. +* You can see it also in *Invoicing > Accounting > Miscellaneous > Journal Items*. + +To renumber journal entries: + +#. Have full accounting permissions. +#. Have *Accounting / Invoicing / Billing Administrator* permissions. +#. Go to *Invoicing > Accounting > Actions > Renumber journal entries*. +#. Configure those parameters. +#. Click on *Renumber*. + +Note that: + +* You will only be able to select sequences related to journals. +* A sequence usually affects various journals, if not all. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Moduon + +Contributors +~~~~~~~~~~~~ + +* Jairo Llopis (`Moduon `__) +* Rafael Blasco (`Moduon `__) + +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. + +This module is part of the `OCA/account-financial-tools `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_journal_general_sequence/__init__.py b/account_journal_general_sequence/__init__.py new file mode 100644 index 00000000..aee8895e --- /dev/null +++ b/account_journal_general_sequence/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/account_journal_general_sequence/__manifest__.py b/account_journal_general_sequence/__manifest__.py new file mode 100644 index 00000000..2ca011f5 --- /dev/null +++ b/account_journal_general_sequence/__manifest__.py @@ -0,0 +1,22 @@ +# Copyright 2022 Moduon Team S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). +{ + "name": "General sequence in account journals", + "summary": "Add configurable sequence to account moves, per journal", + "version": "14.0.1.0.0", + "category": "Accounting/Accounting", + "website": "https://github.com/OCA/account-financial-tools", + "author": "Moduon, Odoo Community Association (OCA)", + "license": "LGPL-3", + "external_dependencies": {"python": ["freezegun"]}, + "depends": [ + "account", + ], + "data": [ + "security/ir.model.access.csv", + "views/account_journal.xml", + "views/account_move_line.xml", + "views/account_move.xml", + "wizards/account_move_renumber_wizard_views.xml", + ], +} diff --git a/account_journal_general_sequence/i18n/account_journal_general_sequence.pot b/account_journal_general_sequence/i18n/account_journal_general_sequence.pot new file mode 100644 index 00000000..3fa5305f --- /dev/null +++ b/account_journal_general_sequence/i18n/account_journal_general_sequence.pot @@ -0,0 +1,188 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_journal_general_sequence +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-27 08:32+0000\n" +"PO-Revision-Date: 2022-05-27 08:32+0000\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_journal_general_sequence +#: code:addons/account_journal_general_sequence/models/account_journal.py:0 +#, python-format +msgid "Account entry default numbering" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_journal__entry_number_sequence_id +msgid "Account entry number sequence" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model,name:account_journal_general_sequence.model_account_move_renumber_wizard +msgid "Account move entry renumbering wizard" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,help:account_journal_general_sequence.field_account_bank_statement_line__entry_number +#: model:ir.model.fields,help:account_journal_general_sequence.field_account_move__entry_number +#: model:ir.model.fields,help:account_journal_general_sequence.field_account_move_line__entry_number +#: model:ir.model.fields,help:account_journal_general_sequence.field_account_payment__entry_number +msgid "Automatic numbering, based on journal configuration." +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__available_sequence_ids +msgid "Available sequences" +msgstr "" + +#. module: account_journal_general_sequence +#: model_terms:ir.ui.view,arch_db:account_journal_general_sequence.account_move_renumber_wizard_view_form +msgid "Cancel" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__create_uid +msgid "Created by" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__create_date +msgid "Created on" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_journal__display_name +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move__display_name +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_line__display_name +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__display_name +msgid "Display Name" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_bank_statement_line__entry_number +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move__entry_number +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_line__entry_number +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_payment__entry_number +msgid "Entry Number" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.constraint,message:account_journal_general_sequence.constraint_account_move_entry_number_unique +msgid "Entry number must be unique per company." +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_journal__id +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move__id +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_line__id +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__id +msgid "ID" +msgstr "" + +#. module: account_journal_general_sequence +#: model_terms:ir.ui.view,arch_db:account_journal_general_sequence.account_move_renumber_wizard_view_form +msgid "" +"If you have already filed and legalized your journals with the competent " +"authority, this action could change it. Continue?" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model,name:account_journal_general_sequence.model_account_journal +msgid "Journal" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model,name:account_journal_general_sequence.model_account_move +msgid "Journal Entry" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model,name:account_journal_general_sequence.model_account_move_line +msgid "Journal Item" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_journal____last_update +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move____last_update +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_line____last_update +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard____last_update +msgid "Last Modified on" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__write_date +msgid "Last Updated on" +msgstr "" + +#. module: account_journal_general_sequence +#: code:addons/account_journal_general_sequence/wizards/account_move_renumber_wizard.py:0 +#, python-format +msgid "No account moves found." +msgstr "" + +#. module: account_journal_general_sequence +#: model_terms:ir.ui.view,arch_db:account_journal_general_sequence.account_move_renumber_wizard_view_form +msgid "Renumber" +msgstr "" + +#. module: account_journal_general_sequence +#: model_terms:ir.ui.view,arch_db:account_journal_general_sequence.account_move_renumber_wizard_view_form +msgid "Renumber account entry numbers" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,help:account_journal_general_sequence.field_account_move_renumber_wizard__starting_date +msgid "Renumber account moves starting this day." +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.actions.act_window,name:account_journal_general_sequence.account_move_renumber_wizard_action +#: model:ir.ui.menu,name:account_journal_general_sequence.account_move_renumber_wizard_menu +msgid "Renumber journal entries" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,help:account_journal_general_sequence.field_account_move_renumber_wizard__starting_number +msgid "Reset sequence to this number before starting." +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__sequence_id +msgid "Sequence" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,help:account_journal_general_sequence.field_account_move_renumber_wizard__sequence_id +msgid "" +"Sequence to use for renumbering. Affects all journals that use this " +"sequence." +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,help:account_journal_general_sequence.field_account_journal__entry_number_sequence_id +msgid "Sequence used for account entry numbering." +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__starting_date +msgid "Starting Date" +msgstr "" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__starting_number +msgid "Starting Number" +msgstr "" diff --git a/account_journal_general_sequence/i18n/es.po b/account_journal_general_sequence/i18n/es.po new file mode 100644 index 00000000..0870890a --- /dev/null +++ b/account_journal_general_sequence/i18n/es.po @@ -0,0 +1,193 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_move_numbered +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-27 08:32+0000\n" +"PO-Revision-Date: 2022-05-27 09:34+0100\n" +"Last-Translator: Jairo Llopis \n" +"Language-Team: \n" +"Language: es_ES\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 3.0.1\n" + +#. module: account_journal_general_sequence +#: code:addons/account_journal_general_sequence/models/account_journal.py:0 +#, python-format +msgid "Account entry default numbering" +msgstr "Numeración por defecto de asientos contables" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_journal__entry_number_sequence_id +msgid "Account entry number sequence" +msgstr "Secuencia para numerar los asientos" + +#. module: account_journal_general_sequence +#: model:ir.model,name:account_journal_general_sequence.model_account_move_renumber_wizard +msgid "Account move entry renumbering wizard" +msgstr "Asistente para renumerar asientos contables" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,help:account_journal_general_sequence.field_account_bank_statement_line__entry_number +#: model:ir.model.fields,help:account_journal_general_sequence.field_account_move__entry_number +#: model:ir.model.fields,help:account_journal_general_sequence.field_account_move_line__entry_number +#: model:ir.model.fields,help:account_journal_general_sequence.field_account_payment__entry_number +msgid "Automatic numbering, based on journal configuration." +msgstr "Numeración automática, basada en la configuración por diario." + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__available_sequence_ids +msgid "Available sequences" +msgstr "Secuencias disponibles" + +#. module: account_journal_general_sequence +#: model_terms:ir.ui.view,arch_db:account_journal_general_sequence.account_move_renumber_wizard_view_form +msgid "Cancel" +msgstr "Cancelar" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_journal__display_name +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move__display_name +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_line__display_name +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_bank_statement_line__entry_number +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move__entry_number +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_line__entry_number +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_payment__entry_number +msgid "Entry Number" +msgstr "Número de asiento" + +#. module: account_journal_general_sequence +#: model:ir.model.constraint,message:account_journal_general_sequence.constraint_account_move_entry_number_unique +msgid "Entry number must be unique per company." +msgstr "El número de asiento debe ser único por compañía." + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_journal__id +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move__id +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_line__id +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__id +msgid "ID" +msgstr "ID" + +#. module: account_journal_general_sequence +#: model_terms:ir.ui.view,arch_db:account_journal_general_sequence.account_move_renumber_wizard_view_form +msgid "" +"If you have already filed and legalized your journals with the competent " +"authority, this action could change it. Continue?" +msgstr "" +"Si ya ha presentado y legalizado sus diarios ante la autoridad competente, " +"esta acción podría modificarlo. ¿Continuar?" + +#. module: account_journal_general_sequence +#: model:ir.model,name:account_journal_general_sequence.model_account_journal +msgid "Journal" +msgstr "Diario" + +#. module: account_journal_general_sequence +#: model:ir.model,name:account_journal_general_sequence.model_account_move +msgid "Journal Entry" +msgstr "Asiento contable" + +#. module: account_journal_general_sequence +#: model:ir.model,name:account_journal_general_sequence.model_account_move_line +msgid "Journal Item" +msgstr "Apunte contable" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_journal____last_update +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move____last_update +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_line____last_update +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard____last_update +msgid "Last Modified on" +msgstr "Última modificación el" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__write_uid +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__write_date +msgid "Last Updated on" +msgstr "Última actualización el" + +#. module: account_journal_general_sequence +#: code:addons/account_journal_general_sequence/wizards/account_move_renumber_wizard.py:0 +#, python-format +msgid "No account moves found." +msgstr "No se ha encontrado ningún asiento contable." + +#. module: account_journal_general_sequence +#: model_terms:ir.ui.view,arch_db:account_journal_general_sequence.account_move_renumber_wizard_view_form +msgid "Renumber" +msgstr "Renumerar" + +#. module: account_journal_general_sequence +#: model_terms:ir.ui.view,arch_db:account_journal_general_sequence.account_move_renumber_wizard_view_form +msgid "Renumber account entry numbers" +msgstr "Renumerar asientos contables" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,help:account_journal_general_sequence.field_account_move_renumber_wizard__starting_date +msgid "Renumber account moves starting this day." +msgstr "Renumerar asientos contables a partir de este día." + +#. module: account_journal_general_sequence +#: model:ir.actions.act_window,name:account_journal_general_sequence.account_move_renumber_wizard_action +#: model:ir.ui.menu,name:account_journal_general_sequence.account_move_renumber_wizard_menu +msgid "Renumber journal entries" +msgstr "Renumerar asientos contables" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,help:account_journal_general_sequence.field_account_move_renumber_wizard__starting_number +msgid "Reset sequence to this number before starting." +msgstr "Reiniciar la secuencia a este número antes de empezar." + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__sequence_id +msgid "Sequence" +msgstr "Secuencia" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,help:account_journal_general_sequence.field_account_move_renumber_wizard__sequence_id +msgid "" +"Sequence to use for renumbering. Affects all journals that use this sequence." +msgstr "" +"Secuencia usada al renumerar. Afectará a todos los diarios que usen esta " +"secuencia." + +#. module: account_journal_general_sequence +#: model:ir.model.fields,help:account_journal_general_sequence.field_account_journal__entry_number_sequence_id +msgid "Sequence used for account entry numbering." +msgstr "Secuencia usada para renumerar los asientos contables." + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__starting_date +msgid "Starting Date" +msgstr "Fecha de inicio" + +#. module: account_journal_general_sequence +#: model:ir.model.fields,field_description:account_journal_general_sequence.field_account_move_renumber_wizard__starting_number +msgid "Starting Number" +msgstr "Número inicial" diff --git a/account_journal_general_sequence/models/__init__.py b/account_journal_general_sequence/models/__init__.py new file mode 100644 index 00000000..3af26e9a --- /dev/null +++ b/account_journal_general_sequence/models/__init__.py @@ -0,0 +1,3 @@ +from . import account_journal +from . import account_move +from . import account_move_line diff --git a/account_journal_general_sequence/models/account_journal.py b/account_journal_general_sequence/models/account_journal.py new file mode 100644 index 00000000..c36eb153 --- /dev/null +++ b/account_journal_general_sequence/models/account_journal.py @@ -0,0 +1,39 @@ +# Copyright 2022 Moduon +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). +import logging + +from odoo import _, fields, models + +_logger = logging.getLogger(__name__) + + +class AccountJournal(models.Model): + _inherit = "account.journal" + + entry_number_sequence_id = fields.Many2one( + comodel_name="ir.sequence", + string="Account entry number sequence", + default=lambda self: self._default_entry_number_sequence(), + copy=False, + help="Sequence used for account entry numbering.", + ) + + def _default_entry_number_sequence(self): + """Get the default sequence for all journals.""" + result = self.env["ir.sequence"].search( + [("code", "=", "account_journal_general_sequence.default")] + ) + if result: + return result + _logger.info("Creating default sequence for account move numbers") + result = self.env["ir.sequence"].create( + { + "name": _("Account entry default numbering"), + "code": "account_journal_general_sequence.default", + "implementation": "no_gap", + "prefix": "%(range_year)s/", + "padding": 10, + "use_date_range": True, + } + ) + return result diff --git a/account_journal_general_sequence/models/account_move.py b/account_journal_general_sequence/models/account_move.py new file mode 100644 index 00000000..c3c2c9c1 --- /dev/null +++ b/account_journal_general_sequence/models/account_move.py @@ -0,0 +1,56 @@ +# Copyright 2022 Moduon +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). +import logging + +from odoo import api, fields, models + +_logger = logging.getLogger(__name__) + + +class AccountMove(models.Model): + _inherit = "account.move" + + _sql_constraints = [ + ( + "entry_number_unique", + "UNIQUE(entry_number, company_id)", + "Entry number must be unique per company.", + ), + ] + + entry_number = fields.Char( + index=True, + readonly=True, + tracking=True, + store=True, + compute="_compute_entry_number", + help="Automatic numbering, based on journal configuration.", + ) + + @api.depends("state") + def _compute_entry_number(self): + """Assign an entry number when posting.""" + canceled = self.filtered_domain( + [("state", "=", "cancel"), ("entry_number", "!=", False)] + ) + canceled.entry_number = False + if canceled: + no_gap_seqs = canceled.mapped( + "journal_id.entry_number_sequence_id" + ).filtered_domain([("implementation", "=", "no_gap")]) + if no_gap_seqs: + _logger.warning( + "Emptied entry_number for %r after cancellation. " + "This created gaps on %r.", + canceled, + no_gap_seqs, + ) + chosen = self.filtered_domain( + [("state", "=", "posted"), ("entry_number", "=", False)] + ) + for move in chosen.sorted(lambda one: (one.date, one.name, one.id)): + move.entry_number = move.journal_id.entry_number_sequence_id._next( + move.date + ) + if chosen: + _logger.info("Added entry_number to %r", chosen) diff --git a/account_journal_general_sequence/models/account_move_line.py b/account_journal_general_sequence/models/account_move_line.py new file mode 100644 index 00000000..e60e321c --- /dev/null +++ b/account_journal_general_sequence/models/account_move_line.py @@ -0,0 +1,10 @@ +# Copyright 2022 Moduon +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). + +from odoo import fields, models + + +class AccountMoveLine(models.Model): + _inherit = "account.move.line" + + entry_number = fields.Char(related="move_id.entry_number") diff --git a/account_journal_general_sequence/readme/CONFIGURE.rst b/account_journal_general_sequence/readme/CONFIGURE.rst new file mode 100644 index 00000000..8f532758 --- /dev/null +++ b/account_journal_general_sequence/readme/CONFIGURE.rst @@ -0,0 +1,13 @@ +To configure journal sequences: + +#. Have full accounting permissions. +#. Go to *Invoicing > Configuration > Accounting > Journals*. +#. Select or create the journal you want to configure. +#. Use *Account entry number sequence* to configure a sequence. + +Note that: + +* Various journals can share the same sequence. +* All journals get a shared default sequence, per company. +* Entry numbers must be unique per company. So, if you use different sequences + per journal, make sure they don't produce colliding results. diff --git a/account_journal_general_sequence/readme/CONTRIBUTORS.rst b/account_journal_general_sequence/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..958ce345 --- /dev/null +++ b/account_journal_general_sequence/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Jairo Llopis (`Moduon `__) +* Rafael Blasco (`Moduon `__) diff --git a/account_journal_general_sequence/readme/DESCRIPTION.rst b/account_journal_general_sequence/readme/DESCRIPTION.rst new file mode 100644 index 00000000..ce756023 --- /dev/null +++ b/account_journal_general_sequence/readme/DESCRIPTION.rst @@ -0,0 +1,5 @@ +This module extends the functionality of account to support sequenced entry +numbers and to allow you to renumber them if needed, on demand. + +It adds a new field called *Entry number*. This is independent from the *Number* +that Odoo adds by default, and has different purpose. diff --git a/account_journal_general_sequence/readme/USAGE.rst b/account_journal_general_sequence/readme/USAGE.rst new file mode 100644 index 00000000..3b6cbe10 --- /dev/null +++ b/account_journal_general_sequence/readme/USAGE.rst @@ -0,0 +1,22 @@ +To see journal entry numbers: + +#. Go to *Invoicing > Accounting > Miscellaneous > Journal Entries*. +#. Notice the new field *Entry Number*. Only posted moves get an entry number. + +Note that: + +* You can use that new field in quick searches. +* You can see it also in *Invoicing > Accounting > Miscellaneous > Journal Items*. + +To renumber journal entries: + +#. Have full accounting permissions. +#. Have *Accounting / Invoicing / Billing Administrator* permissions. +#. Go to *Invoicing > Accounting > Actions > Renumber journal entries*. +#. Configure those parameters. +#. Click on *Renumber*. + +Note that: + +* You will only be able to select sequences related to journals. +* A sequence usually affects various journals, if not all. diff --git a/account_journal_general_sequence/security/ir.model.access.csv b/account_journal_general_sequence/security/ir.model.access.csv new file mode 100644 index 00000000..70a86e48 --- /dev/null +++ b/account_journal_general_sequence/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_account_move_renumber_wizard,access_account_move_renumber_wizard,model_account_move_renumber_wizard,account.group_account_manager,1,1,1,1 diff --git a/account_journal_general_sequence/static/description/icon.png b/account_journal_general_sequence/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..553d700b8b6554cbe6b3b8c7e04430dc85cadcaa GIT binary patch literal 28942 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 + + + + + +General sequence in account journals + + + +
+

General sequence in account journals

+ + +

Beta License: LGPL-3 OCA/account-financial-tools Translate me on Weblate Try me on Runbot

+

This module extends the functionality of account to support sequenced entry +numbers and to allow you to renumber them if needed, on demand.

+

It adds a new field called Entry number. This is independent from the Number +that Odoo adds by default, and has different purpose.

+

Table of contents

+ +
+

Configuration

+

To configure journal sequences:

+
    +
  1. Have full accounting permissions.
  2. +
  3. Go to Invoicing > Configuration > Accounting > Journals.
  4. +
  5. Select or create the journal you want to configure.
  6. +
  7. Use Account entry number sequence to configure a sequence.
  8. +
+

Note that:

+
    +
  • Various journals can share the same sequence.
  • +
  • All journals get a shared default sequence, per company.
  • +
  • Entry numbers must be unique per company. So, if you use different sequences +per journal, make sure they don’t produce colliding results.
  • +
+
+
+

Usage

+

To see journal entry numbers:

+
    +
  1. Go to Invoicing > Accounting > Miscellaneous > Journal Entries.
  2. +
  3. Notice the new field Entry Number. Only posted moves get an entry number.
  4. +
+

Note that:

+
    +
  • You can use that new field in quick searches.
  • +
  • You can see it also in Invoicing > Accounting > Miscellaneous > Journal Items.
  • +
+

To renumber journal entries:

+
    +
  1. Have full accounting permissions.
  2. +
  3. Have Accounting / Invoicing / Billing Administrator permissions.
  4. +
  5. Go to Invoicing > Accounting > Actions > Renumber journal entries.
  6. +
  7. Configure those parameters.
  8. +
  9. Click on Renumber.
  10. +
+

Note that:

+
    +
  • You will only be able to select sequences related to journals.
  • +
  • A sequence usually affects various journals, if not all.
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Moduon
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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.

+

This module is part of the OCA/account-financial-tools project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/account_journal_general_sequence/tests/__init__.py b/account_journal_general_sequence/tests/__init__.py new file mode 100644 index 00000000..60360ad2 --- /dev/null +++ b/account_journal_general_sequence/tests/__init__.py @@ -0,0 +1 @@ +from . import test_numbering diff --git a/account_journal_general_sequence/tests/test_numbering.py b/account_journal_general_sequence/tests/test_numbering.py new file mode 100644 index 00000000..001a8d3a --- /dev/null +++ b/account_journal_general_sequence/tests/test_numbering.py @@ -0,0 +1,46 @@ +# Copyright 2022 Moduon +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). +from freezegun import freeze_time + +from odoo.tests.common import Form, tagged +from odoo.tools import mute_logger + +from odoo.addons.account.tests.common import TestAccountReconciliationCommon + + +@freeze_time("2022-05-11", tick=True) +@tagged("post_install", "-at_install") +class RenumberCase(TestAccountReconciliationCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + + def test_invoice_gets_entry_number(self): + # Draft invoice without entry number + invoice = self._create_invoice() + self.assertFalse(invoice.entry_number) + # Gets one once posted + invoice.action_post() + self.assertTrue(invoice.entry_number.startswith("2022/")) + # Lost number when canceled + with mute_logger( + "odoo.addons.account_journal_general_sequence.models.account_move" + ): + invoice.button_cancel() + self.assertFalse(invoice.entry_number) + + def test_renumber(self): + # Post invoices in wrong order + new_invoice = self._create_invoice( + date_invoice="2022-05-10", auto_validate=True + ) + old_invoice = self._create_invoice( + date_invoice="2022-04-30", auto_validate=True + ) + self.assertLess(new_invoice.entry_number, old_invoice.entry_number) + # Fix entry number order with wizard; default values are OK + wiz_f = Form(self.env["account.move.renumber.wizard"]) + self.assertEqual(len(wiz_f.available_sequence_ids), 1) + wiz = wiz_f.save() + wiz.action_renumber() + self.assertGreater(new_invoice.entry_number, old_invoice.entry_number) diff --git a/account_journal_general_sequence/views/account_journal.xml b/account_journal_general_sequence/views/account_journal.xml new file mode 100644 index 00000000..13f8e690 --- /dev/null +++ b/account_journal_general_sequence/views/account_journal.xml @@ -0,0 +1,28 @@ + + + + + + Add move number sequence to tree + account.journal + + + + + + + + + + Add move number sequence to form + account.journal + + + + + + + + + diff --git a/account_journal_general_sequence/views/account_move.xml b/account_journal_general_sequence/views/account_move.xml new file mode 100644 index 00000000..30151056 --- /dev/null +++ b/account_journal_general_sequence/views/account_move.xml @@ -0,0 +1,42 @@ + + + + + + Add move number to tree + account.move + + + + + + + + + + Add move number to form + account.move + + + + + + + + + + + + + Add move number to search + account.move + + + + + + + + + diff --git a/account_journal_general_sequence/views/account_move_line.xml b/account_journal_general_sequence/views/account_move_line.xml new file mode 100644 index 00000000..776313cc --- /dev/null +++ b/account_journal_general_sequence/views/account_move_line.xml @@ -0,0 +1,39 @@ + + + + + + Add move number to tree + account.move.line + + + + + + + + + + Add move number to form + account.move.line + + + + + + + + + + Add move number to search + account.move.line + + + + + + + + + diff --git a/account_journal_general_sequence/wizards/__init__.py b/account_journal_general_sequence/wizards/__init__.py new file mode 100644 index 00000000..08f746fc --- /dev/null +++ b/account_journal_general_sequence/wizards/__init__.py @@ -0,0 +1 @@ +from . import account_move_renumber_wizard diff --git a/account_journal_general_sequence/wizards/account_move_renumber_wizard.py b/account_journal_general_sequence/wizards/account_move_renumber_wizard.py new file mode 100644 index 00000000..e8fcc7bc --- /dev/null +++ b/account_journal_general_sequence/wizards/account_move_renumber_wizard.py @@ -0,0 +1,82 @@ +# Copyright 2022 Moduon +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). + +from datetime import date + +from odoo import _, api, exceptions, fields, models + + +class AccountMoveRenumberWizard(models.TransientModel): + _name = "account.move.renumber.wizard" + _description = "Account move entry renumbering wizard" + + starting_date = fields.Date( + required=True, + default=lambda self: self._default_starting_date(), + help="Renumber account moves starting this day.", + ) + starting_number = fields.Integer( + default=1, help="Reset sequence to this number before starting." + ) + available_sequence_ids = fields.Many2many( + comodel_name="ir.sequence", + string="Available sequences", + default=lambda self: self._default_available_sequence_ids(), + ) + sequence_id = fields.Many2one( + comodel_name="ir.sequence", + string="Sequence", + required=True, + default=lambda self: self._default_entry_number_sequence(), + domain="[('id', 'in', available_sequence_ids)]", + help="Sequence to use for renumbering. Affects all journals that use this sequence.", + ) + + @api.model + def _default_starting_date(self): + """Start by default on day 1 of current year.""" + return date(date.today().year, 1, 1) + + @api.model + def _default_entry_number_sequence(self): + """Get default sequence if it exists.""" + return self.env["ir.sequence"].search( + [("code", "=", "account_journal_general_sequence.default")] + ) + + @api.model + def _default_available_sequence_ids(self): + """Let view display only journal-related sequences.""" + return self.env["account.journal"].search([]).mapped("entry_number_sequence_id") + + def action_renumber(self): + """Renumber moves. + + Makes sure moves exist. Sorts them. Resets sequences. Renumbers them. + """ + # Find posted moves that match wizard criteria + moves = self.env["account.move"].search( + [ + ("state", "=", "posted"), + ("date", ">=", self.starting_date), + ("journal_id.entry_number_sequence_id", "=", self.sequence_id.id), + ], + order="date, id", + ) + if not moves: + raise exceptions.UserError(_("No account moves found.")) + # Reset sequence + future_ranges = self.env["ir.sequence.date_range"].search( + [ + ("date_from", ">", self.starting_date), + ("sequence_id", "=", self.sequence_id.id), + ] + ) + future_ranges.unlink() + current_range = self.sequence_id._get_current_sequence(self.starting_date) + current_range.number_next_actual = self.starting_number + self.sequence_id.number_next_actual = self.starting_number + # Renumber the moves + moves.entry_number = False + moves.flush(["entry_number"]) + moves._compute_entry_number() diff --git a/account_journal_general_sequence/wizards/account_move_renumber_wizard_views.xml b/account_journal_general_sequence/wizards/account_move_renumber_wizard_views.xml new file mode 100644 index 00000000..ccb6f40e --- /dev/null +++ b/account_journal_general_sequence/wizards/account_move_renumber_wizard_views.xml @@ -0,0 +1,46 @@ + + + + + + Renumber form + account.move.renumber.wizard + +
+ + + + + + + + +
+
+
+
+
+ + + Renumber journal entries + account.move.renumber.wizard + form + new + + + + +