Skip to content

Commit 9660311

Browse files
zaoralmem-adhoc
authored andcommitted
wip3 limpieza
1 parent aa03969 commit 9660311

6 files changed

+168
-159
lines changed

l10n_uy_edi_stock/__manifest__.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
'sequence': 12,
77
'author': 'Adhoc',
88
'depends': [
9-
'l10n_uy_edi',
9+
# 'l10n_uy_ux',
1010
'stock_account',
11-
# 'sale_stock',
11+
'sale_stock',
12+
'l10n_uy_edi'
1213
],
1314
'data': [
1415
'data/l10n_latam.document.type.csv',

l10n_uy_edi_stock/models/l10n_latam_document_type.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from odoo import fields, models
22

33

4-
class L10nAccountDocumentType(models.Model):
4+
class L10nLatamDocumentType(models.Model):
55

66
_inherit = 'l10n_latam.document.type'
77

l10n_uy_edi_stock/models/l10n_uy_edi_document.py

-22
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ def _is_uy_remito_exp(self):
1313
def _is_uy_remito_loc(self):
1414
return self.l10n_latam_document_type_id.code == '181'
1515

16-
1716
def _is_uy_remito_type_cfe(self):
1817
return self.l10n_latam_document_type_id.internal_type in ['stock_picking']
1918

@@ -67,9 +66,6 @@ def _uy_cfe_B4_IndFact(self, line):
6766

6867
def _uy_cfe_B8_DscItem(self, line):
6968
""" B8 Descripcion Adicional del ítem. Maximo 1000 caracteres """
70-
if self._is_uy_inv_type_cfe():
71-
return super()._uy_cfe_B8_DscItem(line)
72-
7369
self.ensure_one()
7470
res = []
7571
for rec in line.l10n_uy_edi_addenda_ids:
@@ -98,24 +94,6 @@ def _uy_cfe_A_receptor(self):
9894

9995
return res
10096

101-
def _uy_cfe_C_totals(self):
102-
self.ensure_one()
103-
res = super()._uy_cfe_C_totals()
104-
105-
# A110 Tipo moneda transacción: Informar para todos menos para e-Rem loc
106-
if self._is_uy_remito_loc():
107-
res.pop('TpoMoneda')
108-
109-
# A124 Total Monto Total (NUM 17)
110-
# - Si tipo de CFE= 124 (e-remito de exportación), Valor numérico de 15 enteros y 2 decimales:
111-
# - sino, Valor numérico de 15 enteros y 2 decimales, ≥0 : C124 = SUM(C112:C118) + SUM(C121:C123)
112-
113-
# A111 Tipo de Cambio: Informar siempre que la moneda sea diferente al peso Uruguayo y no sea e-Rem Loc
114-
if self._is_uy_remito_loc():
115-
res.pop('TpoCambio')
116-
117-
return res
118-
11997
def _uy_get_cfe_tag(self):
12098
self.ensure_one()
12199
if self._is_uy_remito_loc():

l10n_uy_edi_stock/models/stock_picking.py

+147-93
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,70 @@
1+
import base64
2+
13
from odoo import api, models, fields, _
4+
5+
from odoo.exceptions import UserError
26
from odoo.tools import html2plaintext
37

48

59
class StockPicking(models.Model):
610

711
_inherit = 'stock.picking'
812

9-
l10n_uy_cfe_id = fields.Many2one("l10n_uy_edi.document", string="Uruguay E-Resguardo CFE", copy=False)
13+
# Need to make it work with document types
1014
l10n_latam_document_type_id = fields.Many2one('l10n_latam.document.type', string='Document Type (UY)', copy=False)
11-
l10n_latam_document_number = fields.Char(string='Document Number (UY)', readonly=True, states={'draft': [('readonly', False)]}, copy=False)
15+
l10n_latam_document_number = fields.Char(string='Document Number (UY)', readonly=True, copy=False)
16+
l10n_latam_available_document_type_ids = fields.Many2many('l10n_latam.document.type', compute='_compute_l10n_latam_available_document_types')
1217

13-
# Fields that need to be fill before creating the CFE
14-
l10n_uy_edi_cfe_uuid = fields.Char(
15-
'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)
18+
# Need to make it work with EDI (simil to what we have in account.move)
19+
l10n_uy_edi_document_id = fields.Many2one("l10n_uy_edi.document", string="Uruguay E-Invoice CFE", copy=False)
20+
l10n_uy_edi_cfe_uuid = fields.Char(related="l10n_uy_edi_document_id.uuid")
21+
l10n_uy_edi_cfe_state = fields.Selection(related="l10n_uy_edi_document_id.state", store=True)
22+
l10n_uy_edi_error = fields.Text(related="l10n_uy_edi_document_id.message")
23+
l10n_uy_is_cfe = fields.Boolean(
24+
compute="_compute_l10n_uy_is_cfe",
25+
help="Campo tecnico para saber si es un comprobante electronico o no y usarlo en la vista para mostrar o requerir ciertos campos."
26+
" por los momentos lo estamos usando solo para remitos pero podemos extenderlo para otros modelos"
27+
)
1628
l10n_uy_edi_addenda_ids = fields.Many2many(
17-
'l10n_uy_edi.addenda', string="Addenda & Disclosure",
18-
domain="[('type', 'in', ['issuer', 'receiver', 'cfe_doc', 'addenda'])]")
19-
20-
l10n_latam_available_document_type_ids = fields.Many2many('l10n_latam.document.type', compute='_compute_l10n_latam_available_document_types')
29+
"l10n_uy_edi.addenda",
30+
string="Addenda & Disclosure",
31+
domain="[('type', 'in', ['issuer', 'receiver', 'cfe_doc', 'addenda'])]",
32+
help="Addendas and Mandatory Disclosure to add on the CFE. They can be added either to the issuer, receiver,"
33+
" cfe doc additional info section or to the addenda section. However, the item type should not be set in"
34+
" this field; instead, it should be specified in the invoice lines.")
35+
l10n_uy_edi_cfe_sale_mode = fields.Selection([
36+
("1", "General Regime"),
37+
("2", "Consignment"),
38+
("3", "Reviewable Price"),
39+
("4", "Own goods to customs exclaves"),
40+
("90", "General Regime - exportation of services"),
41+
("99", "Other transactions"),
42+
], "Sales Modality", help="This field is used in the XML to create an Export e-Invoice")
43+
l10n_uy_edi_cfe_transport_route = fields.Selection([
44+
("1", "Maritime"),
45+
("2", "Air"),
46+
("3", "Ground"),
47+
("8", "N/A"),
48+
("9", "Other"),
49+
], "Transportation Route", help="This field is used in the XML to create an Export e-Invoice")
50+
51+
# New fields only for pickings
2152
l10n_uy_transfer_of_goods = fields.Selection(
22-
[('1', 'Venta'), ('2', 'Traslados internos')],
53+
[('1', 'Venta'),
54+
('2', 'Traslados internos')],
2355
string="Traslados de Bienes",
2456
)
25-
26-
l10n_uy_cfe_sale_mod = fields.Selection([
27-
('1', 'General Regime'),
28-
('2', 'Consignment'),
29-
('3', 'Reviewable Price'),
30-
('4', 'Own goods to customs exclaves'),
31-
('90', 'General Regime - exportation of services'),
32-
('99', 'Other transactions'),
33-
], 'Sales Modality', help="This field is used in the XML to create an Export e-Delivery Guide")
34-
l10n_uy_cfe_transport_route = fields.Selection([
35-
('1', 'Maritime'),
36-
('2', 'Air'),
37-
('3', 'Ground'),
38-
('8', 'N/A'),
39-
('9', 'Other'),
40-
], 'Transportation Route', help="This field is used in the XML to create an Export e-Delivery Guide")
41-
l10n_uy_place_of_delivery = fields.Char(
42-
"Place of Delivery",
43-
size=100,
44-
help="Indicación de donde se entrega la mercadería o se presta el servicio (Dirección, Sucursal, Puerto, etc,)")
4557
l10n_uy_edi_place_of_delivery = fields.Boolean(
4658
"Place of Delivery",
59+
size=100,
4760
help="CFE: Indication of where the merchandise is delivered or the service is provided"
4861
" (Address, Branch, Port, etc.) if True then we will inform the shipping address's name and street")
4962

63+
# TODO KZ campo similar a lo que tenemos en el UX, revisar como se llama y usarlo igual
64+
l10n_uy_cfe_xml = fields.Text()
65+
5066
def name_get(self):
51-
""" Display: 'Stock Picking Internal Sequence : Remito (if defined)' """
67+
""" Display: 'Stock Picking Internal Sequence : Remito Number (if defined)' """
5268
res = []
5369
for rec in self:
5470
if rec.l10n_latam_document_number:
@@ -58,31 +74,31 @@ def name_get(self):
5874
res.append((rec.id, name))
5975
return res
6076

77+
def _compute_l10n_uy_is_cfe(self):
78+
self.l10n_uy_is_cfe = False
79+
if self.l10n_uy_edi_document_id.move_id.journal_id.l10n_uy_edi_type == 'electronic':
80+
self.l10n_uy_is_cfe = True
81+
6182
@api.depends('partner_id', 'company_id', 'picking_type_code')
6283
def _compute_l10n_latam_available_document_types(self):
6384
uy_remitos = self.filtered(lambda x: x.country_code == 'UY' and x.picking_type_code == 'outgoing')
64-
6585
uy_remitos.l10n_latam_available_document_type_ids = self.env['l10n_latam.document.type'].search(
6686
self._get_l10n_latam_documents_domain())
6787
(self - uy_remitos).l10n_latam_available_document_type_ids = False
6888

6989
def _get_l10n_latam_documents_domain(self):
90+
""" return domain """
7091
codes = self._l10n_uy_get_remito_codes()
7192
return [('code', 'in', codes), ('active', '=', True), ('internal_type', '=', 'stock_picking')]
7293

73-
# TODO KZ evaluar si estaria bueno tener un boolean como este l10n_cl_draft_status
74-
# TODO KZ evaluar si agregar una constrains de unicidad para remitos, aplicaria para:
75-
# 1. remitos manual o preimpresos (no electronico),
76-
# 2. remitos generados en uruware y pasados a mano luego a oodo
77-
# 3. remitos de proveedor? no se si los necesitamos registrar
78-
7994
def action_cancel(self):
80-
# The move cannot be modified once the CFE has been accepted by the DGI
95+
""" El remito no puede ser modificado una vez que ya fue aceptado por DGI """
8196
remitos = self.filtered(lambda x: x.country_code == 'UY' and x.picking_type_code == 'outgoing')
82-
remitos._uy_check_state()
97+
if remitos.filtered(lambda x: x.l10n_uy_edi_cfe_state in ['accepted', 'rejected', 'received']):
98+
raise UserError(_('Can not cancel a Remito already process by DGI'))
8399
return super().action_cancel()
84100

85-
def uy_post_dgi_remito(self):
101+
def l10n_uy_edi_stock_post_dgi(self):
86102
""" El E-remito tiene las siguientes partes en el xml
87103
A. Encabezado
88104
B. Detalle de los productos
@@ -91,15 +107,16 @@ def uy_post_dgi_remito(self):
91107
"""
92108
# Filtrar solo los e-remitos
93109
uy_remitos = self.filtered(
94-
lambda x: x.country_code == 'UY' and x.picking_type_code == 'outgoing'
110+
lambda x: x.country_code == 'UY'
111+
and x.picking_type_code == 'outgoing'
95112
and x.l10n_latam_document_type_id
96113
and int(x.l10n_latam_document_type_id.code) > 0
97114
and x.l10n_uy_edi_cfe_state not in ['accepted', 'rejected', 'received']
98115
)
99116

100-
# If the invoice was previosly validated in Uruware and need to be link to Odoo we check that the
101-
# l10n_uy_edi_cfe_uuid has been manually set and we consult to get the invoice information from Uruware
102-
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)
117+
# If the invoice was previously validated in Uruware and need to be link to Odoo
118+
# we check that the l10n_uy_edi_cfe_uuid has been manually set and we consult to get the invoice information from Uruware
119+
pre_validated_in_uruware = uy_remitos.filtered(lambda x: x.l10n_uy_edi_cfe_uuid and not x.attachment_id and not x.l10n_uy_edi_cfe_state)
103120
if pre_validated_in_uruware:
104121
pre_validated_in_uruware.uy_ux_action_get_uruware_cfe()
105122
uy_remitos = uy_remitos - pre_validated_in_uruware
@@ -110,38 +127,99 @@ def uy_post_dgi_remito(self):
110127
# Send invoices to DGI and get the return info
111128
for remito in uy_remitos:
112129
if remito.company_id.l10n_uy_edi_ucfe_env == "demo":
113-
remito._uy_dummy_validation()
114-
continue
115-
116-
# TODO KZ I think we can avoid this loop. review
117-
remito._uy_dgi_post()
130+
attachments = remito._l10n_uy_edi_dummy_validation()
131+
msg = _(
132+
"This CFE has been generated in DEMO Mode. It is considered"
133+
" as accepted and it won\"t be sent to DGI.")
134+
else:
135+
remito._uy_dgi_post()
136+
msg += _("The electronic invoice was created successfully")
137+
138+
remito.with_context(no_new_invoice=True).message_post(
139+
body=msg, attachment_ids=attachments.ids if attachments else False)
140+
141+
def _l10n_uy_edi_dummy_validation(self):
142+
# COPY l10n_uy_edi (only change move_id with picking_id)
143+
""" When we want to skip DGI and validate only in Odoo """
144+
edi_doc = self.l10n_uy_edi_document_id
145+
edi_doc.state = "accepted"
146+
self.write({
147+
"l10n_latam_document_number": "DE%07d" % (edi_doc.picking_id.id),
148+
"ref": "*DEMO",
149+
})
150+
151+
return self._l10n_uy_edi_get_preview_xml()
152+
153+
def _l10n_uy_edi_get_preview_xml(self):
154+
# COPY l10n_uy_edi
155+
self.ensure_one()
156+
edi_doc = self.l10n_uy_edi_document_id
157+
edi_doc.attachment_id.res_field = False
158+
xml_file = self.env["ir.attachment"].create({
159+
"res_model": "l10n_uy_edi.document",
160+
"res_field": "attachment_file",
161+
"res_id": edi_doc.id,
162+
"name": edi_doc._get_xml_attachment_name(),
163+
"type": "binary",
164+
"datas": base64.b64encode(self._l10n_uy_edi_get_xml_content().encode()),
165+
})
166+
edi_doc.invalidate_recordset(["attachment_id", "attachment_file"])
167+
return xml_file
168+
169+
def _l10n_uy_edi_get_xml_content(self):
170+
# COPY l10n_uy_edi
171+
""" Create the CFE xml structure and validate it
172+
:return: string the xml content to send to DGI """
173+
self.ensure_one()
118174

