From 461338e188f29e193eea76d910aac6331012e047 Mon Sep 17 00:00:00 2001 From: Katherine Zaoral Date: Wed, 31 Jul 2024 09:01:09 -0300 Subject: [PATCH 1/4] [MIG] l10n_uy_edi_stock: version 17.0 - cosas que movimos de l10n_uy_edi que nos pidieron quitaramos - adaptando modelo cfe, y trayendo tolo que no nos aceptaron adaptandolo para que funcione como un modelo normal y no un mixin - get dgi state button - update methods and field renamed - move stuff from l10n uy edi official - UY transport - add constraint not sure yet - place of delivery boolean - TipoTraslado - move template to views - replace fields --- l10n_uy_edi_stock/__manifest__.py | 7 +- .../data/l10n_latam.document.type.csv | 11 +- l10n_uy_edi_stock/models/__init__.py | 3 + .../models/l10n_latam_document_type.py | 8 + l10n_uy_edi_stock/models/l10n_uy_addenda.py | 9 + .../models/l10n_uy_edi_document.py | 215 ++++++++++++++++++ l10n_uy_edi_stock/models/stock_picking.py | 154 +++++++++++-- l10n_uy_edi_stock/views/cfe_template.xml | 22 ++ .../views/stock_picking_views.xml | 29 +-- 9 files changed, 419 insertions(+), 39 deletions(-) create mode 100644 l10n_uy_edi_stock/models/l10n_latam_document_type.py create mode 100644 l10n_uy_edi_stock/models/l10n_uy_addenda.py create mode 100644 l10n_uy_edi_stock/models/l10n_uy_edi_document.py create mode 100644 l10n_uy_edi_stock/views/cfe_template.xml diff --git a/l10n_uy_edi_stock/__manifest__.py b/l10n_uy_edi_stock/__manifest__.py index a8bbf524..db778b59 100644 --- a/l10n_uy_edi_stock/__manifest__.py +++ b/l10n_uy_edi_stock/__manifest__.py @@ -1,20 +1,21 @@ { "name": """Uruguay - E-Remitos""", - 'version': "16.0.1.1.0", + 'version': "17.0.1.0.0", 'category': 'Accounting/Localizations/EDI', 'sequence': 12, 'author': 'Adhoc', - 'description': """ + 'summary': """ Este modulo permite a los usuarios hacer e-remitos en el sistemas que son reportados a la DGI """, 'depends': [ - 'l10n_uy_edi', + 'l10n_uy_edi_ux', # TODO no recuerdo. porque no directo l10n_uy_ux 'stock_account', 'sale_stock', ], 'data': [ 'data/l10n_latam.document.type.csv', + 'views/cfe_template.xml', 'views/stock_picking_views.xml', ], 'installable': False, diff --git a/l10n_uy_edi_stock/data/l10n_latam.document.type.csv b/l10n_uy_edi_stock/data/l10n_latam.document.type.csv index 0caefa5c..317ae420 100644 --- a/l10n_uy_edi_stock/data/l10n_latam.document.type.csv +++ b/l10n_uy_edi_stock/data/l10n_latam.document.type.csv @@ -1,5 +1,6 @@ -id,active -l10n_uy_account.dc_e_remito_expo,False -l10n_uy_account.dc_e_remito,True -l10n_uy_account.dc_e_remito_de_exportación_contingencia,False -l10n_uy_account.dc_ e_remito_contingencia,False +"id","internal_type" +"l10n_uy.dc_remito","stock_picking" +"l10n_uy.dc_e_remito_expo","stock_picking" +"l10n_uy.dc_e_remito","stock_picking" +"l10n_uy.dc_e_remito_de_exportación_contingencia","stock_picking" +"l10n_uy.dc_e_remito_contingencia","stock_picking" diff --git a/l10n_uy_edi_stock/models/__init__.py b/l10n_uy_edi_stock/models/__init__.py index ae4c2722..152f9ddd 100644 --- a/l10n_uy_edi_stock/models/__init__.py +++ b/l10n_uy_edi_stock/models/__init__.py @@ -1 +1,4 @@ from . import stock_picking +from . import l10n_latam_document_type +from . import l10n_uy_addenda +from . import l10n_uy_edi_document diff --git a/l10n_uy_edi_stock/models/l10n_latam_document_type.py b/l10n_uy_edi_stock/models/l10n_latam_document_type.py new file mode 100644 index 00000000..c1f79822 --- /dev/null +++ b/l10n_uy_edi_stock/models/l10n_latam_document_type.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class L10nAccountDocumentType(models.Model): + + _inherit = 'l10n_latam.document.type' + + internal_type = fields.Selection(selection_add=[('stock_picking', 'Remito')]) diff --git a/l10n_uy_edi_stock/models/l10n_uy_addenda.py b/l10n_uy_edi_stock/models/l10n_uy_addenda.py new file mode 100644 index 00000000..5bd36018 --- /dev/null +++ b/l10n_uy_edi_stock/models/l10n_uy_addenda.py @@ -0,0 +1,9 @@ +from odoo import models, fields + + +class L10nUyAddenda(models.Model): + + _inherit = "l10n_uy_edi.addenda" + + apply_on = fields.Selection(selection_add=[ + ('stock.picking', 'Delivery Guide'), ('all', 'All CFE')]) diff --git a/l10n_uy_edi_stock/models/l10n_uy_edi_document.py b/l10n_uy_edi_stock/models/l10n_uy_edi_document.py new file mode 100644 index 00000000..4945e94d --- /dev/null +++ b/l10n_uy_edi_stock/models/l10n_uy_edi_document.py @@ -0,0 +1,215 @@ +from odoo import _, models +from odoo.exceptions import UserError +from odoo.tools.float_utils import float_repr + + +class L10nUyEdiDocument(models.Model): + + _inherit = 'l10n_uy_edi.document' + + def _is_uy_remito_exp(self): + return self.l10n_latam_document_type_id.code == '124' + + def _is_uy_remito_loc(self): + return self.l10n_latam_document_type_id.code == '181' + + + def _is_uy_remito_type_cfe(self): + return self.l10n_latam_document_type_id.internal_type in ['stock_picking'] + + def _uy_get_uuid(self): + """ Extend to properly return picking info + + Uruware UUID and also A4.1 NroInterno DGI field. Spec (V24) + Nº interno que referencia - ALFA50 - Sin validación al comprobante + """ + self.ensure_one() + if self.picking_id and not self.move_id: + res = self.picking_id._name + '-' + str(self.picking_id.id) + if self.company_id._l10n_uy_edi_get_environment_type() == 'testing': + res = 'sp' + str(self.picking_id.id) + '-' + self.env.cr.dbname + return res[:50] + return super()._uy_get_uuid() + + def _uy_cfe_B4_IndFact(self, line): + """ B4: Indicador de facturación + + Indica si el producto o servicio es exento, o a que tasa está gravado o si corresponde a un concepto no + facturable. + + Extendido para remitos + + En la docu de DGI dice N/A para e-remito y e-remito de exportación, excepto: + * usar indicador de facturación 8 si corresponde a un ítem a rebajar de otro remito ya emitido, + * usar indicador de facturación 5 si corresponde a un ítem con valor unitario igual a cero (sólo para e-remito de exportación). + + * Donde argumento line es: + * ser un stock.move para el caso de stock.picking + + move_ids_without_package Stock moves not in package (stock.move) + move_line_ids Operations (stock.move.line) + move_line_ids_without_package Operations without package (stock.move.line) + """ + # NOTE: By the moment, this is working for one VAT tax per move line. we should implement other cases. + self.ensure_one() + res = False + if len(line) != 1: + raise UserError(_('Only the Invoice Index can be calculated for each line')) + + if self._is_uy_remito_type_cfe(): + # Solo implementamos por los momentos en el N/A + res = False + + else: + return super()._uy_cfe_B4_IndFact() + + return {'IndFact': res} if res else {} + + def _uy_cfe_B8_DscItem(self, line): + """ B8 Descripcion Adicional del ítem. Maximo 1000 caracteres """ + if self._is_uy_inv_type_cfe(): + return super()._uy_cfe_B8_DscItem(line) + + self.ensure_one() + res = [] + for rec in line.l10n_uy_edi_addenda_ids: + res.append('{ %s }' % rec.content if rec.is_legend else rec.content) + + if self._is_uy_remito_type_cfe(): + res.append(line.description_picking) + res = '\n'.join(res) + self._uy_check_field_size('B8_DscItem', res, 1000) + + return {'DscItem': res} if res else {} + + def _uy_cfe_A_receptor(self): + """ XML Section A (Encabezado) """ + self.ensure_one() + cond_e_remito = self._is_uy_remito_type_cfe() + + if cond_e_remito and not all([self.partner_id.street, self.partner_id.city]): + raise UserError(_('You must configure at least the address and city of the receiver to be able to send this CFE')) + + res = super()._uy_cfe_A_receptor() + + # A130 Monto Total a Pagar (NO debe ser reportado si de tipo remito) + if self._is_uy_remito_type_cfe(): + res.pop('MntPagar') + + return res + + def _uy_cfe_C_totals(self): + self.ensure_one() + res = super()._uy_cfe_C_totals() + + # A110 Tipo moneda transacción: Informar para todos menos para e-Rem loc + if self._is_uy_remito_loc(): + res.pop('TpoMoneda') + + # A124 Total Monto Total (NUM 17) + # - Si tipo de CFE= 124 (e-remito de exportación), Valor numérico de 15 enteros y 2 decimales: + # - sino, Valor numérico de 15 enteros y 2 decimales, ≥0 : C124 = SUM(C112:C118) + SUM(C121:C123) + + # A111 Tipo de Cambio: Informar siempre que la moneda sea diferente al peso Uruguayo y no sea e-Rem Loc + if self._is_uy_remito_loc(): + res.pop('TpoCambio') + + return res + + def _uy_get_cfe_tag(self): + self.ensure_one() + if self._is_uy_remito_loc(): + return 'eRem' + elif self._is_uy_remito_exp(): + return 'eRem_Exp' + return super()._uy_get_cfe_tag() + + def _uy_cfe_B11_PrecioUnitario(self, line, IndFact): + """ B11: Precio Unitario. Valor numérico de 11 enteros y 6 decimales >0, excepto: + + * Si B-C4=5, B-C11 debe ser 0 + * Si B-C4=8, B-C11 puede ser ≥0 + Donde B4 es "Indicador de facturación" (IndFact) + + Required for all the documents except for e-Delivery Guide and e-Resguardo (does not apply) """ + self.ensure_one() + if self._is_uy_remito_exp(): + res = False + if IndFact == 5: + res = float_repr(0, 6) + elif IndFact == 8: + raise UserError(_( + "The use of billing indicator 8 is not implemented (Corresponds to an item to be reduced from another remittance already issued)")) + # NOTE: In order to implement we need to return the price unit of the product, but I was not able to find a direct relations between the aml and thesml. + else: + res = line.quantity_done + return {'PrecioUnitario': res} if res else {} + return super()._uy_cfe_B11_PrecioUnitario(line, IndFact) + + def _uy_cfe_B24_MontoItem(self, line): + """ B24: Monto Item. Valor por linea de detalle. Valor numérico de 15 enteros y 2 decimales + + - Debe ser cero cuando: C4=5 + - Calculo C24 = (B-C9 * B-C11) - B-C13 + B-C17 + + Donde: + B-C4: Indicador de Facturacion. 4 - Gravado a otra tasa/iva sobre fictos. + B-C9: Cantidad + B-C11 Precio unitario + B-C13 Monto Descuento + B-C17 Monto Recargo + + No corresponde para e-Rem pero es obligatorio para e-Rem Exp """ + self.ensure_one() + if not self._is_uy_remito_type_cfe(): + return super()._uy_cfe_B24_MontoItem(line) + + if self._is_uy_remito_exp(): + raise UserError(_('Export e-Delivery Guide')) + return False + + def _uy_cfe_A113_MntExpoyAsim(self, res): + """ Si tipo de CFE= 124 (e-remito de exportación): + * Suma de ítems del e-remito de exportación Menos Suma de ítems del e-remito de exportación con indicador de facturación (B- C4)=8 + * Sino, Suma de ítems de exportación y asimilados, menos descuentos globales más recargos globales (asignados a ítems de exportación + + Es de tipo NUM 17 - -Valor numérico de 15 enteros y 2 decimales + + * Es condicional para los docs regulares. + * No corresponde si es de tipo e-Rem + * es Obligatorio si es cualquier tipo de tipo Expo incluyendo e-Rem Exp. + + Si A-C2=124, C113= ∑B-C24 - ∑B-C24 (si B- C4=8), sino + C113= ∑ B-C24 (si B-C4=10) menos ∑ D-C6 (si D-C7=10 y si D- C2=D) más ∑ D-C6 (si D-C7=10 y si D- C2=R)) + """ + if self._is_uy_remito_exp(): + res.update({ + 'MntExpoyAsim': float_repr(self.move_id.amount_total, 2), + }) + return res + return super()._uy_cfe_A113_MntExpoyAsim(res) + + def _uy_cfe_A5_FchEmis(self): + self.ensure_one() + if self._is_uy_remito_type_cfe(): + return self.scheduled_date.strftime('%Y-%m-%d') + return super()._uy_cfe_A5_FchEmis() + + def _uy_cfe_B7_NomItem(self, line): + """ B7 Nombre del ítem (producto o servicio). Maximo 80 caracteres """ + self.ensure_one() + if self._is_uy_inv_type_cfe() or self._is_uy_remito_type_cfe(): + return super()._uy_cfe_B7_NomItem(line) + + def _uy_cfe_B10_UniMed(self, line): + if self._is_uy_remito_type_cfe(): + return line.product_uom.name[:4] if line.product_uom else 'N/A' + return super()._uy_cfe_B10_UniMed(line) + + def _uy_cfe_B9_Cantidad(self, line): + """ # B9 Cantidad. Valor numerico 14 enteros y 3 decimales. Puede ser numero negativo """ + if self._is_uy_remito_type_cfe(): + res = 0.0 + res = line.quantity_done + return float_repr(res, 3) + return super()._uy_cfe_B9_Cantidad(line) diff --git a/l10n_uy_edi_stock/models/stock_picking.py b/l10n_uy_edi_stock/models/stock_picking.py index 4a74a0e6..bca03f8a 100644 --- a/l10n_uy_edi_stock/models/stock_picking.py +++ b/l10n_uy_edi_stock/models/stock_picking.py @@ -1,21 +1,52 @@ -from os import uname from odoo import api, models, fields, _ +from odoo.tools import html2plaintext class StockPicking(models.Model): - _name = 'stock.picking' - _inherit = ['l10n.uy.cfe', 'stock.picking'] + _inherit = 'stock.picking' + l10n_uy_cfe_id = fields.Many2one("l10n_uy_edi.document", string="Uruguay E-Resguardo CFE", copy=False) l10n_latam_document_type_id = fields.Many2one('l10n_latam.document.type', string='Document Type (UY)', copy=False) l10n_latam_document_number = fields.Char(string='Document Number (UY)', readonly=True, states={'draft': [('readonly', False)]}, copy=False) + + # Fields that need to be fill before creating the CFE + l10n_uy_edi_cfe_uuid = fields.Char( + 'Key or UUID CFE', help="Unique identification per CFE in UCFE. Currently is formed by the concatenation of model name initials plust record id", copy=False) + l10n_uy_edi_addenda_ids = fields.Many2many( + 'l10n_uy_edi.addenda', string="Addenda & Disclosure", + domain="[('type', 'in', ['issuer', 'receiver', 'cfe_doc', 'addenda'])]") + l10n_latam_available_document_type_ids = fields.Many2many('l10n_latam.document.type', compute='_compute_l10n_latam_available_document_types') l10n_uy_transfer_of_goods = fields.Selection( - [('1', 'Venta'), - ('2', 'Traslados internos')], + [('1', 'Venta'), ('2', 'Traslados internos')], string="Traslados de Bienes", ) + l10n_uy_cfe_sale_mod = fields.Selection([ + ('1', 'General Regime'), + ('2', 'Consignment'), + ('3', 'Reviewable Price'), + ('4', 'Own goods to customs exclaves'), + ('90', 'General Regime - exportation of services'), + ('99', 'Other transactions'), + ], 'Sales Modality', help="This field is used in the XML to create an Export e-Delivery Guide") + l10n_uy_cfe_transport_route = fields.Selection([ + ('1', 'Maritime'), + ('2', 'Air'), + ('3', 'Ground'), + ('8', 'N/A'), + ('9', 'Other'), + ], 'Transportation Route', help="This field is used in the XML to create an Export e-Delivery Guide") + l10n_uy_place_of_delivery = fields.Char( + "Place of Delivery", + size=100, + help="Indicación de donde se entrega la mercadería o se presta el servicio (Dirección, Sucursal, Puerto, etc,)") + l10n_uy_edi_place_of_delivery = fields.Boolean( + "Place of Delivery", + help="CFE: Indication of where the merchandise is delivered or the service is provided" + " (Address, Branch, Port, etc.) if True then we will inform the shipping address's name and street") + def name_get(self): """ Display: 'Stock Picking Internal Sequence : Remito (if defined)' """ res = [] @@ -48,7 +79,7 @@ def _get_l10n_latam_documents_domain(self): def action_cancel(self): # The move cannot be modified once the CFE has been accepted by the DGI remitos = self.filtered(lambda x: x.country_code == 'UY' and x.picking_type_code == 'outgoing') - remitos.check_uy_state() + remitos._uy_check_state() return super().action_cancel() def uy_post_dgi_remito(self): @@ -63,14 +94,14 @@ def uy_post_dgi_remito(self): lambda x: x.country_code == 'UY' and x.picking_type_code == 'outgoing' and x.l10n_latam_document_type_id and int(x.l10n_latam_document_type_id.code) > 0 - and x.l10n_uy_ucfe_state not in x._uy_cfe_already_sent() + and x.l10n_uy_edi_cfe_state not in ['accepted', 'rejected', 'received'] ) # If the invoice was previosly validated in Uruware and need to be link to Odoo we check that the - # l10n_uy_cfe_uuid has been manually set and we consult to get the invoice information from Uruware - pre_validated_in_uruware = uy_remitos.filtered(lambda x: x.l10n_uy_cfe_uuid and not x.l10n_uy_cfe_file and not x.l10n_uy_cfe_state) + # l10n_uy_edi_cfe_uuid has been manually set and we consult to get the invoice information from Uruware + pre_validated_in_uruware = uy_remitos.filtered(lambda x: x.l10n_uy_edi_cfe_uuid and not x.l10n_uy_cfe_file and not x.l10n_uy_edi_cfe_state) if pre_validated_in_uruware: - pre_validated_in_uruware.action_l10n_uy_get_uruware_cfe() + pre_validated_in_uruware.uy_ux_action_get_uruware_cfe() uy_remitos = uy_remitos - pre_validated_in_uruware if not uy_remitos: @@ -78,12 +109,109 @@ def uy_post_dgi_remito(self): # Send invoices to DGI and get the return info for remito in uy_remitos: - if remito._is_dummy_dgi_validation(): - remito._dummy_dgi_validation() + if remito.company_id.l10n_uy_edi_ucfe_env == "demo": + remito._uy_dummy_validation() continue # TODO KZ I think we can avoid this loop. review - remito._l10n_uy_dgi_post() + remito._uy_dgi_post() # TODO KZ buscar el metodo _l10n_cl_get_tax_amounts para ejemplos de como extraer la info de los impuestos en un picking. viene siempre de una # factura + + def _uy_get_cfe_addenda(self): + """ Add Specific MOVE model fields to the CFE Addenda if they are set: + + * field Origin added with the prefix "Origin: ..." + * Observation + """ + self.ensure_one() + res = super()._uy_get_cfe_addenda() + if self.origin: + res += "\n\nOrigin: %s" % self.origin + if self.note: + res += "\n\n%s" % html2plaintext(self.note) + return res.strip() + + def _uy_get_cfe_lines(self): + self.ensure_one() + if self._is_uy_remito_type_cfe(): + # TODO KZ: Toca revisar realmente cual es el line que corresponde, el que veo en la interfaz parece ser move_ids_without_package pero no se si esto siempre aplica + + # move_ids_without_package Stock moves not in package (stock.move) + # move_line_ids Operations (stock.move.line) + # move_line_ids_without_package Operations without package (stock.move.line) + return self.move_ids_without_package + + def _l10n_uy_get_remito_codes(self): + """ return list of the available document type codes for uruguayan of stock picking""" + # self.ensure_one() + # if self.picking_type_code != 'outgoing': + # return [] + return ['0', '124', '181', '224', '281'] + + def l10n_uy_edi_action_get_dgi_state(self): + self.ensure_one() + self.l10n_uy_edi_cfe_id.l10n_uy_edi_action_get_dgi_state() + + # TODO KZ este metodo esta en el account.move. debemos de generarlo tal cual en stock picking + def _l10n_uy_edi_cfe_A_receptor(self): + # EXTEND l10n_uy_edi + """ Agregamos mas campos no obligatorios que no nos permitieron agregar en oficial """ + res = super()._l10n_uy_edi_cfe_A_receptor() + # A69 - LugarDestEnt + if self.l10n_uy_edi_place_of_delivery and not self._is_uy_resguardo(): + value = '' + delivery_address = self.partner_shipping_id + if delivery_address: + value = (delivery_address.name + ' ' + delivery_address.street)[:100] + res['LugarDestEnt'] = value + + def _uy_cfe_A_iddoc(self): + res = super()._uy_cfe_A_iddoc() + + return res + + def _l10n_uy_edi_cfe_A_iddoc(self): + res = self.env['account.move']._l10n_uy_edi_cfe_A_iddoc() + + if self._is_uy_remito_type_cfe(): # A6 + res.update({'TipoTraslado': self.l10n_uy_transfer_of_goods}) + + # TODO KZ A5 FchEmis - Fecha del Comprobante - + # ver que fecha deberiamos de usar en caso de ser picking. opciones + # scheduled_date - Scheduled Date + # date - Creation Date + # date_deadline - Deadline + # date_done - Date of Transfer + # return res + # . self.scheduled_date.strftime('%Y-%m-%d') + + res.update(self._l10n_uy_get_cfe_serie()) + + return res + + + # TODO KZ este metodo debemos adaptarlo para obtener el IndFact + def _uy_cfe_B4_IndFact(self, line): + """ B4: Indicador de facturación + + TODO KZ: Toca revisar realmente cual es el line que corresponde, el que veo en la interfaz parece ser move_ids_without_package pero no se si esto siempre aplica + move_ids_without_package Stock moves not in package (stock.move) + move_line_ids Operations (stock.move.line) + move_line_ids_without_package Operations without package (stock.move.line) + """ + # Another cases for future + # 4: Gravado a Otra Tasa/IVA sobre fictos + # 5: Entrega Gratuita. Por ejemplo docenas de trece + # 6: Producto o servicio no facturable. No existe validación, excepto si A-C20= 1, B-C4=6 o 7. + # 7: Producto o servicio no facturable negativo. . No existe validación, excepto si A-C20= 1, B-C4=6 o 7. + # 8: Sólo para remitos: Ítem a rebajar en e-remitos y en e- remitos de exportación. En área de referencia se debe indicar el N° de remito que ajusta + # 9: Sólo para resguardos: Ítem a anular en resguardos. En área de referencia se debe indicar el N° de resguardo que anular + # 11: Impuesto percibido + # 12: IVA en suspenso + # 13: Sólo para e-Boleta de entrada y sus notas de corrección: Ítem vendido por un no contribuyente (valida que A-C60≠2) + # 14: Sólo para e-Boleta de entrada y sus notas de corrección: Ítem vendido por un contribuyente IVA mínimo, Monotributo o Monotributo MIDES (valida que A-C60=2) + # 15: Sólo para e-Boleta de entrada y sus notas de corrección: Ítem vendido por un contribuyente IMEBA (valida A-C60 = 2) + # 16: Sólo para ítems vendidos por contribuyentes con obligación IVA mínimo, Monotributo o Monotributo MIDES. Si A-C10=3, no puede utilizar indicadores 1, 2, 3, 4, 11 ni 12 + return super()._uy_cfe_B4_IndFact(line) diff --git a/l10n_uy_edi_stock/views/cfe_template.xml b/l10n_uy_edi_stock/views/cfe_template.xml new file mode 100644 index 00000000..8a77ecf1 --- /dev/null +++ b/l10n_uy_edi_stock/views/cfe_template.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/l10n_uy_edi_stock/views/stock_picking_views.xml b/l10n_uy_edi_stock/views/stock_picking_views.xml index d0448d23..a75f3ed3 100644 --- a/l10n_uy_edi_stock/views/stock_picking_views.xml +++ b/l10n_uy_edi_stock/views/stock_picking_views.xml @@ -18,17 +18,16 @@ '|', '&', '&', ('l10n_latam_document_number', '=', False), ('l10n_uy_is_cfe', '=', True), ('l10n_latam_document_type_id', '!=', False), ('l10n_latam_document_type_id', '=', False) ], 'required': [('l10n_uy_is_cfe', '=', False), ('l10n_latam_document_type_id', '!=', False)]}"/> - - + -