diff --git a/l10n_ar_account_tax_settlement/models/account_journal.py b/l10n_ar_account_tax_settlement/models/account_journal.py index e97bbe1e..41fca19d 100644 --- a/l10n_ar_account_tax_settlement/models/account_journal.py +++ b/l10n_ar_account_tax_settlement/models/account_journal.py @@ -836,7 +836,7 @@ def iibb_aplicado_sircar_files_values(self, move_lines): line_nbr = 1 for line in move_lines.filtered('payment_id'): alicuot_line = line.tax_line_id.get_partner_alicuot( - line.partner_id, line.date) + line.partner_id, line.date, line) if not alicuot_line: raise ValidationError(_( 'No hay alicuota configurada en el partner ' diff --git a/l10n_ar_account_tax_settlement_mendoza/__init__.py b/l10n_ar_account_tax_settlement_mendoza/__init__.py new file mode 100644 index 00000000..aee8895e --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/l10n_ar_account_tax_settlement_mendoza/__manifest__.py b/l10n_ar_account_tax_settlement_mendoza/__manifest__.py new file mode 100644 index 00000000..3bd94e44 --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/__manifest__.py @@ -0,0 +1,21 @@ +{ + 'name': 'Tax settlement Mendoza', + 'version': "16.0.1.0.0", + 'category': 'Accounting', + 'author': 'ADHOC SA', + 'license': 'LGPL-3', + 'depends': [ + 'l10n_ar_account_tax_settlement', + 'l10n_ar_withholding_ux', + #'base_import_match', + ], + 'data': [ + 'views/account_move_views.xml', + 'views/afip_activity_view.xml', + 'views/account_payment_view.xml', + 'wizard/res_config_settings_views.xml', + ], + 'installable': False, + 'auto_install': False, + 'application': False, +} diff --git a/l10n_ar_account_tax_settlement_mendoza/models/__init__.py b/l10n_ar_account_tax_settlement_mendoza/models/__init__.py new file mode 100644 index 00000000..c037b5a6 --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/models/__init__.py @@ -0,0 +1,6 @@ +from . import account_move +from . import afip_activity +from . import res_company +from . import account_payment +from . import account_tax +from . import account_journal diff --git a/l10n_ar_account_tax_settlement_mendoza/models/account_journal.py b/l10n_ar_account_tax_settlement_mendoza/models/account_journal.py new file mode 100644 index 00000000..a344b1a4 --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/models/account_journal.py @@ -0,0 +1,27 @@ +from odoo import models, _ +from odoo.exceptions import RedirectWarning + + +class AccountJournal(models.Model): + _inherit = 'account.journal' + + def iibb_aplicado_sircar_files_values(self, move_lines): + """ Extendemos este método del original de l10n_ar_account_tax_settlement para mendoza. El objetivo de este método es validar que el impuesto de mendoza tenga código de régimen. + """ + tax_group_id_mendoza_id = self.env.ref('l10n_ar_ux.tax_group_retencion_iibb_za') + mendoza_lines = move_lines.filtered(lambda x: x.payment_id and x.tax_line_id.withholding_type == 'code' and x.tax_group_id == tax_group_id_mendoza_id) + missing_codigo_regimen = mendoza_lines.filtered(lambda x: not x.payment_id.tax_withholding_id.codigo_regimen) + if mendoza_lines and missing_codigo_regimen: + raise RedirectWarning( + message=_("El impuesto '%s' not tiene código de regimen en solapa 'Opciones avanzadas' campo 'Codigo de regimen'.", missing_codigo_regimen.payment_id.tax_withholding_id.name), + action={ + 'type': 'ir.actions.act_window', + 'res_model': 'account.tax', + 'views': [(False, 'form')], + 'res_id': mendoza_lines.tax_line_id.id, + 'name': _('Tax'), + 'view_mode': 'form', + }, + button_text=_('Editar Impuesto'), + ) + return super().iibb_aplicado_sircar_files_values(move_lines) diff --git a/l10n_ar_account_tax_settlement_mendoza/models/account_move.py b/l10n_ar_account_tax_settlement_mendoza/models/account_move.py new file mode 100644 index 00000000..0812e111 --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/models/account_move.py @@ -0,0 +1,15 @@ +from odoo import models, fields + + +class AccountMove(models.Model): + _inherit = 'account.move' + + actividades_padron = fields.Many2many( + 'afip.activity', + related='partner_id.actividades_padron', + ) + activities_mendoza_ids = fields.Many2many( + comodel_name='afip.activity', + string="Activities in Mendoza", + domain="[('id', 'in', actividades_padron)]" + ) diff --git a/l10n_ar_account_tax_settlement_mendoza/models/account_payment.py b/l10n_ar_account_tax_settlement_mendoza/models/account_payment.py new file mode 100644 index 00000000..3c6491fb --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/models/account_payment.py @@ -0,0 +1,22 @@ +from odoo import models, fields +from odoo.exceptions import ValidationError + + +class AccountPayment(models.Model): + + _inherit = "account.payment" + + # IMPORTANTE: alicuota_mendoza se guarda al momento de correr el código python del impuesto 'Retención IIBB Mendoza Aplicada' --> payment.write({'alicuota_mendoza': alicuota}). Ver por interfaz. + alicuota_mendoza = fields.Float(help="Guardamos la alícuota para el txt de mendoza.", readonly=True) + + def compute_withholdings(self): + """ Para el cálculo de retenciones automáticas de aplicadas de Mendoza siempre tiene que haber una factura vinculada al payment. Además debemos guardar en el payment de mendoza la alícuota aplicada. """ + res = super().compute_withholdings() + tax_group_mendoza_id = self.env.ref('l10n_ar.tax_group_withholding_vat').id + payment_mendoza = self.payment_ids.filtered(lambda x: x.tax_withholding_id.tax_group_id.id == tax_group_mendoza_id and x.tax_withholding_id.withholding_type == 'code' and x.state == 'draft') + if payment_mendoza: + if not self.to_pay_move_line_ids: + raise ValidationError('No puede calcular retenciones automáticas de aplicadas de Mendoza si no seleccionó una factura para pagar') + # Agregamos la alícuota de mendoza al payment (es necesario para generar el txt iibb_aplicado_sircar_files_values) + payment_mendoza.alicuota_mendoza = self.alicuota_mendoza + return res diff --git a/l10n_ar_account_tax_settlement_mendoza/models/account_tax.py b/l10n_ar_account_tax_settlement_mendoza/models/account_tax.py new file mode 100644 index 00000000..7a36befc --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/models/account_tax.py @@ -0,0 +1,13 @@ +from odoo import models + + +class AccountTax(models.Model): + _inherit = "account.tax" + + def get_partner_alicuot(self, partner, date, line=None): + """ La alícuota para el archivo txt de mendoza que se genera desde el método iibb_aplicado_sircar_files_values + no se obtiene del partner sino que se obtiene del payment, y el código de régimen se obtiene del impuesto pero + extendemos el método get_partner_alicuot original para usarlo como puente, agregamos 'line' como parámentro. """ + if line and line.payment_id and line.payment_id.alicuota_mendoza and line.payment_id.tax_withholding_id.codigo_regimen: + return self.env['res.partner.arba_alicuot'].new({'alicuota_retencion': line.payment_id.alicuota_mendoza * 100, 'partner_id': partner, 'regimen_retencion': line.payment_id.tax_withholding_id.codigo_regimen}) + return super().get_partner_alicuot(partner, date, line=line) diff --git a/l10n_ar_account_tax_settlement_mendoza/models/afip_activity.py b/l10n_ar_account_tax_settlement_mendoza/models/afip_activity.py new file mode 100644 index 00000000..79284f1d --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/models/afip_activity.py @@ -0,0 +1,35 @@ +from odoo import models, fields +from odoo.exceptions import UserError + + +class AfipActivity(models.Model): + _inherit = "afip.activity" + + alicuota_general = fields.Char() + posee_tasa_cero = fields.Char() + no_posee_certificado_tasa_cero = fields.Char() + + def menor_alicuota(self, actividades_con_alicuota_cero): + """Método utilizado para el cálculo de la menor alícuota a aplicar para 'Retención IIBB Mendoza Aplicada'.""" + aliquots = [] + activity_codes = self.mapped('code') + activities = self.env['afip.activity'].search([('code', 'in', activity_codes)]) + activities_with_aliquots = activities.filtered(lambda x: x.alicuota_general or x.posee_tasa_cero or x.no_posee_certificado_tasa_cero) + if activities and not activities_with_aliquots: + raise UserError('No hay actividades con alícuotas') + actividades_con_alic = activities_with_aliquots.mapped('code') + elementos_no_en_ambas = [activity for activity in activity_codes if activity not in actividades_con_alic] + if elementos_no_en_ambas: + raise UserError('Hay actividades en la factura que no tienen alícuota. Actividades: %s' % (','.join(elementos_no_en_ambas))) + for code in activities_with_aliquots: + if code.alicuota_general: + aliquots.append((code.code, float(code.alicuota_general))) + elif code.posee_tasa_cero and code.code in actividades_con_alicuota_cero: + aliquots.append((code.code, float(code.posee_tasa_cero))) + elif code.no_posee_certificado_tasa_cero: + aliquots.append((code.code, float(code.no_posee_certificado_tasa_cero))) + if not aliquots: + raise UserError('Las actividades incluidas en la factura no tienen alícuota') + # Busco la menor alícuota + min_aliquot = min(aliquots, key=lambda x: x[1]) + return min_aliquot diff --git a/l10n_ar_account_tax_settlement_mendoza/models/res_company.py b/l10n_ar_account_tax_settlement_mendoza/models/res_company.py new file mode 100644 index 00000000..7aeea187 --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/models/res_company.py @@ -0,0 +1,38 @@ +from odoo import models, fields, api +import base64 +import csv +from io import StringIO +from odoo.exceptions import UserError + + +class ResCompany(models.Model): + _inherit = "res.company" + + riesgo_fiscal_csv_file = fields.Binary(string="CSV Riesgo Fiscal") + riesgo_fiscal_csv_file_last_update = fields.Datetime( + readonly=True, + ) + + @api.model + def write(self, vals): + if 'riesgo_fiscal_csv_file' in vals: + vals['riesgo_fiscal_csv_file_last_update'] = fields.Datetime.now() + return super(ResCompany, self).write(vals) + + def process_mendoza_csv_file(self, partner_vat, activity_codes): + if self.riesgo_fiscal_csv_file: + # Decode the base64 file content and parse the CSV + csv_content = base64.b64decode(self.riesgo_fiscal_csv_file) + csv_data = StringIO(csv_content.decode('utf-8')) + reader = csv.reader(csv_data, delimiter=';') + actividades_con_riesgo = [] + actividades_con_alicuota_cero = [] + # Process each row in the CSV + for row in reader: + if partner_vat == row[0] and (row[3] in activity_codes): + if row[6] == 'A': + actividades_con_alicuota_cero.append(row[3]) + if row[7] == 'S': + actividades_con_riesgo.append(row[3]) + return actividades_con_riesgo, actividades_con_alicuota_cero + raise UserError('Debe subir el archivo de riesgo fiscal en la sección de ajustes de contabilidad para calcular la retención automática de Mendoza.') diff --git a/l10n_ar_account_tax_settlement_mendoza/views/account_move_views.xml b/l10n_ar_account_tax_settlement_mendoza/views/account_move_views.xml new file mode 100644 index 00000000..2b2a0352 --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/views/account_move_views.xml @@ -0,0 +1,14 @@ + + + + account.move.form + account.move + + + + + + + + + diff --git a/l10n_ar_account_tax_settlement_mendoza/views/account_payment_view.xml b/l10n_ar_account_tax_settlement_mendoza/views/account_payment_view.xml new file mode 100644 index 00000000..287a6bb2 --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/views/account_payment_view.xml @@ -0,0 +1,13 @@ + + + + account.payment.form + account.payment + + + + + + + + diff --git a/l10n_ar_account_tax_settlement_mendoza/views/afip_activity_view.xml b/l10n_ar_account_tax_settlement_mendoza/views/afip_activity_view.xml new file mode 100644 index 00000000..09d878b8 --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/views/afip_activity_view.xml @@ -0,0 +1,30 @@ + + + + + afip.activity.form + afip.activity + + + + + + + + + + + + afip.activity.tree + afip.activity + + + + + + + + + + + diff --git a/l10n_ar_account_tax_settlement_mendoza/wizard/__init__.py b/l10n_ar_account_tax_settlement_mendoza/wizard/__init__.py new file mode 100644 index 00000000..e5010bbc --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/wizard/__init__.py @@ -0,0 +1,5 @@ +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from . import res_config_settings diff --git a/l10n_ar_account_tax_settlement_mendoza/wizard/res_config_settings.py b/l10n_ar_account_tax_settlement_mendoza/wizard/res_config_settings.py new file mode 100644 index 00000000..5d145aa1 --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/wizard/res_config_settings.py @@ -0,0 +1,12 @@ +from odoo import models, fields + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + riesgo_fiscal_csv_file = fields.Binary( + related='company_id.riesgo_fiscal_csv_file', + readonly=False, + ) + riesgo_fiscal_csv_file_last_update = fields.Datetime( + related='company_id.riesgo_fiscal_csv_file_last_update', + ) diff --git a/l10n_ar_account_tax_settlement_mendoza/wizard/res_config_settings_views.xml b/l10n_ar_account_tax_settlement_mendoza/wizard/res_config_settings_views.xml new file mode 100644 index 00000000..fa61c86d --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/wizard/res_config_settings_views.xml @@ -0,0 +1,33 @@ + + + + + res.config.settings.view.form.inherit.l10n_ar_afip_ws + res.config.settings + + + +
+
+
+
+
+ + + + + diff --git a/l10n_ar_account_tax_settlement_mendoza/wizards/__init__.py b/l10n_ar_account_tax_settlement_mendoza/wizards/__init__.py new file mode 100644 index 00000000..e5010bbc --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/wizards/__init__.py @@ -0,0 +1,5 @@ +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from . import res_config_settings diff --git a/l10n_ar_account_tax_settlement_mendoza/wizards/res_config_settings.py b/l10n_ar_account_tax_settlement_mendoza/wizards/res_config_settings.py new file mode 100644 index 00000000..5d145aa1 --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/wizards/res_config_settings.py @@ -0,0 +1,12 @@ +from odoo import models, fields + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + riesgo_fiscal_csv_file = fields.Binary( + related='company_id.riesgo_fiscal_csv_file', + readonly=False, + ) + riesgo_fiscal_csv_file_last_update = fields.Datetime( + related='company_id.riesgo_fiscal_csv_file_last_update', + ) diff --git a/l10n_ar_account_tax_settlement_mendoza/wizards/res_config_settings_views.xml b/l10n_ar_account_tax_settlement_mendoza/wizards/res_config_settings_views.xml new file mode 100644 index 00000000..ad45644d --- /dev/null +++ b/l10n_ar_account_tax_settlement_mendoza/wizards/res_config_settings_views.xml @@ -0,0 +1,33 @@ + + + + + res.config.settings.view.form.inherit.l10n_ar_afip_ws + res.config.settings + + +
+
+
+
+
+
+
+ + + +