119-
# 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
120-
# factura
175+
values = {
176+
"cfe": self,
177+
"IdDoc": self._l10n_uy_edi_cfe_A_iddoc(),
178+
"emisor": self._l10n_uy_edi_cfe_A_issuer(),
179+
"receptor": self._l10n_uy_edi_cfe_A_receptor(),
180+
"item_detail": self._l10n_uy_edi_cfe_B_details(),
181+
"totals_detail": self._l10n_uy_edi_cfe_C_totals(),
182+
"referencia_lines": self._l10n_uy_edi_cfe_F_reference(),
183+
"format_float": format_float,
184+
}
185+
cfe = self.env["ir.qweb"]._render(
186+
"l10n_uy_edi." + self.l10n_uy_edi_document_id._get_cfe_tag(self) + "_template", values)
187+
return etree.tostring(cleanup_xml_node(cfe)).decode()
188+
189+
def _l10n_uy_edi_get_addenda(self):
190+
""" return string with the addenda of the remito """
191+
addenda = self.l10n_uy_edi_document_id._get_legends("addenda", self)
192+
if self.origin:
193+
addenda += "\n\nOrigin: %s" % self.origin
194+
if self.note:
195+
addenda += "\n\n%s" % html2plaintext(self.note)
196+
return addenda.strip()
121197

