# Fusion Accounting - Move Line Extensions # Bank-line exclusion flag, tax-closing safeguards, and report shadowing from odoo import api, fields, models, _ from odoo.exceptions import UserError from odoo.tools import SQL class FusionAccountMoveLine(models.Model): """Extends journal items with a computed bank-line exclusion flag, guards against tax manipulation on closing entries, and provides utilities for building temporary shadow tables used by analytic and budget reports.""" _name = "account.move.line" _inherit = "account.move.line" # ---- Fields ---- exclude_bank_lines = fields.Boolean( compute='_compute_exclude_bank_lines', store=True, ) # ---- Computed ---- @api.depends('journal_id') def _compute_exclude_bank_lines(self): """Flag lines whose account differs from their journal's default account, used to filter non-bank entries in bank journal views.""" for ml in self: ml.exclude_bank_lines = ( ml.account_id != ml.journal_id.default_account_id ) # ---- Constraints ---- @api.constrains('tax_ids', 'tax_tag_ids') def _check_taxes_on_closing_entries(self): """Prevent taxes from being added to tax-closing move lines.""" for ml in self: if ml.move_id.tax_closing_report_id and (ml.tax_ids or ml.tax_tag_ids): raise UserError( _("Tax lines are not permitted on tax-closing entries.") ) # ---- Tax Computation Override ---- @api.depends('product_id', 'product_uom_id', 'move_id.tax_closing_report_id') def _compute_tax_ids(self): """Skip automatic tax computation for lines on tax-closing moves, which might otherwise trigger the constraint above.""" non_closing_lines = self.filtered( lambda ln: not ln.move_id.tax_closing_report_id ) (self - non_closing_lines).tax_ids = False super(FusionAccountMoveLine, non_closing_lines)._compute_tax_ids() # ---- Report Shadow Table Utility ---- @api.model def _prepare_aml_shadowing_for_report(self, change_equivalence_dict): """Build SQL fragments for creating a temporary table that mirrors ``account_move_line`` but substitutes selected columns with alternative expressions (e.g. analytic or budget data). :param change_equivalence_dict: Mapping ``{field_name: sql_expression}`` where each value replaces the corresponding column in the shadow table. :returns: A tuple ``(insert_columns, select_expressions)`` of SQL objects suitable for ``INSERT INTO ... SELECT ...``. """ field_metadata = self.env['account.move.line'].fields_get() self.env.cr.execute( "SELECT column_name FROM information_schema.columns " "WHERE table_name='account_move_line'" ) db_columns = { row[0] for row in self.env.cr.fetchall() if row[0] in field_metadata } select_parts = [] for col_name in db_columns: if col_name in change_equivalence_dict: select_parts.append(SQL( "%(src)s AS %(alias)s", src=change_equivalence_dict[col_name], alias=SQL('"account_move_line.%s"', SQL(col_name)), )) else: col_meta = field_metadata[col_name] if col_meta.get("translate"): pg_type = SQL('jsonb') else: pg_type = SQL( self.env['account.move.line']._fields[col_name].column_type[0] ) select_parts.append(SQL( "CAST(NULL AS %(pg_type)s) AS %(alias)s", pg_type=pg_type, alias=SQL('"account_move_line.%s"', SQL(col_name)), )) insert_cols = SQL(', ').join(SQL.identifier(c) for c in db_columns) select_clause = SQL(', ').join(select_parts) return insert_cols, select_clause