commit
358925d8ce
@ -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)",
|
||||
|
@ -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
|
@ -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")]
|
||||
for one in self:
|
||||
sequence = self.env["ir.sequence"].search(
|
||||
[
|
||||
("code", "=", "account_journal_general_sequence.default"),
|
||||
("company_id", "=", one.company_id.id),
|
||||
]
|
||||
)
|
||||
if result:
|
||||
return result
|
||||
if not sequence:
|
||||
_logger.info("Creating default sequence for account move numbers")
|
||||
result = self.env["ir.sequence"].create(
|
||||
sequence = self.env["ir.sequence"].create(
|
||||
{
|
||||
"name": _("Account entry default numbering"),
|
||||
"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": 10,
|
||||
"padding": 8,
|
||||
"use_date_range": True,
|
||||
}
|
||||
)
|
||||
return result
|
||||
one.entry_number_sequence_id = sequence
|
||||
|
@ -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")
|
||||
|
@ -20,7 +20,11 @@
|
||||
<field name="inherit_id" ref="account.view_account_journal_form" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="refund_sequence" position="before">
|
||||
<field name="entry_number_sequence_id" />
|
||||
<field name="entry_number_sequence_id_name" invisible="1" />
|
||||
<field
|
||||
name="entry_number_sequence_id"
|
||||
attrs="{'readonly':[('entry_number_sequence_id_name','=','account_journal_general_sequence.default')]}"
|
||||
/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user