diff --git a/l10n_ar_txt_tucuman/README.rst b/l10n_ar_txt_tucuman/README.rst new file mode 100644 index 000000000..ed558acd9 --- /dev/null +++ b/l10n_ar_txt_tucuman/README.rst @@ -0,0 +1,61 @@ +.. |company| replace:: ADHOC SA + +.. |company_logo| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-logo.png + :alt: ADHOC SA + :target: https://www.adhoc.com.ar + +.. |icon| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-icon.png + +.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: https://www.gnu.org/licenses/agpl + :alt: License: AGPL-3 + +============================= +Tax Settlements For Tucuman +============================= + +Este módulo imlementa: + +* Descargar TXT Tucuman de retenciones y percepciones aplicadas. + + +Installation +============ + +To install this module, you need to: + +#. Only need to install the module + +Configuration +============= + +To configure this module, you need to: + +#. Nothing to configure + +Usage +===== + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: http://runbot.adhoc.com.ar/ + +Credits +======= + +Images +------ + +* |company| |icon| + +Contributors +------------ + +Maintainer +---------- + +|company_logo| + +This module is maintained by the |company|. + +To contribute to this module, please visit https://www.adhoc.com.ar. diff --git a/l10n_ar_txt_tucuman/__init__.py b/l10n_ar_txt_tucuman/__init__.py new file mode 100644 index 000000000..d03377692 --- /dev/null +++ b/l10n_ar_txt_tucuman/__init__.py @@ -0,0 +1,5 @@ +############################################################################## +# For copyright and license notices, see __manifest__.py file in module root +# directory +############################################################################## +from . import models diff --git a/l10n_ar_txt_tucuman/__manifest__.py b/l10n_ar_txt_tucuman/__manifest__.py new file mode 100644 index 000000000..91cd0a023 --- /dev/null +++ b/l10n_ar_txt_tucuman/__manifest__.py @@ -0,0 +1,21 @@ +{ + 'name': 'Txt Tucuman', + 'version': "16.0.1.0.0", + 'category': 'Accounting', + 'website': 'www.adhoc.com.ar', + 'license': 'LGPL-3', + 'images': [ + ], + 'depends': [ + 'l10n_ar_account_tax_settlement', + ], + 'data': [ + ], + 'demo': [ + ], + 'test': [ + ], + 'installable': True, + 'auto_install': False, + 'application': False, +} diff --git a/l10n_ar_txt_tucuman/doc/MRETPER6R2.pdf b/l10n_ar_txt_tucuman/doc/MRETPER6R2.pdf new file mode 100644 index 000000000..3fd126bcd Binary files /dev/null and b/l10n_ar_txt_tucuman/doc/MRETPER6R2.pdf differ diff --git a/l10n_ar_txt_tucuman/models/__init__.py b/l10n_ar_txt_tucuman/models/__init__.py new file mode 100644 index 000000000..2388e1196 --- /dev/null +++ b/l10n_ar_txt_tucuman/models/__init__.py @@ -0,0 +1 @@ +from . import account_journal diff --git a/l10n_ar_txt_tucuman/models/account_journal.py b/l10n_ar_txt_tucuman/models/account_journal.py new file mode 100644 index 000000000..348132989 --- /dev/null +++ b/l10n_ar_txt_tucuman/models/account_journal.py @@ -0,0 +1,99 @@ +from odoo import models, fields +from odoo.exceptions import ValidationError +from odoo.addons.l10n_ar_account_tax_settlement.models.account_journal import get_pos_and_number + + +class AccountJournal(models.Model): + _inherit = 'account.journal' + + settlement_tax = fields.Selection(selection_add=[ + ('iibb_tucuman', 'TXT Retenciones/Percepciones Tucuman') + ]) + + def iibb_tucuman_files_values(self, move_lines): + """ Implementado segun especificación indicada en tarea 38200. Ver especificación también en l10n_ar_txt_tucuman/doc/MRETPER6R2.pdf a partir de la página 12. """ + self.ensure_one() + content_datos = '' + content_retper = '' + content_ncfact = '' + + if nc_without_reversed_entry_id := move_lines.filtered(lambda x: x.move_type == 'out_refund' and not x.move_id.reversed_entry_id): + raise ValidationError(f"Algunos comprobantes rectificativos no contienen información de que comprobante original están revirtiendo: %s" % (", ".join(nc_without_reversed_entry_id.mapped('move_id.name')))) + if moves_without_street_city_state := move_lines.filtered(lambda x: not x.partner_id.street or not x.partner_id.city or not x.partner_id.state_id or not x.partner_id.zip): + raise ValidationError(f"Algunos comprobantes no contienen información acerca de la calle/ciudad/provincia/cod postal del contacto: %s" % (", ".join(moves_without_street_city_state.mapped('move_id.name')))) + + # Percepciones + for line in move_lines.sorted(key=lambda r: (r.date, r.id)): + # Archivo DATOS.TXT + # FECHA, longitud: 8. Formato AAAAMMDD + content_datos += fields.Date.from_string(line.date).strftime('%Y%m%d') + # TIPODOC, longitud: 2 + content_datos += line.partner_id.l10n_latam_identification_type_id.l10n_ar_afip_code + # DOCUMENTO, longitud: 11 + content_datos += line.partner_id.l10n_ar_vat + # TIPO COMP, longitud: 2 + # 99 para retenciones por el ejemplo que pasó en el archivo adjunto el cliente en la tareas + content_datos += line.move_id.l10n_latam_document_type_id.code.zfill(2) if line.l10n_latam_document_type_id.internal_type == 'invoice' else '99' + # LETRA, longitud: 1 + content_datos += line.move_id.l10n_latam_document_type_id.l10n_ar_letter if line.l10n_latam_document_type_id.internal_type == 'invoice' else ' ' + # COD. LUGAR EMISION, longitud: 4 + pos, number = get_pos_and_number(line.move_id.l10n_latam_document_number) + content_datos += pos[-4:] + # NUMERO, longitud: 8 + content_datos += number + # BASE_CALCULO, longitud: 15,2 + content_datos += '%015.2f' % (line.tax_base_amount if line.l10n_latam_document_type_id.internal_type == 'invoice' else line.payment_id.withholding_base_amount) + # PORCENTAJE/ALICUOTA, longitud: 6,3 + content_datos += '%06.3f' % line.tax_line_id.get_partner_alicuot(line.partner_id, line.date).alicuota_percepcion + # MONTO_RET/PER, longitud: 15,2 + content_datos += '%015.2f' % abs(line.balance) + content_datos += '\r\n' + + # Archivo RETPER.TXT + # TIPODOC, longitud: 2 + content_retper += line.partner_id.l10n_latam_identification_type_id.l10n_ar_afip_code + # DOCUMENTO, longitud: 11 + content_retper += line.partner_id.l10n_ar_vat + # NOMBRE, longitud: 40 + content_retper += line.partner_id.name[:40].ljust(40) + # DOMICILIO, longitud: 40 + content_retper += line.partner_id.street[:40].ljust(40) + # Nro, longitud: 5 + # Hacemos '9' * 5 por el ejemplo que pasó en el archivo adjunto el cliente en la tarea + content_retper += '9' * 5 + # LOCALIDAD, longitud: 15 + content_retper += line.partner_id.city[:15].ljust(15) + # PROVINCIA, longitud: 15 + content_retper += line.partner_id.state_id.name[:15].ljust(15) + # NO USADO, longitud 11 + content_retper += ' ' * 11 + # C. POSTAL, longitud: 8 + content_retper += '%8s' % line.partner_id.zip + content_retper += '\r\n' + + # Archivo NCFACT.TXT + # COD. LUGAR EMISION NC, longitud: 4 + if line.move_type == 'out_refund': + pos_nc, number_nc = get_pos_and_number(line.move_id.l10n_latam_document_number) + content_ncfact += pos_nc[-4:] + # NUMERO NV, longitud: 8 + content_ncfact += number_nc + # COD LUGAR EMISION FAC, longitud: 4 + pos, number = get_pos_and_number(line.move_id.reversed_entry_id.l10n_latam_document_number) + content_ncfact += pos[-4:] + # NUMERO FAC, longitud: 8 + content_ncfact += number + # TIPO FAC, longitud: 2 + content_ncfact += line.move_id.reversed_entry_id.l10n_latam_document_type_id.code.zfill(2) + content_ncfact += '\r\n' + + return [{ + 'txt_filename': 'DATOS.txt', + 'txt_content': content_datos, + }, + {'txt_filename': 'RETPER.txt', + 'txt_content': content_retper, + }, + {'txt_filename': 'NCFACT.TXT', + 'txt_content': content_ncfact, + }]