diff --git a/addons/at_accounting/models/account_move_line.py b/addons/at_accounting/models/account_move_line.py new file mode 100644 index 0000000..5de52bc --- /dev/null +++ b/addons/at_accounting/models/account_move_line.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, models, fields, _ + +from odoo.exceptions import UserError +from odoo.tools import SQL + +class AccountMoveLine(models.Model): + _name = "account.move.line" + _inherit = "account.move.line" + + exclude_bank_lines = fields.Boolean(compute='_compute_exclude_bank_lines', store=True) + + @api.depends('journal_id') + def _compute_exclude_bank_lines(self): + for move_line in self: + move_line.exclude_bank_lines = move_line.account_id != move_line.journal_id.default_account_id + + @api.constrains('tax_ids', 'tax_tag_ids') + def _check_taxes_on_closing_entries(self): + for aml in self: + if aml.move_id.tax_closing_report_id and (aml.tax_ids or aml.tax_tag_ids): + raise UserError(_("You cannot add taxes on a tax closing move line.")) + + @api.depends('product_id', 'product_uom_id', 'move_id.tax_closing_report_id') + def _compute_tax_ids(self): + """ Some special cases may see accounts used in tax closing having default taxes. + They would trigger the constrains above, which we don't want. Instead, we don't trigger + the tax computation in this case. + """ + # EXTEND account + lines_to_compute = self.filtered(lambda line: not line.move_id.tax_closing_report_id) + (self - lines_to_compute).tax_ids = False + super(AccountMoveLine, lines_to_compute)._compute_tax_ids() + + @api.model + def _prepare_aml_shadowing_for_report(self, change_equivalence_dict): + """ Prepares the fields lists for creating a temporary table shadowing the account_move_line one. + This is used to switch the computation mode of the reports, with analytics or financial budgets, for example. + + :param change_equivalence_dict: A dict, in the form {aml_field: sql_equivalence}, where: + - aml_field: is a string containing the name of field of account.move.line + - sql_equivalence: is the value to use to shadow aml_field. It can be an SQL object; if + it's not, it'll be escaped in the query. + + :return: A tuple of 2 SQL objects, so that: + - The first one is the fields list to pass into the INSERT TO part of the query filling up the temporary table + - The second one contains the field values to insert into the SELECT clause of the same query, in the same order + as in the first element of the returned tuple. + """ + line_fields = self.env['account.move.line'].fields_get() + self.env.cr.execute("SELECT column_name FROM information_schema.columns WHERE table_name='account_move_line'") + stored_fields = {f[0] for f in self.env.cr.fetchall() if f[0] in line_fields} + + fields_to_insert = [] + for fname in stored_fields: + if fname in change_equivalence_dict: + fields_to_insert.append(SQL( + "%(original)s AS %(asname)s", + original=change_equivalence_dict[fname], + asname=SQL('"account_move_line.%s"', SQL(fname)), + )) + else: + line_field = line_fields[fname] + if line_field.get("translate"): + typecast = SQL('jsonb') + else: + typecast = SQL(self.env['account.move.line']._fields[fname].column_type[0]) + + fields_to_insert.append(SQL( + "CAST(NULL AS %(typecast)s) AS %(fname)s", + typecast=typecast, + fname=SQL('"account_move_line.%s"', SQL(fname)), + )) + + return SQL(', ').join(SQL.identifier(fname) for fname in stored_fields), SQL(', ').join(fields_to_insert)