From d1d6874aad0b2e71413c16c10e5edf6fd0cb9930 Mon Sep 17 00:00:00 2001 From: Moises Lopez Date: Thu, 5 May 2022 15:18:14 +0000 Subject: [PATCH] [REF] account_move_name_sequence: Add number_next_actual and date range lines data based on current moves --- account_move_name_sequence/__manifest__.py | 4 +- .../models/account_journal.py | 158 +++++++++++++++++- account_move_name_sequence/post_install.py | 7 +- .../readme/CONTRIBUTORS.rst | 1 + .../views/account_journal.xml | 5 +- 5 files changed, 164 insertions(+), 11 deletions(-) diff --git a/account_move_name_sequence/__manifest__.py b/account_move_name_sequence/__manifest__.py index 2d131aab..1ce93e5f 100644 --- a/account_move_name_sequence/__manifest__.py +++ b/account_move_name_sequence/__manifest__.py @@ -1,5 +1,7 @@ # Copyright 2021 Akretion France (http://www.akretion.com/) +# Copyright 2022 Vauxoo (https://www.vauxoo.com/) # @author: Alexis de Lattre +# @author: Moisés López # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { @@ -8,7 +10,7 @@ "category": "Accounting", "license": "AGPL-3", "summary": "Generate journal entry number from sequence", - "author": "Akretion,Odoo Community Association (OCA)", + "author": "Akretion,Vauxoo,Odoo Community Association (OCA)", "maintainers": ["alexis-via"], "website": "https://github.com/OCA/account-financial-tools", "depends": ["account"], diff --git a/account_move_name_sequence/models/account_journal.py b/account_move_name_sequence/models/account_journal.py index a1e20146..740e01e9 100644 --- a/account_move_name_sequence/models/account_journal.py +++ b/account_move_name_sequence/models/account_journal.py @@ -1,10 +1,18 @@ # Copyright 2021 Akretion France (http://www.akretion.com/) +# Copyright 2022 Vauxoo (https://www.vauxoo.com/) # @author: Alexis de Lattre +# @author: Moisés López # 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.exceptions import ValidationError +_logger = logging.getLogger(__name__) + class AccountJournal(models.Model): _inherit = "account.journal" @@ -13,16 +21,10 @@ class AccountJournal(models.Model): "ir.sequence", string="Entry Sequence", copy=False, - required=True, check_company=True, domain="[('company_id', '=', company_id)]", 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( "ir.sequence", string="Credit Note Entry Sequence", @@ -31,6 +33,8 @@ class AccountJournal(models.Model): domain="[('company_id', '=', company_id)]", 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") def _check_journal_sequence(self): @@ -95,3 +99,145 @@ class AccountJournal(models.Model): def _create_sequence(self, vals, refund=False): seq_vals = self._prepare_sequence(vals, refund=refund) 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 {} diff --git a/account_move_name_sequence/post_install.py b/account_move_name_sequence/post_install.py index 5384a8cb..b4f8b997 100644 --- a/account_move_name_sequence/post_install.py +++ b/account_move_name_sequence/post_install.py @@ -1,5 +1,7 @@ # Copyright 2021 Akretion France (http://www.akretion.com/) +# Copyright 2022 Vauxoo (https://www.vauxoo.com/) # @author: Alexis de Lattre +# @author: Moisés López # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import SUPERUSER_ID, api @@ -10,16 +12,17 @@ def create_journal_sequences(cr, registry): 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 + 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: 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 journal.write(vals) return diff --git a/account_move_name_sequence/readme/CONTRIBUTORS.rst b/account_move_name_sequence/readme/CONTRIBUTORS.rst index ff65d68c..c3f90701 100644 --- a/account_move_name_sequence/readme/CONTRIBUTORS.rst +++ b/account_move_name_sequence/readme/CONTRIBUTORS.rst @@ -1 +1,2 @@ * Alexis de Lattre +* Moisés López diff --git a/account_move_name_sequence/views/account_journal.xml b/account_move_name_sequence/views/account_journal.xml index 073d40be..6cc643a7 100644 --- a/account_move_name_sequence/views/account_journal.xml +++ b/account_move_name_sequence/views/account_journal.xml @@ -14,14 +14,15 @@