[REF] account_move_name_sequence: Add number_next_actual and date range lines data based on current moves
This commit is contained in:
parent
4aeaa9ca51
commit
d1d6874aad
@ -1,5 +1,7 @@
|
|||||||
# Copyright 2021 Akretion France (http://www.akretion.com/)
|
# Copyright 2021 Akretion France (http://www.akretion.com/)
|
||||||
|
# Copyright 2022 Vauxoo (https://www.vauxoo.com/)
|
||||||
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# @author: Moisés López <moylop260@vauxoo.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).
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -8,7 +10,7 @@
|
|||||||
"category": "Accounting",
|
"category": "Accounting",
|
||||||
"license": "AGPL-3",
|
"license": "AGPL-3",
|
||||||
"summary": "Generate journal entry number from sequence",
|
"summary": "Generate journal entry number from sequence",
|
||||||
"author": "Akretion,Odoo Community Association (OCA)",
|
"author": "Akretion,Vauxoo,Odoo Community Association (OCA)",
|
||||||
"maintainers": ["alexis-via"],
|
"maintainers": ["alexis-via"],
|
||||||
"website": "https://github.com/OCA/account-financial-tools",
|
"website": "https://github.com/OCA/account-financial-tools",
|
||||||
"depends": ["account"],
|
"depends": ["account"],
|
||||||
|
@ -1,10 +1,18 @@
|
|||||||
# Copyright 2021 Akretion France (http://www.akretion.com/)
|
# Copyright 2021 Akretion France (http://www.akretion.com/)
|
||||||
|
# Copyright 2022 Vauxoo (https://www.vauxoo.com/)
|
||||||
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# @author: Moisés López <moylop260@vauxoo.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).
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
from odoo import _, api, fields, models
|
from odoo import _, api, fields, models
|
||||||
from odoo.exceptions import ValidationError
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class AccountJournal(models.Model):
|
class AccountJournal(models.Model):
|
||||||
_inherit = "account.journal"
|
_inherit = "account.journal"
|
||||||
@ -13,16 +21,10 @@ 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.",
|
||||||
)
|
)
|
||||||
refund_sequence = fields.Boolean(
|
|
||||||
string="Dedicated Credit Note Sequence",
|
|
||||||
default=True,
|
|
||||||
help="If enabled, you will be able to setup a sequence dedicated for refunds.",
|
|
||||||
)
|
|
||||||
refund_sequence_id = fields.Many2one(
|
refund_sequence_id = fields.Many2one(
|
||||||
"ir.sequence",
|
"ir.sequence",
|
||||||
string="Credit Note Entry Sequence",
|
string="Credit Note Entry Sequence",
|
||||||
@ -31,6 +33,8 @@ class AccountJournal(models.Model):
|
|||||||
domain="[('company_id', '=', company_id)]",
|
domain="[('company_id', '=', company_id)]",
|
||||||
help="This sequence will be used to generate the journal entry number for refunds.",
|
help="This sequence will be used to generate the journal entry number for refunds.",
|
||||||
)
|
)
|
||||||
|
# Redefine the default to True as <=v13.0
|
||||||
|
refund_sequence = fields.Boolean(default=True)
|
||||||
|
|
||||||
@api.constrains("refund_sequence_id", "sequence_id")
|
@api.constrains("refund_sequence_id", "sequence_id")
|
||||||
def _check_journal_sequence(self):
|
def _check_journal_sequence(self):
|
||||||
@ -95,3 +99,145 @@ class AccountJournal(models.Model):
|
|||||||
def _create_sequence(self, vals, refund=False):
|
def _create_sequence(self, vals, refund=False):
|
||||||
seq_vals = self._prepare_sequence(vals, refund=refund)
|
seq_vals = self._prepare_sequence(vals, refund=refund)
|
||||||
return self.env["ir.sequence"].sudo().create(seq_vals)
|
return self.env["ir.sequence"].sudo().create(seq_vals)
|
||||||
|
|
||||||
|
def _prepare_sequence_current_moves(self, refund=False):
|
||||||
|
"""Get sequence dict values the journal based on current moves"""
|
||||||
|
self.ensure_one()
|
||||||
|
move_domain = [
|
||||||
|
("journal_id", "=", self.id),
|
||||||
|
("name", "!=", "/"),
|
||||||
|
]
|
||||||
|
if self.refund_sequence:
|
||||||
|
# Based on original Odoo behavior
|
||||||
|
if refund:
|
||||||
|
move_domain.append(("move_type", "in", ("out_refund", "in_refund")))
|
||||||
|
else:
|
||||||
|
move_domain.append(("move_type", "not in", ("out_refund", "in_refund")))
|
||||||
|
last_move = self.env["account.move"].search(
|
||||||
|
move_domain, limit=1, order="id DESC"
|
||||||
|
)
|
||||||
|
msg_err = (
|
||||||
|
"Journal %s could not get sequence %s values based on current moves. "
|
||||||
|
"Using default values." % (self.id, refund and "refund" or "")
|
||||||
|
)
|
||||||
|
if not last_move:
|
||||||
|
_logger.warning("%s %s", msg_err, "No moves found")
|
||||||
|
return {}
|
||||||
|
try:
|
||||||
|
with self.env.cr.savepoint():
|
||||||
|
# get the current sequence values could be buggy to get
|
||||||
|
# But even we can use the default values
|
||||||
|
# or do manual changes instead of raising errors
|
||||||
|
last_sequence = last_move._get_last_sequence()
|
||||||
|
if not last_sequence:
|
||||||
|
last_sequence = (
|
||||||
|
last_move._get_last_sequence(relaxed=True)
|
||||||
|
or last_move._get_starting_sequence()
|
||||||
|
)
|
||||||
|
|
||||||
|
__, seq_format_values = last_move._get_sequence_format_param(
|
||||||
|
last_sequence
|
||||||
|
)
|
||||||
|
prefix1 = seq_format_values["prefix1"]
|
||||||
|
prefix = prefix1
|
||||||
|
if seq_format_values["year_length"] == 4:
|
||||||
|
prefix += "%(range_year)s"
|
||||||
|
elif seq_format_values["year_length"] == 2:
|
||||||
|
prefix += "%(range_y)s"
|
||||||
|
else:
|
||||||
|
# If there is not year so current values are valid
|
||||||
|
seq_vals = {
|
||||||
|
"padding": seq_format_values["seq_length"],
|
||||||
|
"suffix": seq_format_values["suffix"],
|
||||||
|
"prefix": prefix,
|
||||||
|
"date_range_ids": [],
|
||||||
|
"use_date_range": False,
|
||||||
|
"number_next_actual": seq_format_values["seq"] + 1,
|
||||||
|
}
|
||||||
|
return seq_vals
|
||||||
|
prefix2 = seq_format_values.get("prefix2") or ""
|
||||||
|
prefix += prefix2
|
||||||
|
month = seq_format_values.get("month") # It is 0 if only have year
|
||||||
|
if month:
|
||||||
|
prefix += "%(range_month)s"
|
||||||
|
prefix3 = seq_format_values.get("prefix3") or ""
|
||||||
|
where_name_value = "%s%s%s%s%s%%" % (
|
||||||
|
prefix1,
|
||||||
|
"_" * seq_format_values["year_length"],
|
||||||
|
prefix2,
|
||||||
|
"_" * bool(month) * 2,
|
||||||
|
prefix3,
|
||||||
|
)
|
||||||
|
prefixes = prefix1 + prefix2
|
||||||
|
select_year = (
|
||||||
|
"split_part(name, '%s', %d)" % (prefix2, prefixes.count(prefix2))
|
||||||
|
if prefix2
|
||||||
|
else "''"
|
||||||
|
)
|
||||||
|
prefixes += prefix3
|
||||||
|
select_month = (
|
||||||
|
"split_part(name, '%s', %d)" % (prefix3, prefixes.count(prefix3))
|
||||||
|
if prefix3
|
||||||
|
else "''"
|
||||||
|
)
|
||||||
|
select_max_number = (
|
||||||
|
"MAX(split_part(name, '%s', %d)::INTEGER) AS max_number"
|
||||||
|
% (prefixes[-1], prefixes.count(prefixes[-1]) + 1)
|
||||||
|
)
|
||||||
|
query = (
|
||||||
|
"SELECT %s, %s, %s FROM account_move "
|
||||||
|
"WHERE name LIKE %%s AND journal_id=%%s GROUP BY 1,2"
|
||||||
|
) % (
|
||||||
|
select_year,
|
||||||
|
select_month,
|
||||||
|
select_max_number,
|
||||||
|
)
|
||||||
|
# It is not using user input
|
||||||
|
# pylint: disable=sql-injection
|
||||||
|
self.env.cr.execute(query, (where_name_value, self.id))
|
||||||
|
res = self.env.cr.fetchall()
|
||||||
|
prefix += prefix3
|
||||||
|
seq_vals = {
|
||||||
|
"padding": seq_format_values["seq_length"],
|
||||||
|
"suffix": seq_format_values["suffix"],
|
||||||
|
"prefix": prefix,
|
||||||
|
"date_range_ids": [],
|
||||||
|
"use_date_range": True,
|
||||||
|
}
|
||||||
|
for year, month, max_number in res:
|
||||||
|
if not year and not month:
|
||||||
|
seq_vals.update(
|
||||||
|
{
|
||||||
|
"use_date_range": False,
|
||||||
|
"number_next_actual": max_number + 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
if len(year) == 2:
|
||||||
|
# Year >=50 will be considered as last century 1950
|
||||||
|
# Year <=49 will be considered as current century 2049
|
||||||
|
if int(year) >= 50:
|
||||||
|
year = "19" + year
|
||||||
|
else:
|
||||||
|
year = "20" + year
|
||||||
|
if month:
|
||||||
|
date_from = fields.Date.to_date("%s-%s-1" % (year, month))
|
||||||
|
date_to = date_from + relativedelta(day=31)
|
||||||
|
else:
|
||||||
|
date_from = fields.Date.to_date("%s-1-1" % year)
|
||||||
|
date_to = fields.Date.to_date("%s-12-31" % year)
|
||||||
|
seq_vals["date_range_ids"].append(
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
"date_from": date_from,
|
||||||
|
"date_to": date_to,
|
||||||
|
"number_next_actual": max_number + 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return seq_vals
|
||||||
|
except Exception as e:
|
||||||
|
_logger.warning("%s %s", msg_err, e)
|
||||||
|
return {}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
# Copyright 2021 Akretion France (http://www.akretion.com/)
|
# Copyright 2021 Akretion France (http://www.akretion.com/)
|
||||||
|
# Copyright 2022 Vauxoo (https://www.vauxoo.com/)
|
||||||
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# @author: Moisés López <moylop260@vauxoo.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 SUPERUSER_ID, api
|
from odoo import SUPERUSER_ID, api
|
||||||
@ -10,16 +12,17 @@ def create_journal_sequences(cr, registry):
|
|||||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
journals = env["account.journal"].with_context(active_test=False).search([])
|
journals = env["account.journal"].with_context(active_test=False).search([])
|
||||||
for journal in journals:
|
for journal in journals:
|
||||||
vals = {}
|
|
||||||
journal_vals = {
|
journal_vals = {
|
||||||
"code": journal.code,
|
"code": journal.code,
|
||||||
"name": journal.name,
|
"name": journal.name,
|
||||||
"company_id": journal.company_id.id,
|
"company_id": journal.company_id.id,
|
||||||
}
|
}
|
||||||
seq_vals = journal._prepare_sequence(journal_vals)
|
seq_vals = journal._prepare_sequence(journal_vals)
|
||||||
vals["sequence_id"] = env["ir.sequence"].create(seq_vals).id
|
seq_vals.update(journal._prepare_sequence_current_moves())
|
||||||
|
vals = {"sequence_id": env["ir.sequence"].create(seq_vals).id}
|
||||||
if journal.type in ("sale", "purchase") and journal.refund_sequence:
|
if journal.type in ("sale", "purchase") and journal.refund_sequence:
|
||||||
rseq_vals = journal._prepare_sequence(journal_vals, refund=True)
|
rseq_vals = journal._prepare_sequence(journal_vals, refund=True)
|
||||||
|
rseq_vals.update(journal._prepare_sequence_current_moves(refund=True))
|
||||||
vals["refund_sequence_id"] = env["ir.sequence"].create(rseq_vals).id
|
vals["refund_sequence_id"] = env["ir.sequence"].create(rseq_vals).id
|
||||||
journal.write(vals)
|
journal.write(vals)
|
||||||
return
|
return
|
||||||
|
@ -1 +1,2 @@
|
|||||||
* Alexis de Lattre <alexis.delattre@akretion.com>
|
* Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
* Moisés López <moylop260@vauxoo.com>
|
||||||
|
@ -14,14 +14,15 @@
|
|||||||
<field
|
<field
|
||||||
name="sequence_id"
|
name="sequence_id"
|
||||||
required="0"
|
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 or 'UNKNOWN') + '/%%(range_year)s/'}"
|
||||||
/>
|
/>
|
||||||
</field>
|
</field>
|
||||||
<field name="refund_sequence" position="after">
|
<field name="refund_sequence" position="after">
|
||||||
<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/'}"
|
required="1"
|
||||||
|
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 or 'UNKNOWN') + '/%%(range_year)s/'}"
|
||||||
/>
|
/>
|
||||||
</field>
|
</field>
|
||||||
</field>
|
</field>
|
||||||
|
Loading…
Reference in New Issue
Block a user