diff --git a/account_move_template/__manifest__.py b/account_move_template/__manifest__.py index 7bcdb8f5..69b60865 100644 --- a/account_move_template/__manifest__.py +++ b/account_move_template/__manifest__.py @@ -5,7 +5,7 @@ { "name": "Account Move Template", - "version": "14.0.1.1.1", + "version": "15.0.1.0.0", "category": "Accounting", "summary": "Templates for recurring Journal Entries", "author": "Agile Business Group, Aurium Technologies, Vauxoo, ForgeFlow, " diff --git a/account_move_template/i18n/account_move_template.pot b/account_move_template/i18n/account_move_template.pot index 066bd8de..3efa7d4d 100644 --- a/account_move_template/i18n/account_move_template.pot +++ b/account_move_template/i18n/account_move_template.pot @@ -4,8 +4,10 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 14.0\n" +"Project-Id-Version: Odoo Server 15.0+e\n" "Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-06-03 20:52+0000\n" +"PO-Revision-Date: 2022-06-03 20:52+0000\n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -200,25 +202,25 @@ msgstr "" #: code:addons/account_move_template/models/account_move_template.py:0 #, python-format msgid "" -"Impossible to compute the formula of line with sequence %s (formula: %s). " -"Check that the lines used in the formula really exists and have a lower " -"sequence than the current line." +"Impossible to compute the formula of line with sequence %(sequence)s " +"(formula: %(code)s). Check that the lines used in the formula really exists " +"and have a lower sequence than the current line." msgstr "" #. module: account_move_template #: code:addons/account_move_template/models/account_move_template.py:0 #, python-format msgid "" -"Impossible to compute the formula of line with sequence %s (formula: %s): " -"the syntax of the formula is wrong." +"Impossible to compute the formula of line with sequence %(sequence)s " +"(formula: %(code)s): the syntax of the formula is wrong." msgstr "" #. module: account_move_template #: code:addons/account_move_template/wizard/account_move_template_run.py:0 #, python-format msgid "" -"Invalid dictionary: {}\n" -"{}" +"Invalid dictionary: %(exception)s\n" +"%(msg)s" msgstr "" #. module: account_move_template @@ -435,8 +437,8 @@ msgstr "" #: code:addons/account_move_template/wizard/account_move_template_run.py:0 #, python-format msgid "" -"The selected template (%s) is not in the same company (%s) as the current " -"user (%s)." +"The selected template (%(template)s) is not in the same company " +"(%(company)s) as the current user (%(user_company)s)." msgstr "" #. module: account_move_template diff --git a/account_move_template/models/account_move_template.py b/account_move_template/models/account_move_template.py index fc275af1..c12cbaff 100644 --- a/account_move_template/models/account_move_template.py +++ b/account_move_template/models/account_move_template.py @@ -45,24 +45,26 @@ class AccountMoveTemplate(models.Model): try: val = safe_eval(line.python_code, safe_eval_dict) sequence2amount[line.sequence] = val - except ValueError: + except ValueError as err: raise UserError( _( - "Impossible to compute the formula of line with sequence %s " - "(formula: %s). Check that the lines used in the formula " + "Impossible to compute the formula of line with sequence %(sequence)s " + "(formula: %(code)s). Check that the lines used in the formula " "really exists and have a lower sequence than the current " - "line." + "line.", + sequence=line.sequence, + code=line.python_code, ) - % (line.sequence, line.python_code) - ) - except SyntaxError: + ) from err + except SyntaxError as err: raise UserError( _( - "Impossible to compute the formula of line with sequence %s " - "(formula: %s): the syntax of the formula is wrong." + "Impossible to compute the formula of line with sequence %(sequence)s " + "(formula: %(code)s): the syntax of the formula is wrong.", + sequence=line.sequence, + code=line.python_code, ) - % (line.sequence, line.python_code) - ) + ) from err def compute_lines(self, sequence2amount): company_cur = self.company_id.currency_id @@ -109,7 +111,7 @@ class AccountMoveTemplateLine(models.Model): "account.move.template", string="Move Template", ondelete="cascade" ) name = fields.Char(string="Label", required=True) - sequence = fields.Integer("Sequence", required=True) + sequence = fields.Integer(required=True) account_id = fields.Many2one( "account.account", string="Account", @@ -138,11 +140,10 @@ class AccountMoveTemplateLine(models.Model): note = fields.Char() type = fields.Selection( [("computed", "Computed"), ("input", "User input")], - string="Type", required=True, default="input", ) - python_code = fields.Text("Python Code") + python_code = fields.Text() move_line_type = fields.Selection( [("cr", "Credit"), ("dr", "Debit")], required=True, string="Direction" ) diff --git a/account_move_template/tests/test_account_move_template.py b/account_move_template/tests/test_account_move_template.py index 510c40d4..869b2ae8 100644 --- a/account_move_template/tests/test_account_move_template.py +++ b/account_move_template/tests/test_account_move_template.py @@ -4,7 +4,7 @@ import logging from psycopg2 import IntegrityError -from odoo import fields +from odoo import Command, fields from odoo.tests.common import TransactionCase from odoo.tools import mute_logger @@ -20,7 +20,7 @@ class TestAccountMoveTemplate(TransactionCase): def _chart_of_accounts_create(self, company, chart): _logger.debug("Creating chart of account") self.env.user.write( - {"company_ids": [(4, company.id)], "company_id": company.id} + {"company_ids": [Command.link(company.id)], "company_id": company.id} ) self.with_context(company_id=company.id, force_company=company.id) wizard = self.env["wizard.multi.charts.accounts"].create( @@ -71,7 +71,7 @@ class TestAccountMoveTemplate(TransactionCase): ) ], "company_id": self.company.id, - "company_ids": [(4, self.company.id)], + "company_ids": [Command.link(self.company.id)], } ) ) @@ -179,7 +179,7 @@ class TestAccountMoveTemplate(TransactionCase): "sequence": 2, "account_id": self.account_company_1.id, "move_line_type": "cr", - "tax_ids": [(4, self.tax.id)], + "tax_ids": [Command.link(self.tax.id)], "type": "input", }, ), diff --git a/account_move_template/tests/test_account_move_template_options.py b/account_move_template/tests/test_account_move_template_options.py index a5310814..52f7dfc4 100644 --- a/account_move_template/tests/test_account_move_template_options.py +++ b/account_move_template/tests/test_account_move_template_options.py @@ -1,6 +1,7 @@ # Copyright 2020 Ecosoft (http://ecosoft.co.th) # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html -from odoo.exceptions import ValidationError +from odoo import Command +from odoo.exceptions import UserError, ValidationError from odoo.tests.common import Form, TransactionCase @@ -69,9 +70,9 @@ class TestAccountMoveTemplateEnhanced(TransactionCase): "name": "Test Template", "journal_id": self.journal.id, "line_ids": [ - (0, 0, ar_line), - (0, 0, income_line1), - (0, 0, income_line2), + Command.create(ar_line), + Command.create(income_line1), + Command.create(income_line2), ], } ) @@ -143,7 +144,8 @@ class TestAccountMoveTemplateEnhanced(TransactionCase): } ) template_run = f.save() - with self.assertRaises(ValidationError): + msg_error = "overwrite are .'partner_id', 'amount', 'name', 'date_maturity'" + with self.assertRaisesRegex(ValidationError, msg_error): template_run.load_lines() # Assign only on valid fields, and load_lines again with Form(self.env["account.move.template.run"]) as f: @@ -159,7 +161,7 @@ class TestAccountMoveTemplateEnhanced(TransactionCase): res = template_run.load_lines() self.assertEqual(template_run.line_ids[0].partner_id, self.partners[0]) self.assertEqual(template_run.line_ids[0].amount, 3000) - res = template_run.with_context(res["context"]).generate_move() + res = template_run.with_context(**res["context"]).generate_move() move = self.Move.browse(res["res_id"]) self.assertRecordValues( move.line_ids.sorted("credit"), @@ -184,3 +186,86 @@ class TestAccountMoveTemplateEnhanced(TransactionCase): }, ], ) + + def test_move_copy(self): + template_copy = self.move_template.copy() + self.assertEqual(template_copy.name, "Test Template (copy)") + + def test_move_generate_from_action_button(self): + # `Generate Journal Entry` action button + res = self.move_template.generate_journal_entry() + self.assertEqual(res["name"], "Create Entry from Template") + self.assertEqual(res["res_model"], "account.move.template.run") + + def test_move_template_exceptions(self): + msg_error = "Python Code must be set for computed line with sequence 1." + with self.assertRaisesRegex(ValidationError, msg_error): + self.move_template.line_ids[1].python_code = "" + + self.move_template.line_ids[1].python_code = "P0*1/3" + with Form(self.env["account.move.template.run"]) as f: + f.template_id = self.move_template + template_run = f.save() + template_run.load_lines() + msg_error = "really exists and have a lower sequence than the current line." + with self.assertRaisesRegex(UserError, msg_error): + template_run.generate_move() + + self.move_template.line_ids[1].python_code = "L0*" + with Form(self.env["account.move.template.run"]) as f: + f.template_id = self.move_template + template_run = f.save() + template_run.load_lines() + msg_error = "the syntax of the formula is wrong." + with self.assertRaisesRegex(UserError, msg_error): + template_run.generate_move() + + self.move_template.line_ids[1].python_code = "L0*1/3" + with Form(self.env["account.move.template.run"]) as f: + f.template_id = self.move_template + template_run = f.save() + template_run.load_lines() + template_run.line_ids[0].amount = 0 + msg_error = "Debit and credit of all lines are null." + with self.assertRaisesRegex(UserError, msg_error): + template_run.generate_move() + + with Form(self.env["account.move.template.run"]) as f: + f.template_id = self.move_template + f.overwrite = [] + template_run = f.save() + msg_error = "Overwrite value must be a valid python dict" + with self.assertRaisesRegex(ValidationError, msg_error): + template_run.load_lines() + + with Form(self.env["account.move.template.run"]) as f: + f.template_id = self.move_template + f.overwrite = str({"P0": {"amount": 100}}) + template_run = f.save() + msg_error = "Keys must be line sequence, i..e, L1, L2, ..." + with self.assertRaisesRegex(ValidationError, msg_error): + template_run.load_lines() + + with Form(self.env["account.move.template.run"]) as f: + f.template_id = self.move_template + f.overwrite = str({"L0": []}) + template_run = f.save() + msg_error = "Invalid dictionary: 'list' object has no attribute 'keys'" + with self.assertRaisesRegex(ValidationError, msg_error): + template_run.load_lines() + + with Form(self.env["account.move.template.run"]) as f: + f.template_id = self.move_template + f.overwrite = str({"L0": {"test": 100}}) + template_run = f.save() + msg_error = "overwrite are .'partner_id', 'amount', 'name', 'date_maturity'" + with self.assertRaisesRegex(ValidationError, msg_error): + template_run.load_lines() + + with Form(self.env["account.move.template.run"]) as f: + f.template_id = self.move_template + template_run = f.save() + template_run.line_ids.unlink() + msg_error = "You deleted a line in the wizard. This is not allowed:" + with self.assertRaisesRegex(UserError, msg_error): + template_run.generate_move() diff --git a/account_move_template/wizard/account_move_template_run.py b/account_move_template/wizard/account_move_template_run.py index d8ed322a..d4e22153 100644 --- a/account_move_template/wizard/account_move_template_run.py +++ b/account_move_template/wizard/account_move_template_run.py @@ -2,7 +2,7 @@ # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html from ast import literal_eval -from odoo import _, fields, models +from odoo import Command, _, fields, models from odoo.exceptions import UserError, ValidationError @@ -51,9 +51,9 @@ Valid dictionary to overwrite template lines: "partner_id": tmpl_line.partner_id.id or False, "move_line_type": tmpl_line.move_line_type, "tax_line_id": tmpl_line.tax_line_id.id, - "tax_ids": [(6, 0, tmpl_line.tax_ids.ids)], + "tax_ids": [Command.set(tmpl_line.tax_ids.ids)], "analytic_account_id": tmpl_line.analytic_account_id.id, - "analytic_tag_ids": [(6, 0, tmpl_line.analytic_tag_ids.ids)], + "analytic_tag_ids": [Command.set(tmpl_line.analytic_tag_ids.ids)], "note": tmpl_line.note, "payment_term_id": tmpl_line.payment_term_id.id or False, "is_refund": tmpl_line.is_refund, @@ -70,13 +70,11 @@ Valid dictionary to overwrite template lines: if self.company_id != self.template_id.company_id: raise UserError( _( - "The selected template (%s) is not in the same company (%s) " - "as the current user (%s)." - ) - % ( - self.template_id.name, - self.template_id.company_id.display_name, - self.company_id.display_name, + "The selected template (%(template)s) is not in the same company " + "(%(company)s) as the current user (%(user_company)s).", + template=self.template_id.name, + company=self.template_id.company_id.display_name, + user_company=self.company_id.display_name, ) ) tmpl_lines = self.template_id.line_ids @@ -121,8 +119,10 @@ Valid dictionary to overwrite template lines: try: overwrite_vals = literal_eval(overwrite_vals) assert isinstance(overwrite_vals, dict) - except (SyntaxError, ValueError, AssertionError): - raise ValidationError(_("Overwrite value must be a valid python dict")) + except (SyntaxError, ValueError, AssertionError) as err: + raise ValidationError( + _("Overwrite value must be a valid python dict") + ) from err # First level keys must be L1, L2, ... keys = overwrite_vals.keys() if list(filter(lambda x: x[:1] != "L" or not x[1:].isdigit(), keys)): @@ -144,7 +144,13 @@ Valid dictionary to overwrite template lines: 'L2': {'partner_id': 2, 'amount': 20}, } """ - raise ValidationError(_("Invalid dictionary: {}\n{}".format(e, msg))) + raise ValidationError( + _( + "Invalid dictionary: %(exception)s\n%(msg)s", + exception=e, + msg=msg, + ) + ) from e return overwrite_vals def _safe_vals(self, model, vals): @@ -179,7 +185,7 @@ Valid dictionary to overwrite template lines: amount = sequence2amount[line.sequence] if not company_cur.is_zero(amount): move_vals["line_ids"].append( - (0, 0, self._prepare_move_line(line, amount)) + Command.create(self._prepare_move_line(line, amount)) ) move = self.env["account.move"].create(move_vals) action = self.env.ref("account.action_move_journal_line") @@ -223,9 +229,9 @@ Valid dictionary to overwrite template lines: "tax_repartition_line_id": line.tax_repartition_line_id.id or False, } if line.analytic_tag_ids: - values["analytic_tag_ids"] = [(6, 0, line.analytic_tag_ids.ids)] + values["analytic_tag_ids"] = [Command.set(line.analytic_tag_ids.ids)] if line.tax_ids: - values["tax_ids"] = [(6, 0, line.tax_ids.ids)] + values["tax_ids"] = [Command.set(line.tax_ids.ids)] tax_repartition = "refund_tax_id" if line.is_refund else "invoice_tax_id" atrl_ids = self.env["account.tax.repartition.line"].search( [ @@ -233,9 +239,11 @@ Valid dictionary to overwrite template lines: ("repartition_type", "=", "base"), ] ) - values["tag_ids"] = [(6, 0, atrl_ids.mapped("tag_ids").ids)] + values["tax_tag_ids"] = [Command.set(atrl_ids.mapped("tag_ids").ids)] if line.tax_repartition_line_id: - values["tag_ids"] = [(6, 0, line.tax_repartition_line_id.tag_ids.ids)] + values["tax_tag_ids"] = [ + Command.set(line.tax_repartition_line_id.tag_ids.ids) + ] # With overwrite options overwrite = self._context.get("overwrite", {}) move_line_vals = overwrite.get("L{}".format(line.sequence), {}) @@ -264,8 +272,8 @@ class AccountMoveTemplateLineRun(models.TransientModel): company_currency_id = fields.Many2one( related="wizard_id.company_id.currency_id", string="Company Currency" ) - sequence = fields.Integer("Sequence", required=True) - name = fields.Char("Name", readonly=True) + sequence = fields.Integer(required=True) + name = fields.Char(readonly=True) account_id = fields.Many2one("account.account", required=True, readonly=True) analytic_account_id = fields.Many2one("account.analytic.account", readonly=True) analytic_tag_ids = fields.Many2many( @@ -285,9 +293,7 @@ class AccountMoveTemplateLineRun(models.TransientModel): readonly=True, string="Direction", ) - amount = fields.Monetary( - "Amount", required=True, currency_field="company_currency_id" - ) + amount = fields.Monetary(required=True, currency_field="company_currency_id") note = fields.Char(readonly=True) is_refund = fields.Boolean(string="Is a refund?", readonly=True) tax_repartition_line_id = fields.Many2one(