sequence is now required on journals
Add post-install script to create a sequence for all existing journals Update README accordingly
This commit is contained in:
parent
42def7f06f
commit
4aeaa9ca51
@ -1 +1,2 @@
|
|||||||
|
from .post_install import create_journal_sequences
|
||||||
from . import models
|
from . import models
|
||||||
|
@ -14,7 +14,9 @@
|
|||||||
"depends": ["account"],
|
"depends": ["account"],
|
||||||
"data": [
|
"data": [
|
||||||
"views/account_journal.xml",
|
"views/account_journal.xml",
|
||||||
|
"views/account_move.xml",
|
||||||
"security/ir.model.access.csv",
|
"security/ir.model.access.csv",
|
||||||
],
|
],
|
||||||
|
"post_init_hook": "create_journal_sequences",
|
||||||
"installable": True,
|
"installable": True,
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ class AccountJournal(models.Model):
|
|||||||
"ir.sequence",
|
"ir.sequence",
|
||||||
string="Entry Sequence",
|
string="Entry Sequence",
|
||||||
copy=False,
|
copy=False,
|
||||||
|
required=True,
|
||||||
check_company=True,
|
check_company=True,
|
||||||
domain="[('company_id', '=', company_id)]",
|
domain="[('company_id', '=', company_id)]",
|
||||||
help="This sequence will be used to generate the journal entry number.",
|
help="This sequence will be used to generate the journal entry number.",
|
||||||
|
@ -2,31 +2,44 @@
|
|||||||
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from odoo import models
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
class AccountMove(models.Model):
|
class AccountMove(models.Model):
|
||||||
_inherit = "account.move"
|
_inherit = "account.move"
|
||||||
|
|
||||||
def _compute_name(self):
|
name = fields.Char(compute="_compute_name_by_sequence")
|
||||||
for move in self.filtered(
|
# highest_name, sequence_prefix and sequence_number are not needed any more
|
||||||
lambda m: (m.name == "/" or not m.name)
|
# -> compute=False to improve perf
|
||||||
and m.state == "posted"
|
highest_name = fields.Char(compute=False)
|
||||||
and m.journal_id
|
sequence_prefix = fields.Char(compute=False)
|
||||||
and m.journal_id.sequence_id
|
sequence_number = fields.Integer(compute=False)
|
||||||
):
|
|
||||||
|
@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 (
|
if (
|
||||||
move.move_type in ("out_refund", "in_refund")
|
move.state == "posted"
|
||||||
and move.journal_id.type in ("sale", "purchase")
|
and (not move.name or move.name == "/")
|
||||||
and move.journal_id.refund_sequence
|
and move.journal_id
|
||||||
and move.journal_id.refund_sequence_id
|
and move.journal_id.sequence_id
|
||||||
):
|
):
|
||||||
seq = move.journal_id.refund_sequence_id
|
if (
|
||||||
else:
|
move.move_type in ("out_refund", "in_refund")
|
||||||
seq = move.journal_id.sequence_id
|
and move.journal_id.type in ("sale", "purchase")
|
||||||
move.name = seq.next_by_id(sequence_date=move.date)
|
and move.journal_id.refund_sequence
|
||||||
super()._compute_name()
|
and move.journal_id.refund_sequence_id
|
||||||
for move in self.filtered(
|
):
|
||||||
lambda m: m.name and m.name != "/" and m.state != "posted"
|
seq = move.journal_id.refund_sequence_id
|
||||||
):
|
else:
|
||||||
move.name = "/"
|
seq = move.journal_id.sequence_id
|
||||||
|
name = seq.next_by_id(sequence_date=move.date)
|
||||||
|
move.name = name
|
||||||
|
|
||||||
|
# We must by-pass this constraint of sequence.mixin
|
||||||
|
def _constrains_date_sequence(self):
|
||||||
|
return True
|
||||||
|
25
account_move_name_sequence/post_install.py
Normal file
25
account_move_name_sequence/post_install.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# 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 SUPERUSER_ID, api
|
||||||
|
|
||||||
|
|
||||||
|
def create_journal_sequences(cr, registry):
|
||||||
|
with api.Environment.manage():
|
||||||
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
|
journals = env["account.journal"].with_context(active_test=False).search([])
|
||||||
|
for journal in journals:
|
||||||
|
vals = {}
|
||||||
|
journal_vals = {
|
||||||
|
"code": journal.code,
|
||||||
|
"name": journal.name,
|
||||||
|
"company_id": journal.company_id.id,
|
||||||
|
}
|
||||||
|
seq_vals = journal._prepare_sequence(journal_vals)
|
||||||
|
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)
|
||||||
|
vals["refund_sequence_id"] = env["ir.sequence"].create(rseq_vals).id
|
||||||
|
journal.write(vals)
|
||||||
|
return
|
@ -1,3 +1,5 @@
|
|||||||
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 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 additionnal option to have another sequence dedicated to refunds.
|
On sale and purchase journals, you have an additionnal 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.
|
||||||
|
@ -78,6 +78,9 @@ class TestAccountMoveNameSequence(TransactionCase):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
self.assertEqual(drange_count, 1)
|
self.assertEqual(drange_count, 1)
|
||||||
|
move.button_draft()
|
||||||
|
move.action_post()
|
||||||
|
self.assertEqual(move.name, move_name)
|
||||||
|
|
||||||
def test_in_refund(self):
|
def test_in_refund(self):
|
||||||
in_refund_invoice = self.env["account.move"].create(
|
in_refund_invoice = self.env["account.move"].create(
|
||||||
@ -105,3 +108,6 @@ class TestAccountMoveNameSequence(TransactionCase):
|
|||||||
move_name = "%s%s" % (seq.prefix, "1".zfill(seq.padding))
|
move_name = "%s%s" % (seq.prefix, "1".zfill(seq.padding))
|
||||||
move_name = move_name.replace("%(range_year)s", str(self.date.year))
|
move_name = move_name.replace("%(range_year)s", str(self.date.year))
|
||||||
self.assertEqual(in_refund_invoice.name, move_name)
|
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)
|
||||||
|
@ -6,29 +6,25 @@
|
|||||||
-->
|
-->
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<record id="view_account_journal_form" model="ir.ui.view">
|
<record id="view_account_journal_form" model="ir.ui.view">
|
||||||
<field name="model">account.journal</field>
|
<field name="model">account.journal</field>
|
||||||
<field
|
<field name="inherit_id" ref="account.view_account_journal_form" />
|
||||||
name="inherit_id"
|
<field name="arch" type="xml">
|
||||||
ref="account.view_account_journal_form"
|
<field name="refund_sequence" position="before">
|
||||||
/>
|
<field
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="currency_id" position="after">
|
|
||||||
<field
|
|
||||||
name="sequence_id"
|
name="sequence_id"
|
||||||
|
required="0"
|
||||||
context="{'default_name': name, 'default_company_id': company_id, 'default_implementation': 'no_gap', 'default_padding': 4, 'default_use_date_range': True, 'default_prefix': code + '/%%(range_year)s/'}"
|
context="{'default_name': name, 'default_company_id': company_id, 'default_implementation': 'no_gap', 'default_padding': 4, 'default_use_date_range': True, 'default_prefix': code + '/%%(range_year)s/'}"
|
||||||
/>
|
/>
|
||||||
<field
|
</field>
|
||||||
name="refund_sequence"
|
<field name="refund_sequence" position="after">
|
||||||
attrs="{'invisible': [('type', 'not in', ('sale', 'purchase'))]}"
|
<field
|
||||||
/>
|
|
||||||
<field
|
|
||||||
name="refund_sequence_id"
|
name="refund_sequence_id"
|
||||||
attrs="{'invisible': ['|', ('type', 'not in', ('sale', 'purchase')), ('refund_sequence', '=', False)]}"
|
attrs="{'invisible': ['|', ('type', 'not in', ('sale', 'purchase')), ('refund_sequence', '=', False)]}"
|
||||||
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 + '/%%(range_year)s/'}"
|
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 + '/%%(range_year)s/'}"
|
||||||
/>
|
/>
|
||||||
</field>
|
</field>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
33
account_move_name_sequence/views/account_move.xml
Normal file
33
account_move_name_sequence/views/account_move.xml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?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="//div[hasclass('oe_title')]/h1[hasclass('mt0')]"
|
||||||
|
position="attributes"
|
||||||
|
>
|
||||||
|
<attribute
|
||||||
|
name="attrs"
|
||||||
|
>{'invisible': [('name', '=', '/')]}</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath
|
||||||
|
expr="//div[hasclass('oe_title')]//field[@name='name']"
|
||||||
|
position="attributes"
|
||||||
|
>
|
||||||
|
<attribute name="attrs">{'readonly': 1}</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='highest_name']/.." position="attributes">
|
||||||
|
<attribute name="attrs">{'invisible': 1}</attribute>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
Loading…
x
Reference in New Issue
Block a user