122-
def _uy_get_cfe_addenda(self):
123-
""" Add Specific MOVE model fields to the CFE Addenda if they are set:
198+
def _l10n_uy_edi_get_used_rate(self):
199+
# COPY l10n_uy_edi
200+
self.ensure_one()
201+
# We need to use abs to avoid error on Credit Notes (amount_total_signed is negative)
202+
return abs(self.amount_total_signed) / self.amount_total
124203

125-
* field Origin added with the prefix "Origin: ..."
126-
* Observation
127-
"""
204+
def _l10n_uy_edi_cfe_C_totals(self):
128205
self.ensure_one()
129-
res = super()._uy_get_cfe_addenda()
130-
if self.origin:
131-
res += "\n\nOrigin: %s" % self.origin
132-
if self.note:
133-
res += "\n\n%s" % html2plaintext(self.note)
134-
return res.strip()
206+
currency_name = self.currency_id.name if self.currency_id else self.company_id.currency_id.name
207+
lines = self._uy_get_cfe_lines()
208+
res = {
209+
'TpoMoneda': currency_name if not self._is_uy_remito_loc() else None, # A110
210+
'TpoCambio': None if currency_name == "UYU" else self._l10n_uy_edi_get_used_rate() or None, # A111
211+
'CantLinDet': len(lines), # A126
212+
}
213+
return res
135214

