diff --git a/account_journal_general_sequence/__manifest__.py b/account_journal_general_sequence/__manifest__.py index 39bfb62c..6a4b99d8 100644 --- a/account_journal_general_sequence/__manifest__.py +++ b/account_journal_general_sequence/__manifest__.py @@ -3,7 +3,7 @@ { "name": "General sequence in account journals", "summary": "Add configurable sequence to account moves, per journal", - "version": "16.0.1.1.0", + "version": "16.0.2.0.0", "category": "Accounting/Accounting", "website": "https://github.com/OCA/account-financial-tools", "author": "Moduon, Odoo Community Association (OCA)", diff --git a/account_journal_general_sequence/migrations/16.0.2.0.0/post-migration.py b/account_journal_general_sequence/migrations/16.0.2.0.0/post-migration.py new file mode 100644 index 00000000..a17d1baf --- /dev/null +++ b/account_journal_general_sequence/migrations/16.0.2.0.0/post-migration.py @@ -0,0 +1,44 @@ +# Copyright 2023 Moduon Team S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl-3.0) +from odoo import SUPERUSER_ID, api, fields + + +def migrate(cr, version): + """One journal sequence per company.""" + env = api.Environment(cr, SUPERUSER_ID, {}) + journals = env["account.journal"].search( + [ + ( + "entry_number_sequence_id.code", + "=", + "account_journal_general_sequence.default", + ) + ] + ) + for journal in journals: + if journal.company_id != journal.entry_number_sequence_id.company_id: + new_sequence = env["ir.sequence"].search( + [ + ("code", "=", "account_journal_general_sequence.default"), + ("company_id", "=", journal.company_id.id), + ] + ) or journal.entry_number_sequence_id.copy( + { + "company_id": journal.company_id.id, + "name": "{} ({})".format( + journal.entry_number_sequence_id.name, journal.company_id.name + ), + "number_next_actual": journal.entry_number_sequence_id.number_next_actual, + "date_range_ids": [ + fields.Command.create( + { + "date_from": rng.date_from, + "date_to": rng.date_to, + "number_next_actual": rng.number_next_actual, + } + ) + for rng in journal.entry_number_sequence_id.date_range_ids + ], + } + ) + journal.entry_number_sequence_id = new_sequence diff --git a/account_journal_general_sequence/models/account_journal.py b/account_journal_general_sequence/models/account_journal.py index c36eb153..6a6feb42 100644 --- a/account_journal_general_sequence/models/account_journal.py +++ b/account_journal_general_sequence/models/account_journal.py @@ -2,7 +2,7 @@ # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). import logging -from odoo import _, fields, models +from odoo import _, api, fields, models _logger = logging.getLogger(__name__) @@ -13,27 +13,40 @@ class AccountJournal(models.Model): entry_number_sequence_id = fields.Many2one( comodel_name="ir.sequence", string="Account entry number sequence", - default=lambda self: self._default_entry_number_sequence(), + compute="_compute_entry_number_sequence", + domain="[('company_id', '=', company_id)]", + check_company=True, + readonly=False, + store=True, copy=False, help="Sequence used for account entry numbering.", ) + entry_number_sequence_id_name = fields.Char(related="entry_number_sequence_id.code") - def _default_entry_number_sequence(self): + @api.depends("company_id") + def _compute_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 + for one in self: + sequence = self.env["ir.sequence"].search( + [ + ("code", "=", "account_journal_general_sequence.default"), + ("company_id", "=", one.company_id.id), + ] + ) + if not sequence: + _logger.info("Creating default sequence for account move numbers") + sequence = self.env["ir.sequence"].create( + { + "name": _( + "Account entry default numbering (%s)", + one.company_id.name, + ), + "code": "account_journal_general_sequence.default", + "company_id": one.company_id.id, + "implementation": "no_gap", + "prefix": "%(range_year)s/", + "padding": 8, + "use_date_range": True, + } + ) + one.entry_number_sequence_id = sequence diff --git a/account_journal_general_sequence/tests/test_numbering.py b/account_journal_general_sequence/tests/test_numbering.py index b8c9f86b..8087a499 100644 --- a/account_journal_general_sequence/tests/test_numbering.py +++ b/account_journal_general_sequence/tests/test_numbering.py @@ -2,6 +2,7 @@ # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). from freezegun import freeze_time +from odoo.fields import Command from odoo.tests.common import Form, new_test_user, tagged, users from odoo.tools import mute_logger @@ -14,11 +15,18 @@ class RenumberCase(TestAccountReconciliationCommon): @classmethod def setUpClass(cls): super().setUpClass() + companies = cls.company_data["company"] | cls.company_data_2["company"] cls.invoicer = new_test_user( - cls.env, "test_invoicer", "account.group_account_invoice" + cls.env, + "test_invoicer", + "account.group_account_invoice", + company_ids=[Command.set(companies.ids)], ) cls.manager = new_test_user( - cls.env, "test_manager", "account.group_account_manager" + cls.env, + "test_manager", + "account.group_account_manager", + company_ids=[Command.set(companies.ids)], ) @users("test_invoicer") @@ -42,18 +50,22 @@ class RenumberCase(TestAccountReconciliationCommon): next_year_invoice = self._create_invoice( date_invoice="2023-12-31", auto_validate=True ) - next_year_invoice.flush(["entry_number"], next_year_invoice) + next_year_invoice.flush_recordset(["entry_number"]) new_invoice = self._create_invoice( date_invoice="2022-05-10", auto_validate=True ) - new_invoice.flush(["entry_number"], new_invoice) + new_invoice.flush_recordset(["entry_number"]) old_invoice = self._create_invoice( date_invoice="2022-04-30", auto_validate=True ) - old_invoice.flush(["entry_number"], old_invoice) + old_invoice.flush_recordset(["entry_number"]) 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"]) + wiz_f = Form( + self.env["account.move.renumber.wizard"].with_company( + self.company_data["company"] + ) + ) self.assertEqual(len(wiz_f.available_sequence_ids), 1) wiz = wiz_f.save() wiz.action_renumber() @@ -67,10 +79,10 @@ class RenumberCase(TestAccountReconciliationCommon): wiz_f = Form(self.env["account.move.renumber.wizard"]) wiz = wiz_f.save() wiz.action_renumber() - self.assertEqual(opening_invoice.entry_number, "2022/0000000001") - self.assertEqual(old_invoice.entry_number, "2022/0000000002") - self.assertEqual(new_invoice.entry_number, "2022/0000000003") - self.assertEqual(next_year_invoice.entry_number, "2023/0000000001") + self.assertEqual(opening_invoice.entry_number, "2022/00000001") + self.assertEqual(old_invoice.entry_number, "2022/00000002") + self.assertEqual(new_invoice.entry_number, "2022/00000003") + self.assertEqual(next_year_invoice.entry_number, "2023/00000001") @users("test_invoicer") def test_install_no_entry_number(self): @@ -88,3 +100,21 @@ class RenumberCase(TestAccountReconciliationCommon): invoice.action_post() # Ensure there's no entry number self.assertFalse(invoice.entry_number) + + @users("test_invoicer") + def test_new_company_journal(self): + # Create new companies + cmp1 = self.company_data["company"] + cmp2 = self.company_data_2["company"] + # Create a new invoice for each company + self.env = self.env( + context=dict(self.env.context, allowed_company_ids=cmp1.ids) + ) + invoice1 = self.create_invoice() + self.env = self.env( + context=dict(self.env.context, allowed_company_ids=cmp2.ids) + ) + invoice2 = self.create_invoice() + # Each company has a different sequence, so the entry number should be the same + self.assertEqual(invoice1.entry_number, "2022/00000001") + self.assertEqual(invoice2.entry_number, "2022/00000001") diff --git a/account_journal_general_sequence/views/account_journal.xml b/account_journal_general_sequence/views/account_journal.xml index 13f8e690..d38116b8 100644 --- a/account_journal_general_sequence/views/account_journal.xml +++ b/account_journal_general_sequence/views/account_journal.xml @@ -20,7 +20,11 @@ - + + diff --git a/account_journal_general_sequence/wizards/account_move_renumber_wizard.py b/account_journal_general_sequence/wizards/account_move_renumber_wizard.py index ff261c41..ac9cacdd 100644 --- a/account_journal_general_sequence/wizards/account_move_renumber_wizard.py +++ b/account_journal_general_sequence/wizards/account_move_renumber_wizard.py @@ -41,13 +41,21 @@ class AccountMoveRenumberWizard(models.TransientModel): def _default_entry_number_sequence(self): """Get default sequence if it exists.""" return self.env["ir.sequence"].search( - [("code", "=", "account_journal_general_sequence.default")] + [ + "&", + ("code", "=", "account_journal_general_sequence.default"), + ("company_id", "in", self.env.companies.ids), + ] ) @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") + return ( + self.env["account.journal"] + .search([("company_id", "in", self.env.companies.ids)]) + .mapped("entry_number_sequence_id") + ) def action_renumber(self): """Renumber moves.