2
0
account-financial-tools/account_move_template/models/account_move_template.py
2023-11-05 14:25:12 +01:00

229 lines
8.2 KiB
Python

# Copyright 2015-2019 See manifest
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from odoo import _, api, fields, models
from odoo.exceptions import UserError, ValidationError
from odoo.tools.safe_eval import safe_eval
class AccountMoveTemplate(models.Model):
_name = "account.move.template"
_description = "Journal Entry Template"
_check_company_auto = True
name = fields.Char(required=True)
company_id = fields.Many2one(
"res.company",
string="Company",
required=True,
ondelete="cascade",
default=lambda self: self.env.company,
)
journal_id = fields.Many2one(
"account.journal",
string="Journal",
required=True,
check_company=True,
domain="[('company_id', '=', company_id)]",
)
ref = fields.Char(string="Reference", copy=False)
line_ids = fields.One2many(
"account.move.template.line", inverse_name="template_id", string="Lines"
)
active = fields.Boolean(default=True)
_sql_constraints = [
(
"name_company_unique",
"unique(name, company_id)",
"This name is already used by another template!",
)
]
def copy(self, default=None):
self.ensure_one()
default = dict(default or {}, name=_("%s (copy)") % self.name)
return super().copy(default)
def eval_computed_line(self, line, sequence2amount):
safe_eval_dict = {}
for seq, amount in sequence2amount.items():
safe_eval_dict["L%d" % seq] = amount
try:
val = safe_eval(line.python_code, safe_eval_dict)
sequence2amount[line.sequence] = val
except ValueError as err:
raise UserError(
_(
"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.",
sequence=line.sequence,
code=line.python_code,
)
) from err
except SyntaxError as err:
raise UserError(
_(
"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,
)
) from err
def compute_lines(self, sequence2amount):
company_cur = self.company_id.currency_id
input_sequence2amount = sequence2amount.copy()
for line in self.line_ids.filtered(lambda x: x.type == "input"):
if line.sequence not in sequence2amount:
raise UserError(
_(
"You deleted a line in the wizard. This is not allowed: "
"you should either update the template or modify the "
"journal entry that will be generated by this wizard."
)
)
input_sequence2amount.pop(line.sequence)
if input_sequence2amount:
raise UserError(
_(
"You added a line in the wizard. This is not allowed: "
"you should either update the template or modify "
"the journal entry that will be generated by this wizard."
)
)
for line in self.line_ids.filtered(lambda x: x.type == "computed"):
self.eval_computed_line(line, sequence2amount)
sequence2amount[line.sequence] = company_cur.round(
sequence2amount[line.sequence]
)
return sequence2amount
def generate_journal_entry(self):
"""Called by the button on the form view"""
self.ensure_one()
wiz = self.env["account.move.template.run"].create({"template_id": self.id})
action = wiz.load_lines()
return action
class AccountMoveTemplateLine(models.Model):
_name = "account.move.template.line"
_description = "Journal Item Template"
_order = "sequence, id"
_inherit = "analytic.mixin"
_check_company_auto = True
template_id = fields.Many2one(
"account.move.template", string="Move Template", ondelete="cascade"
)
name = fields.Char(string="Label")
sequence = fields.Integer(required=True)
account_id = fields.Many2one(
"account.account",
string="Account",
required=True,
domain="[('company_id', '=', company_id), ('deprecated', '=', False)]",
check_company=True,
)
partner_id = fields.Many2one(
"res.partner",
string="Partner",
domain=["|", ("parent_id", "=", False), ("is_company", "=", True)],
)
tax_ids = fields.Many2many("account.tax", string="Taxes", check_company=True)
tax_line_id = fields.Many2one(
"account.tax", string="Originator Tax", ondelete="restrict", check_company=True
)
company_id = fields.Many2one(related="template_id.company_id", store=True)
company_currency_id = fields.Many2one(
related="template_id.company_id.currency_id",
string="Company Currency",
store=True,
)
note = fields.Char()
type = fields.Selection(
[
("input", "User input"),
("computed", "Computed"),
],
required=True,
default="input",
)
python_code = fields.Text(string="Formula")
move_line_type = fields.Selection(
[("cr", "Credit"), ("dr", "Debit")], required=True, string="Direction"
)
payment_term_id = fields.Many2one(
"account.payment.term",
string="Payment Terms",
help="Used to compute the due date of the journal item.",
)
is_refund = fields.Boolean(
default=False,
string="Is a refund?",
)
tax_repartition_line_id = fields.Many2one(
"account.tax.repartition.line",
string="Tax Repartition Line",
compute="_compute_tax_repartition_line_id",
store=True,
)
opt_account_id = fields.Many2one(
"account.account",
string="Account if Negative",
domain="[('company_id', '=', company_id), ('deprecated', '=', False)]",
check_company=True,
help="When amount is negative, use this account instead",
)
@api.depends("is_refund", "account_id", "tax_line_id")
def _compute_tax_repartition_line_id(self):
for record in self.filtered(lambda x: x.account_id and x.tax_line_id):
tax_repartition = "refund_tax_id" if record.is_refund else "invoice_tax_id"
record.tax_repartition_line_id = self.env[
"account.tax.repartition.line"
].search(
[
("account_id", "=", record.account_id.id),
(tax_repartition, "=", record.tax_line_id.id),
],
limit=1,
)
@api.depends("account_id", "partner_id")
def _compute_analytic_distribution(self):
for line in self:
distribution = self.env[
"account.analytic.distribution.model"
]._get_distribution(
{
"partner_id": line.partner_id.id,
"partner_category_id": line.partner_id.category_id.ids,
"product_id": False,
"product_categ_id": False,
"account_prefix": line.account_id.code,
"company_id": line.template_id.company_id.id,
}
)
line.analytic_distribution = distribution or line.analytic_distribution
_sql_constraints = [
(
"sequence_template_uniq",
"unique(template_id, sequence)",
"The sequence of the line must be unique per template!",
)
]
@api.constrains("type", "python_code")
def check_python_code(self):
for line in self:
if line.type == "computed" and not line.python_code:
raise ValidationError(
_("Python Code must be set for computed line with sequence %d.")
% line.sequence
)