Files
enterprise-addons/l10n_cl_reports_f29/models/account_report.py
T

470 lines
24 KiB
Python

from odoo import api, models
from odoo.fields import Domain
from odoo.tools import SQL
class AccountReport(models.Model):
_inherit = 'account.report'
def _get_expression_audit_aml_domain(self, expression_to_audit, options):
# EXTENDS account_reports
if expression_to_audit.engine == 'custom' and expression_to_audit.formula == '_report_custom_engine_f29_combined':
return self._l10n_cl_reports_f29_get_audit_domain(expression_to_audit, options, expression_to_audit.label)
return super()._get_expression_audit_aml_domain(expression_to_audit, options)
def _l10n_cl_reports_f29_get_audit_domain(self, expression, options, audit_label):
def get_matching_tags(*xml_ids):
records = self.env['account.report.expression']
for xml_id in xml_ids:
records += self.env.ref(f'l10n_cl.{xml_id}', raise_if_not_found=False)
return tuple(records._get_matching_tags().ids) or (0,)
subformula = expression.subformula
if not subformula:
return Domain([])
bucket = subformula.split('_')[-1]
# Base universal domain (mirrors the WHERE clause)
domain = [('display_type', '!=', 'payment_term')]
# --- GROUP 1: Strict Base/Tax pairs mapped by display_type ---
strict_pairs = {
'111': (('39', '41'), 'out_invoice'),
'502': (('33',), 'out_invoice'),
'510': (('61',), 'out_refund'),
'513': (('56',), 'out_invoice'),
'528': (('61',), 'in_refund'),
'532': (('56',), 'in_invoice'),
'535': (('914',), 'in_invoice'),
}
if bucket in strict_pairs:
doc_codes, move_type = strict_pairs[bucket]
domain += [
('move_id.l10n_latam_document_type_id.code', 'in', doc_codes),
('move_id.move_type', '=', move_type)
]
if audit_label == 'balance':
domain += [('display_type', 'in', ('product', 'rounding'))]
elif audit_label == 'tax_balance':
domain += [('display_type', '=', 'tax')]
# --- GROUP 2: Static Grids (No display_type split, SQL assigns everything matching to the same grid) ---
elif bucket == '020':
domain += [('move_id.l10n_latam_document_type_id.code', 'in', ('110', '111', '112')), ('move_id.move_type', 'in', ('out_invoice', 'out_refund'))]
elif bucket == '142':
domain += [('move_id.l10n_latam_document_type_id.code', 'in', ('34', '56', '61')), ('move_id.move_type', 'in', ('out_invoice', 'out_refund'))]
# Mirror SQL CASE fallthrough: Exclude document/move combinations already caught by Group 1
domain += ['!', '&', ('move_id.l10n_latam_document_type_id.code', '=', '56'), ('move_id.move_type', '=', 'out_invoice')]
domain += ['!', '&', ('move_id.l10n_latam_document_type_id.code', '=', '61'), ('move_id.move_type', '=', 'out_refund')]
elif bucket == '562':
domain += [('move_id.l10n_latam_document_type_id.code', 'in', ('34', '56', '61')), ('move_id.move_type', 'in', ('in_invoice', 'in_refund'))]
# Mirror SQL CASE fallthrough: Exclude document/move combinations already caught by Group 1
domain += ['!', '&', ('move_id.l10n_latam_document_type_id.code', '=', '56'), ('move_id.move_type', '=', 'in_invoice')]
domain += ['!', '&', ('move_id.l10n_latam_document_type_id.code', '=', '61'), ('move_id.move_type', '=', 'in_refund')]
elif bucket == '566':
domain += [('move_id.l10n_latam_document_type_id', '=', False), ('move_id.move_type', 'in', ('in_invoice', 'in_refund'))]
# --- GROUP 3: Tag-Dependent Grids (Mapped dynamically via the specific base vs tax tags) ---
elif bucket == '151':
domain += [('move_id.l10n_latam_document_type_id.code', '=', '71'), ('move_id.move_type', '=', 'in_invoice')]
if audit_label == 'tax_balance':
domain += [('tax_tag_ids', 'in', get_matching_tags('tax_report_retencion_segunda_categ_tag'))]
elif bucket in ('762', '520', '521', '525'):
domain += [('move_id.l10n_latam_document_type_id.code', 'in', ('33', '46')), ('move_id.move_type', 'in', ('in_invoice', 'in_refund'))]
if bucket == '762':
if audit_label == 'balance':
domain += [('tax_tag_ids', 'in', get_matching_tags('tax_report_compras_supermercado_tag'))]
else:
domain += [('tax_tag_ids', 'in', get_matching_tags('tax_report_compras_iva_supermercado_tag'))]
elif bucket == '520':
if audit_label == 'balance':
domain += [('tax_tag_ids', 'in', get_matching_tags('tax_report_compras_netas_gr_iva_recup_tag'))]
else:
domain += [('tax_tag_ids', 'in', get_matching_tags('tax_report_compras_iva_recup_tag'))]
elif bucket == '521':
if audit_label == 'balance':
domain += [('tax_tag_ids', 'in', get_matching_tags('tax_report_compras_activo_fijo_no_recup_tag', 'tax_report_compras_netas_gr_iva_no_recuperable_tag'))]
else:
domain += [('tax_tag_ids', 'in', get_matching_tags('tax_report_compras_iva_activo_fijo_no_recup_tag', 'tax_report_compras_iva_no_recup_tag'))]
elif bucket == '525':
if audit_label == 'balance':
domain += [('tax_tag_ids', 'in', get_matching_tags('tax_report_compras_activo_fijo_tag', 'tax_report_compras_activo_fijo_uso_comun_tag', 'tax_report_compras_activo_fijo_no_recup_tag'))]
else:
domain += [('tax_tag_ids', 'in', get_matching_tags('tax_report_compras_iva_activo_fijo_tag', 'tax_report_compras_iva_activo_fijo_uso_comun_tag', 'tax_report_compras_iva_activo_fijo_no_recup_tag'))]
elif bucket == '039':
domain += [('move_id.l10n_latam_document_type_id.code', '=', '46'), ('move_id.move_type', 'in', ('in_invoice', 'in_refund'))]
if audit_label == 'tax_balance':
domain += [('display_type', '=', 'tax'), ('tax_tag_ids', 'in', get_matching_tags('tax_report_retencion_total_compras_tag'))]
return Domain(domain)
class L10nClTaxReportHandler(models.AbstractModel):
_name = 'l10n_cl.tax.report.handler'
_inherit = 'account.tax.report.handler'
_description = 'Chilean Tax Report Handler'
@api.model
def _get_tag_ids_per_report_grid(self):
def get_matching_tags(*xml_ids):
records = self.env['account.report.expression']
for xml_id in xml_ids:
records += self.env.ref(f'l10n_cl.{xml_id}')
return tuple(records._get_matching_tags().ids) or (0,)
return {
'tags_039': get_matching_tags('tax_report_retencion_total_compras_tag'),
'tags_151': get_matching_tags('tax_report_retencion_segunda_categ_tag'),
'tags_519': get_matching_tags('tax_report_compras_netas_gr_iva_recup_tag'),
'tags_520': get_matching_tags('tax_report_compras_iva_recup_tag'),
'tags_521': get_matching_tags(
'tax_report_compras_iva_activo_fijo_no_recup_tag',
'tax_report_compras_iva_no_recup_tag',
),
'tags_524': get_matching_tags(
'tax_report_compras_activo_fijo_tag',
'tax_report_compras_activo_fijo_uso_comun_tag',
'tax_report_compras_activo_fijo_no_recup_tag',
),
'tags_525': get_matching_tags(
'tax_report_compras_iva_activo_fijo_tag',
'tax_report_compras_iva_activo_fijo_uso_comun_tag',
'tax_report_compras_iva_activo_fijo_no_recup_tag',
),
'tags_564': get_matching_tags(
'tax_report_compras_activo_fijo_no_recup_tag',
'tax_report_compras_netas_gr_iva_no_recuperable_tag',
),
'tags_761': get_matching_tags('tax_report_compras_supermercado_tag'),
'tags_762': get_matching_tags('tax_report_compras_iva_supermercado_tag'),
'retention_tag': get_matching_tags('tax_report_retencion_total_compras_tag'),
}
@api.model
def _f29_get_aml_query(self, options, date_scope):
report = self.env['account.report'].browse(options['report_id'])
domain_query = report._get_report_query(options, date_scope, domain=[
('move_id.l10n_latam_document_type_id.code', 'in', (False, '33', '34', '39', '41', '46', '56', '61', '71', '110', '111', '112', '914'))
])
domain_query.join(lhs_alias='account_move_line', lhs_column='move_id', rhs_table='account_move', rhs_column='id', link='move')
return SQL(
'''
SELECT DISTINCT ON (1, 2, 3, 4)
account_move_line.id,
account_move_line__move.id AS move_id,
doc_type.code AS doc_code,
CASE
-- Pairs relying on strict Base vs Tax definitions
WHEN doc_type.code IN ('39', '41') AND account_move_line__move.move_type = 'out_invoice' THEN
CASE
WHEN %(base_condition)s THEN '110'
WHEN %(tax_condition)s THEN '111'
ELSE 'OTHER'
END
WHEN doc_type.code = '33' AND account_move_line__move.move_type = 'out_invoice' THEN
CASE
WHEN %(base_condition)s THEN '503'
WHEN %(tax_condition)s THEN '502'
ELSE 'OTHER'
END
WHEN doc_type.code = '61' AND account_move_line__move.move_type = 'out_refund' THEN
CASE
WHEN %(base_condition)s THEN '509'
WHEN %(tax_condition)s THEN '510'
ELSE 'OTHER'
END
WHEN doc_type.code = '56' AND account_move_line__move.move_type = 'out_invoice' THEN
CASE
WHEN %(base_condition)s THEN '512'
WHEN %(tax_condition)s THEN '513'
ELSE 'OTHER'
END
WHEN doc_type.code = '61' AND account_move_line__move.move_type = 'in_refund' THEN
CASE
WHEN %(base_condition)s THEN '527'
WHEN %(tax_condition)s THEN '528'
ELSE 'OTHER'
END
WHEN doc_type.code = '56' AND account_move_line__move.move_type = 'in_invoice' THEN
CASE
WHEN %(base_condition)s THEN '531'
WHEN %(tax_condition)s THEN '532'
ELSE 'OTHER'
END
WHEN doc_type.code = '914' AND account_move_line__move.move_type = 'in_invoice' THEN
CASE
WHEN %(base_condition)s THEN '534'
WHEN %(tax_condition)s THEN '535'
ELSE 'OTHER'
END
-- Static Grids
WHEN doc_type.code IN ('110', '111', '112') AND account_move_line__move.move_type IN ('out_invoice', 'out_refund') THEN '020'
WHEN doc_type.code IN ('34', '56', '61') AND account_move_line__move.move_type IN ('out_invoice', 'out_refund') THEN '142'
WHEN doc_type.code IN ('34', '56', '61') AND account_move_line__move.move_type IN ('in_invoice', 'in_refund') THEN '562'
WHEN doc_type.code IS NULL AND account_move_line__move.move_type IN ('in_invoice', 'in_refund') THEN '566'
-- Tag-Dependent Grids
WHEN doc_type.code = '71' AND account_move_line__move.move_type = 'in_invoice' AND tag_rel.account_account_tag_id IN %(tags_151)s THEN '151'
WHEN doc_type.code IN ('33', '46') AND account_move_line__move.move_type IN ('in_invoice', 'in_refund') THEN
CASE
WHEN tag_rel.account_account_tag_id IN %(tags_761)s THEN '761'
WHEN tag_rel.account_account_tag_id IN %(tags_762)s THEN '762'
WHEN tag_rel.account_account_tag_id IN %(tags_519)s THEN '519'
WHEN tag_rel.account_account_tag_id IN %(tags_520)s THEN '520'
WHEN tag_rel.account_account_tag_id IN %(tags_521)s THEN '521'
WHEN tag_rel.account_account_tag_id IN %(tags_564)s THEN '564'
WHEN tag_rel.account_account_tag_id IN %(tags_524)s THEN '524'
WHEN tag_rel.account_account_tag_id IN %(tags_525)s THEN '525'
WHEN tag_rel.account_account_tag_id IN %(tags_039)s AND %(tax_condition)s THEN '039'
ELSE 'OTHER'
END
ELSE 'OTHER'
END as grid,
%(tax_condition)s AS has_tax,
account_move_line.balance,
account_move_line.account_id
FROM %(from_clause)s
LEFT JOIN account_account_tag_account_move_line_rel tag_rel ON tag_rel.account_move_line_id = account_move_line.id
LEFT JOIN l10n_latam_document_type doc_type ON doc_type.id = account_move_line__move.l10n_latam_document_type_id
LEFT JOIN account_account account ON account.id = account_move_line.account_id
LEFT JOIN account_journal journal ON journal.id = account_move_line__move.journal_id
WHERE
%(where_clause)s
AND COALESCE(account_move_line.display_type, '') != 'payment_term'
''',
from_clause=domain_query.from_clause,
where_clause=domain_query.where_clause,
base_condition=SQL("account_move_line.display_type IN ('product', 'rounding')"),
tax_condition=SQL("account_move_line.display_type = 'tax'"),
**self._get_tag_ids_per_report_grid(),
)
def _report_custom_engine_f29_combined(self, expressions, options, date_scope, current_groupby, next_groupby, offset=0, limit=None, warnings=None):
query = SQL(
'''
WITH
account_move_line_data AS (%(aml_query)s),
account_move_data AS (
SELECT
aml.move_id,
aml.grid,
aml.doc_code,
aml.has_tax AS has_tax,
SUM(aml.balance) AS balance,
CASE WHEN aml.grid = '020' AND aml.doc_code = '110' THEN 1 ELSE 0 END AS count_020_110,
CASE WHEN aml.grid = '020' AND aml.doc_code = '111' THEN 1 ELSE 0 END AS count_020_111,
CASE WHEN aml.grid = '020' AND aml.doc_code = '112' THEN -1 ELSE 0 END AS count_020_112,
CASE WHEN aml.grid IN ('142', '562') AND aml.doc_code = '34' THEN 1 ELSE 0 END AS count_142_34,
CASE WHEN aml.grid IN ('142', '562') AND aml.doc_code = '56' THEN 1 ELSE 0 END AS count_142_56,
CASE WHEN aml.grid IN ('142', '562') AND aml.doc_code = '61' THEN 1 ELSE 0 END AS count_142_61
FROM account_move_line_data aml
GROUP BY 1, 2, 3, 4
)
SELECT
am.grid,
SUM(
CASE
WHEN am.grid = '020' THEN am.count_020_110 + am.count_020_111 - am.count_020_112
WHEN am.grid IN ('142', '562') THEN
am.count_142_34 +
CASE WHEN am.has_tax THEN 0 ELSE am.count_142_56 - am.count_142_61 END
ELSE 1
END
) AS count_rows,
SUM(
CASE
WHEN am.grid = '020' THEN (am.count_020_110 + am.count_020_111 - am.count_020_112) * am.balance
ELSE am.balance
END
) AS balance
FROM account_move_data am
GROUP BY 1
''',
aml_query=self._f29_get_aml_query(options, date_scope),
)
self.env.cr.execute(query)
grid_results = {res['grid']: res for res in self.env.cr.dictfetchall()}
final_results = {
'sii_code_111': '(110)',
'sii_code2_111': '(111)',
'count_rows_111': grid_results.get('111', {}).get('count_rows', 0),
'base_sum_111': grid_results.get('110', {}).get('balance', 0.0),
'tax_sum_111': grid_results.get('111', {}).get('balance', 0.0),
'sii_code_502': '(503)',
'sii_code2_502': '(502)',
'count_rows_502': grid_results.get('502', {}).get('count_rows', 0),
'base_sum_502': grid_results.get('503', {}).get('balance', 0.0),
'tax_sum_502': grid_results.get('502', {}).get('balance', 0.0),
'sii_code_510': '(509)',
'sii_code2_510': '(510)',
'count_rows_510': grid_results.get('510', {}).get('count_rows', 0),
'base_sum_510': grid_results.get('509', {}).get('balance', 0.0),
'tax_sum_510': grid_results.get('510', {}).get('balance', 0.0),
'sii_code_513': '(512)',
'sii_code2_513': '(513)',
'count_rows_513': grid_results.get('513', {}).get('count_rows', 0),
'base_sum_513': grid_results.get('512', {}).get('balance', 0.0),
'tax_sum_513': grid_results.get('513', {}).get('balance', 0.0),
'sii_code_520': '(519)',
'sii_code2_520': '(520)',
'count_rows_520': grid_results.get('520', {}).get('count_rows', 0),
'base_sum_520': grid_results.get('519', {}).get('balance', 0.0),
'tax_sum_520': grid_results.get('520', {}).get('balance', 0.0),
'sii_code_525': '(524)',
'sii_code2_525': '(525)',
'count_rows_525': grid_results.get('525', {}).get('count_rows', 0),
'base_sum_525': grid_results.get('524', {}).get('balance', 0.0),
'tax_sum_525': grid_results.get('525', {}).get('balance', 0.0),
'sii_code_528': '(527)',
'sii_code2_528': '(528)',
'count_rows_528': grid_results.get('528', {}).get('count_rows', 0),
'base_sum_528': grid_results.get('527', {}).get('balance', 0.0),
'tax_sum_528': grid_results.get('528', {}).get('balance', 0.0),
'sii_code_532': '(531)',
'sii_code2_532': '(532)',
'count_rows_532': grid_results.get('532', {}).get('count_rows', 0),
'base_sum_532': grid_results.get('531', {}).get('balance', 0.0),
'tax_sum_532': grid_results.get('532', {}).get('balance', 0.0),
'sii_code_762': '(761)',
'sii_code2_762': '(762)',
'count_rows_762': grid_results.get('762', {}).get('count_rows', 0),
'base_sum_762': grid_results.get('761', {}).get('balance', 0.0),
'tax_sum_762': grid_results.get('762', {}).get('balance', 0.0),
'sii_code_020': '(020)',
'sii_code2_020': None,
'count_rows_020': grid_results.get('020', {}).get('count_rows', 0),
'base_sum_020': grid_results.get('020', {}).get('balance', 0.0),
'tax_sum_020': None,
'sii_code_039': None,
'sii_code2_039': '(039)',
'count_rows_039': grid_results.get('039', {}).get('count_rows', 0),
'base_sum_039': None,
'tax_sum_039': grid_results.get('039', {}).get('balance', 0.0),
'sii_code_142': '(142)',
'sii_code2_142': None,
'count_rows_142': grid_results.get('142', {}).get('count_rows', 0),
'base_sum_142': grid_results.get('142', {}).get('balance', 0.0),
'tax_sum_142': None,
'sii_code_521': '(564)',
'sii_code2_521': '(521)',
'count_rows_521': grid_results.get('521', {}).get('count_rows', 0),
'base_sum_521': grid_results.get('564', {}).get('balance', 0.0),
'tax_sum_521': grid_results.get('521', {}).get('balance', 0.0),
'sii_code_535': '(534-536)',
'sii_code2_535': '(535-553)',
'count_rows_535': grid_results.get('535', {}).get('count_rows', 0),
'base_sum_535': grid_results.get('534', {}).get('balance', 0.0),
'tax_sum_535': grid_results.get('535', {}).get('balance', 0.0),
'sii_code_562': '(562)',
'sii_code2_562': None,
'count_rows_562': grid_results.get('562', {}).get('count_rows', 0),
'base_sum_562': grid_results.get('562', {}).get('balance', 0.0),
'tax_sum_562': None,
'sii_code_566': '(566)',
'sii_code2_566': None,
'count_rows_566': grid_results.get('566', {}).get('count_rows', 0),
'base_sum_566': grid_results.get('566', {}).get('balance', 0.0),
'tax_sum_566': None,
'sii_code_151': None,
'sii_code2_151': '(151)',
'count_rows_151': grid_results.get('151', {}).get('count_rows', 0),
'base_sum_151': None,
'tax_sum_151': grid_results.get('151', {}).get('balance', 0.0),
}
return final_results
def _report_custom_engine_doc_089_include(self, expressions, options, date_scope, current_groupby, next_groupby,
offset=0, limit=None, warnings=None):
expr_xmlids = (
'tax_report_iva_debito_fiscal_tag', 'tax_report_ventas_netas_gravadas_c_iva_tag',
'tax_report_compras_iva_recup_tag', 'tax_report_compras_netas_gr_iva_recup_tag',
'tax_report_compras_iva_activo_fijo_tag', 'tax_report_compras_activo_fijo_tag',
'tax_report_compras_iva_activo_fijo_uso_comun_tag', 'tax_report_compras_activo_fijo_uso_comun_tag',
'tax_report_compras_iva_activo_fijo_no_recup_tag', 'tax_report_compras_activo_fijo_no_recup_tag',
'tax_report_compras_iva_no_recup_tag', 'tax_report_compras_netas_gr_iva_no_recuperable_tag',
'tax_report_compras_iva_uso_comun_tag', 'tax_report_compras_netas_gr_iva_uso_comun_tag',
'tax_report_compras_iva_supermercado_tag', 'tax_report_compras_supermercado_tag',
)
report_expr = self.env['account.report.expression']
for xml_id in expr_xmlids:
report_expr |= self.env.ref(f'l10n_cl.{xml_id}')
report = self.env['account.report'].browse(options['report_id'])
query = report._get_report_query(options, date_scope or 'strict', Domain([
('move_id.move_type', 'in', ('in_invoice', 'in_refund', 'out_invoice', 'out_refund')),
('account_type', '!=', 'liability_payable'),
('tax_tag_ids', 'in', report_expr._get_matching_tags().ids),
]))
result = self.env['account.move.line']._read_group([('id', 'in', query)], [], ['balance:sum'])
tax_sum = result[0][0] if result else 0.0
return {
'include_vat': 1 if tax_sum < 0 else 0,
'C089': '(089)',
}
def _report_custom_engine_code(self, expressions, options, date_scope, current_groupby, next_groupby, offset=0,
limit=None, warnings=None):
"""Map SII codes for F29 report."""
codes = {
'C048': '(048)', # Workers' Tax
'C049': '(049)', # Intermediate value
'C062': '(062)', # PPM
'C077': '(077)', # Remaining Tax Credit
'C115': '(115)', # PPM Rate
'C151': '(151)', # Withholding Fees
'C514': '(514)', # Intermediate value
'C529': '(529)', # Intermediate value
'C535': '(535-553)', # Imports
'C537': '(537)', # VAT Credit
'C547': '(547)', # Total Taxes
'C538': '(538)', # VAT Debit
'C563': '(563)', # Intermediate value
'C755': '(755)', # VAT Postponement
'C596': '(596)', # withholding subject change
}
return codes