136215
def _uy_get_cfe_lines(self):
137216
self.ensure_one()
138-
if self._is_uy_remito_type_cfe():
139-
# 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
217+
# 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
140218

141-
# move_ids_without_package Stock moves not in package (stock.move)
142-
# move_line_ids Operations (stock.move.line)
143-
# move_line_ids_without_package Operations without package (stock.move.line)
144-
return self.move_ids_without_package
219+
# move_ids_without_package Stock moves not in package (stock.move)
220+
# move_line_ids Operations (stock.move.line)
221+
# move_line_ids_without_package Operations without package (stock.move.line)
222+
return self.move_ids_without_package
145223

146224
def _l10n_uy_get_remito_codes(self):
147225
""" return list of the available document type codes for uruguayan of stock picking"""
@@ -191,27 +269,3 @@ def _l10n_uy_edi_cfe_A_iddoc(self):
191269

192270
return res
193271

194-
195-
# TODO KZ este metodo debemos adaptarlo para obtener el IndFact
196-
def _uy_cfe_B4_IndFact(self, line):
197-
""" B4: Indicador de facturación
198-
199-
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
200-
move_ids_without_package Stock moves not in package (stock.move)
201-
move_line_ids Operations (stock.move.line)
202-
move_line_ids_without_package Operations without package (stock.move.line)
203-
"""
204-
# Another cases for future
205-
# 4: Gravado a Otra Tasa/IVA sobre fictos
206-
# 5: Entrega Gratuita. Por ejemplo docenas de trece
207-
# 6: Producto o servicio no facturable. No existe validación, excepto si A-C20= 1, B-C4=6 o 7.
208-
# 7: Producto o servicio no facturable negativo. . No existe validación, excepto si A-C20= 1, B-C4=6 o 7.
209-
# 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
210-
# 9: Sólo para resguardos: Ítem a anular en resguardos. En área de referencia se debe indicar el N° de resguardo que anular
211-
# 11: Impuesto percibido
212-
# 12: IVA en suspenso
213-
# 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)
214-
# 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)
215-
# 15: Sólo para e-Boleta de entrada y sus notas de corrección: Ítem vendido por un contribuyente IMEBA (valida A-C60 = 2)
216-
# 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
217-
return super()._uy_cfe_B4_IndFact(line)

l10n_uy_edi_stock/views/cfe_template.xml

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<odoo>
3-
<!-- to implement in future: eRem_Exp -->
43

54
<template id="cfe_template" inherit_id="l10n_uy_edi.cfe_template">
65
<FchEmis position="after">
7-
<LugarDestEnt t-out="IdDoc['TipoTraslado']"/>
6+
<TipoTraslado t-if="IdDoc.get('TipoTraslado')" t-out="IdDoc['TipoTraslado']"/>
87
</FchEmis>
98
<InfoAdicional position="after">
10-
<LugarDestEnt t-out="receptor.get('LugarDestEnt')"/>
9+
<LugarDestEnt t-if="receptor.get('LugarDestEnt')" t-out="receptor['LugarDestEnt']"/>
1110
</InfoAdicional>
1211
</template>
1312

0 commit comments

Comments
 (0)