From d914e60b2c85b0fb373b85bc8be20f88e38efe99 Mon Sep 17 00:00:00 2001 From: aliya Date: Tue, 22 Aug 2023 11:49:59 +0000 Subject: [PATCH] [FIX] account: fill the journal code for general journal type Currently, when a journal is created during import, we just use the journal name as the code. However, that gets truncated to 5 characters on create, which can conflict with existing journals. The import should be as flexible as possible, as it's better to let users import and fix things after the import rather than debug the import file. - If there is a journal with a shortcode that matches the truncated name of the new journal, just use the existing journal (enterprise PR) - If the shortcode conflict arises within the imported file (so the journals don't exist before the import), keep track of the codes in `vals_list`. The `get_next_bank_cash_default_code` is modified to avoid duplicating code. See also odoo/enterprise#46101 closes odoo/odoo#135051 X-original-commit: 1161307dc59d0c98eb1352fd80d95da7dc98e700 Related: odoo/enterprise#47242 Signed-off-by: Laurent Smet (las) Signed-off-by: Aliya Tastemirova (alta) --- addons/account/i18n/account.pot | 9 +++++++ addons/account/models/account_journal.py | 27 +++++++++++++------- addons/account/tests/test_account_journal.py | 8 ++++++ 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/addons/account/i18n/account.pot b/addons/account/i18n/account.pot index eb1bb9c740a..c6c6a4e3f18 100644 --- a/addons/account/i18n/account.pot +++ b/addons/account/i18n/account.pot @@ -3111,6 +3111,15 @@ msgstr "" msgid "Cannot generate an unused account code." msgstr "" +#. module: account +#. odoo-python +#: code:addons/account/models/account_journal.py:0 +#, python-format +msgid "" +"Cannot generate an unused journal code. Please change the name for journal " +"%s." +msgstr "" + #. module: account #. odoo-python #: code:addons/account/models/account_report.py:0 diff --git a/addons/account/models/account_journal.py b/addons/account/models/account_journal.py index 906cc281946..cdd5656d0d2 100644 --- a/addons/account/models/account_journal.py +++ b/addons/account/models/account_journal.py @@ -596,16 +596,18 @@ class AccountJournal(models.Model): return self.env['mail.alias']._sanitize_alias_name(alias_name) @api.model - def get_next_bank_cash_default_code(self, journal_type, company, cache=None): - journal_code_base = (journal_type == 'cash' and 'CSH' or 'BNK') + def get_next_bank_cash_default_code(self, journal_type, company, cache=None, protected_codes=False): + prefix_map = {'cash': 'CSH', 'general': 'GEN', 'bank': 'BNK'} + journal_code_base = prefix_map.get(journal_type) existing_codes = set(self.env['account.journal'].with_context(active_test=False).search([ *self.env['account.journal']._check_company_domain(company), ('code', 'like', journal_code_base + '%'), ]).mapped('code') + (cache or [])) + for num in range(1, 100): # journal_code has a maximal size of 5, hence we can enforce the boundary num < 100 journal_code = journal_code_base + str(num) - if journal_code not in existing_codes: + if journal_code not in existing_codes and (protected_codes and journal_code not in protected_codes or not protected_codes): return journal_code @api.model @@ -619,12 +621,11 @@ class AccountJournal(models.Model): } @api.model - def _fill_missing_values(self, vals): + def _fill_missing_values(self, vals, protected_codes=False): journal_type = vals.get('type') - if 'import_file' in self.env.context and not journal_type: - vals['type'] = 'general' - if not vals.get('code'): - vals['code'] = vals.get('name') + is_import = 'import_file' in self.env.context + if is_import and not journal_type: + vals['type'] = journal_type = 'general' # 'type' field is required. if not journal_type: @@ -664,6 +665,12 @@ class AccountJournal(models.Model): if journal_type in ('cash', 'bank') and not has_loss_account: vals['loss_account_id'] = company.default_cash_difference_expense_account_id.id + if is_import and not vals.get('code'): + code = vals['name'][:5] + vals['code'] = code if not protected_codes or code not in protected_codes else self.get_next_bank_cash_default_code(journal_type, company, protected_codes) + if not vals['code']: + raise UserError(_("Cannot generate an unused journal code. Please change the name for journal %s.", vals['name'])) + # === Fill missing refund_sequence === if 'refund_sequence' not in vals: vals['refund_sequence'] = vals['type'] in ('sale', 'purchase') @@ -677,7 +684,9 @@ class AccountJournal(models.Model): @api.model_create_multi def create(self, vals_list): for vals in vals_list: - self._fill_missing_values(vals) + # have to keep track of new journal codes when importing + codes = [vals['code'] for vals in vals_list if 'code' in vals] if 'import_file' in self.env.context else False + self._fill_missing_values(vals, protected_codes=codes) journals = super(AccountJournal, self.with_context(mail_create_nolog=True)).create(vals_list) diff --git a/addons/account/tests/test_account_journal.py b/addons/account/tests/test_account_journal.py index c3d7a35b008..6e68c2c1e1a 100644 --- a/addons/account/tests/test_account_journal.py +++ b/addons/account/tests/test_account_journal.py @@ -140,6 +140,14 @@ class TestAccountJournal(AccountTestInvoicingCommon): self.assertFalse(second_method.exists()) + def test_account_journal_duplicates(self): + new_journals = self.env["account.journal"].with_context(import_file=True).create([ + {"name": "OD_BLABLA"}, + {"name": "OD_BLABLU"}, + ]) + + self.assertEqual(sorted(new_journals.mapped("code")), ["GEN1", "OD_BL"], "The journals should be set correctly") + @tagged('post_install', '-at_install', 'mail_alias') class TestAccountJournalAlias(AccountTestInvoicingCommon, MailCommon):