se extrae generacion de CUFE a DianXMLExtensionCUFE
FossilOrigin-Name: 964ad19875edfc7f6990f795396eeeb4c66809eefe93303b1479d6c3ad2e2484
This commit is contained in:
parent
5d564ef149
commit
52632babb2
@ -80,7 +80,8 @@ def generate_invoice(private_key, passphrase, scriptname):
|
|||||||
|
|
||||||
invoice = module.invoice()
|
invoice = module.invoice()
|
||||||
invoice.calculate()
|
invoice.calculate()
|
||||||
xml = form.DIANInvoiceXML(invoice)
|
params = module.params()
|
||||||
|
xml = form.DIANInvoiceXML(invoice, **params)
|
||||||
|
|
||||||
extensions = module.extensions(invoice)
|
extensions = module.extensions(invoice)
|
||||||
for extension in extensions:
|
for extension in extensions:
|
||||||
|
@ -136,7 +136,7 @@ class FachoXML:
|
|||||||
elem = self.set_element('/'+ root_tag + xpath, elements)
|
elem = self.set_element('/'+ root_tag + xpath, elements)
|
||||||
else:
|
else:
|
||||||
for new_element in elements:
|
for new_element in elements:
|
||||||
elem = self.find_or_create_element('/'+ root_tag + xpath)
|
elem = self.find_or_create_element('/' + root_tag + xpath)
|
||||||
self.builder.append(elem, new_element)
|
self.builder.append(elem, new_element)
|
||||||
|
|
||||||
def fragment(self, xpath, append=False):
|
def fragment(self, xpath, append=False):
|
||||||
|
@ -2,4 +2,5 @@ from .fe import FeXML
|
|||||||
from .fe import NAMESPACES
|
from .fe import NAMESPACES
|
||||||
from .fe import DianXMLExtensionSigner
|
from .fe import DianXMLExtensionSigner
|
||||||
from .fe import DianXMLExtensionSoftwareSecurityCode
|
from .fe import DianXMLExtensionSoftwareSecurityCode
|
||||||
|
from .fe import DianXMLExtensionCUFE
|
||||||
from .fe import DianZIP
|
from .fe import DianZIP
|
||||||
|
@ -10,6 +10,7 @@ import zipfile
|
|||||||
import warnings
|
import warnings
|
||||||
import hashlib
|
import hashlib
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
from .data import dian
|
||||||
|
|
||||||
NAMESPACES = {
|
NAMESPACES = {
|
||||||
'fe': 'http://www.dian.gov.co/contratos/facturaelectronica/v1',
|
'fe': 'http://www.dian.gov.co/contratos/facturaelectronica/v1',
|
||||||
@ -39,6 +40,73 @@ class FeXML(FachoXML):
|
|||||||
#self.find_or_create_element(self._cn)
|
#self.find_or_create_element(self._cn)
|
||||||
|
|
||||||
|
|
||||||
|
class DianXMLExtensionCUFE(FachoXMLExtension):
|
||||||
|
AMBIENTE_PRUEBAS = 'Pruebas'
|
||||||
|
AMBIENTE_PRODUCCION = 'Producción'
|
||||||
|
|
||||||
|
def __init__(self, invoice, tipo_ambiente = AMBIENTE_PRUEBAS, clave_tecnica = ''):
|
||||||
|
self.tipo_ambiente = tipo_ambiente
|
||||||
|
self.clave_tecnica = clave_tecnica
|
||||||
|
self.invoice = invoice
|
||||||
|
|
||||||
|
def _tipo_ambiente(self):
|
||||||
|
return int(dian.TipoAmbiente[self.tipo_ambiente]['code'])
|
||||||
|
|
||||||
|
def build(self, fachoxml):
|
||||||
|
cufe = self._generate_cufe(self.invoice, fachoxml)
|
||||||
|
fachoxml.set_element('/fe:Invoice/cbc:UUID[schemaName="CUFE-SHA384"]', cufe)
|
||||||
|
fachoxml.set_element('/fe:Invoice/cbc:ProfileExecutionID', self._tipo_ambiente())
|
||||||
|
return '', []
|
||||||
|
|
||||||
|
def _generate_cufe(self, invoice, fachoxml):
|
||||||
|
NumFac = invoice.invoice_ident
|
||||||
|
FecFac = fachoxml.issue_date(invoice.invoice_issue)
|
||||||
|
HoraFac = fachoxml.issue_time(invoice.invoice_issue)
|
||||||
|
ValorBruto = invoice.invoice_legal_monetary_total.line_extension_amount
|
||||||
|
ValorTotalPagar = invoice.invoice_legal_monetary_total.payable_amount
|
||||||
|
ValorImpuestoPara = {}
|
||||||
|
ValorImpuesto1 = 0.0
|
||||||
|
CodImpuesto1 = 1
|
||||||
|
ValorImpuesto2 = 0.0
|
||||||
|
CodImpuesto2 = 4
|
||||||
|
ValorImpuesto3 = 0.0
|
||||||
|
CodImpuesto3 = 3
|
||||||
|
for invoice_line in invoice.invoice_lines:
|
||||||
|
for subtotal in invoice_line.tax.subtotals:
|
||||||
|
# TODO cual es la naturaleza de tax_scheme_ident?
|
||||||
|
codigo_impuesto = int(subtotal.tax_scheme_ident)
|
||||||
|
ValorImpuestoPara.setdefault(codigo_impuesto, 0.0)
|
||||||
|
ValorImpuestoPara[codigo_impuesto] += subtotal.tax_amount
|
||||||
|
|
||||||
|
NitOFE = invoice.invoice_supplier.ident
|
||||||
|
NumAdq = invoice.invoice_customer.ident
|
||||||
|
TipoAmb = self._tipo_ambiente()
|
||||||
|
ClTec = str(self.clave_tecnica)
|
||||||
|
|
||||||
|
formatVars = [
|
||||||
|
'%s' % NumFac,
|
||||||
|
'%s' % FecFac,
|
||||||
|
'%s' % HoraFac,
|
||||||
|
'%.02f' % ValorBruto,
|
||||||
|
'%02d' % CodImpuesto1,
|
||||||
|
'%.02f' % ValorImpuestoPara.get(CodImpuesto1, 0.0),
|
||||||
|
'%02d' % CodImpuesto2,
|
||||||
|
'%.02f' % ValorImpuestoPara.get(CodImpuesto2, 0.0),
|
||||||
|
'%02d' % CodImpuesto3,
|
||||||
|
'%.02f' % ValorImpuestoPara.get(CodImpuesto3, 0.0),
|
||||||
|
'%.02f' % ValorTotalPagar,
|
||||||
|
'%s' % NitOFE,
|
||||||
|
'%s' % NumAdq,
|
||||||
|
'%s' % ClTec,
|
||||||
|
'%d' % TipoAmb,
|
||||||
|
]
|
||||||
|
cufe = "".join(formatVars)
|
||||||
|
|
||||||
|
# crear hash...
|
||||||
|
h = hashlib.sha384()
|
||||||
|
h.update(cufe.encode('utf-8'))
|
||||||
|
return h.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
class DianXMLExtensionSoftwareSecurityCode(FachoXMLExtension):
|
class DianXMLExtensionSoftwareSecurityCode(FachoXMLExtension):
|
||||||
# RESOLUCION 0001: pagina 535
|
# RESOLUCION 0001: pagina 535
|
||||||
|
@ -92,7 +92,6 @@ class Invoice:
|
|||||||
self.invoice_period_end = None
|
self.invoice_period_end = None
|
||||||
self.invoice_issue = None
|
self.invoice_issue = None
|
||||||
self.invoice_ident = None
|
self.invoice_ident = None
|
||||||
self.invoice_cufe = None
|
|
||||||
self.invoice_legal_monetary_total = LegalMonetaryTotal(0, 0, 0, 0, 0)
|
self.invoice_legal_monetary_total = LegalMonetaryTotal(0, 0, 0, 0, 0)
|
||||||
self.invoice_customer = None
|
self.invoice_customer = None
|
||||||
self.invoice_supplier = None
|
self.invoice_supplier = None
|
||||||
@ -170,18 +169,11 @@ class DianResolucion0001Validator:
|
|||||||
|
|
||||||
|
|
||||||
class DIANInvoiceXML(fe.FeXML):
|
class DIANInvoiceXML(fe.FeXML):
|
||||||
AMBIENTE_PRUEBAS = 'Pruebas'
|
|
||||||
AMBIENTE_PRODUCCION = 'Producción'
|
|
||||||
|
|
||||||
def __init__(self, invoice, tipo_ambiente = AMBIENTE_PRUEBAS, clave_tecnica = ''):
|
def __init__(self, invoice):
|
||||||
super().__init__('Invoice', 'http://www.dian.gov.co/contratos/facturaelectronica/v1')
|
super().__init__('Invoice', 'http://www.dian.gov.co/contratos/facturaelectronica/v1')
|
||||||
self.tipo_ambiente = tipo_ambiente
|
|
||||||
self.clave_tecnica = clave_tecnica
|
|
||||||
self.attach_invoice(invoice)
|
self.attach_invoice(invoice)
|
||||||
|
|
||||||
def _tipo_ambiente(self):
|
|
||||||
return int(dian.TipoAmbiente[self.tipo_ambiente]['code'])
|
|
||||||
|
|
||||||
def attach_invoice(self, invoice):
|
def attach_invoice(self, invoice):
|
||||||
"""adiciona etiquetas a FEXML y retorna FEXML
|
"""adiciona etiquetas a FEXML y retorna FEXML
|
||||||
en caso de fallar validacion retorna None"""
|
en caso de fallar validacion retorna None"""
|
||||||
@ -189,11 +181,7 @@ class DIANInvoiceXML(fe.FeXML):
|
|||||||
|
|
||||||
invoice.calculate()
|
invoice.calculate()
|
||||||
|
|
||||||
cufe = self._generate_cufe(invoice)
|
|
||||||
|
|
||||||
fexml.set_element('/fe:Invoice/cbc:ProfileExecutionID', self._tipo_ambiente())
|
|
||||||
fexml.set_element('/fe:Invoice/cbc:ID', invoice.invoice_ident)
|
fexml.set_element('/fe:Invoice/cbc:ID', invoice.invoice_ident)
|
||||||
fexml.set_element('/fe:Invoice/cbc:UUID[schemaName="CUFE-SHA384"]', cufe)
|
|
||||||
fexml.set_element('/fe:Invoice/cbc:IssueDate', self.issue_date(invoice.invoice_issue))
|
fexml.set_element('/fe:Invoice/cbc:IssueDate', self.issue_date(invoice.invoice_issue))
|
||||||
fexml.set_element('/fe:Invoice/cbc:IssueTime', self.issue_time(invoice.invoice_issue))
|
fexml.set_element('/fe:Invoice/cbc:IssueTime', self.issue_time(invoice.invoice_issue))
|
||||||
fexml.set_element('/fe:Invoice/cac:InvoicePeriod/cbc:StartDate', invoice.invoice_period_start.strftime('%Y-%m-%d'))
|
fexml.set_element('/fe:Invoice/cac:InvoicePeriod/cbc:StartDate', invoice.invoice_period_start.strftime('%Y-%m-%d'))
|
||||||
@ -266,52 +254,3 @@ class DIANInvoiceXML(fe.FeXML):
|
|||||||
def issue_date(self, datetime_):
|
def issue_date(self, datetime_):
|
||||||
return datetime_.strftime('%Y-%m-%d')
|
return datetime_.strftime('%Y-%m-%d')
|
||||||
|
|
||||||
def _generate_cufe(self, invoice):
|
|
||||||
NumFac = invoice.invoice_ident
|
|
||||||
FecFac = self.issue_date(invoice.invoice_issue)
|
|
||||||
HoraFac = self.issue_time(invoice.invoice_issue)
|
|
||||||
ValorBruto = invoice.invoice_legal_monetary_total.line_extension_amount
|
|
||||||
ValorTotalPagar = invoice.invoice_legal_monetary_total.payable_amount
|
|
||||||
ValorImpuestoPara = {}
|
|
||||||
ValorImpuesto1 = 0.0
|
|
||||||
CodImpuesto1 = 1
|
|
||||||
ValorImpuesto2 = 0.0
|
|
||||||
CodImpuesto2 = 4
|
|
||||||
ValorImpuesto3 = 0.0
|
|
||||||
CodImpuesto3 = 3
|
|
||||||
for invoice_line in invoice.invoice_lines:
|
|
||||||
for subtotal in invoice_line.tax.subtotals:
|
|
||||||
# TODO cual es la naturaleza de tax_scheme_ident?
|
|
||||||
codigo_impuesto = int(subtotal.tax_scheme_ident)
|
|
||||||
ValorImpuestoPara.setdefault(codigo_impuesto, 0.0)
|
|
||||||
ValorImpuestoPara[codigo_impuesto] += subtotal.tax_amount
|
|
||||||
|
|
||||||
NitOFE = invoice.invoice_supplier.ident
|
|
||||||
NumAdq = invoice.invoice_customer.ident
|
|
||||||
TipoAmb = self._tipo_ambiente()
|
|
||||||
ClTec = str(self.clave_tecnica)
|
|
||||||
|
|
||||||
formatVars = [
|
|
||||||
'%s' % NumFac,
|
|
||||||
'%s' % FecFac,
|
|
||||||
'%s' % HoraFac,
|
|
||||||
'%.02f' % ValorBruto,
|
|
||||||
'%02d' % CodImpuesto1,
|
|
||||||
'%.02f' % ValorImpuestoPara.get(CodImpuesto1, 0.0),
|
|
||||||
'%02d' % CodImpuesto2,
|
|
||||||
'%.02f' % ValorImpuestoPara.get(CodImpuesto2, 0.0),
|
|
||||||
'%02d' % CodImpuesto3,
|
|
||||||
'%.02f' % ValorImpuestoPara.get(CodImpuesto3, 0.0),
|
|
||||||
'%.02f' % ValorTotalPagar,
|
|
||||||
'%s' % NitOFE,
|
|
||||||
'%s' % NumAdq,
|
|
||||||
'%s' % ClTec,
|
|
||||||
'%d' % TipoAmb,
|
|
||||||
]
|
|
||||||
cufe = "".join(formatVars)
|
|
||||||
|
|
||||||
# crear hash...
|
|
||||||
h = hashlib.sha384()
|
|
||||||
h.update(cufe.encode('utf-8'))
|
|
||||||
return h.hexdigest()
|
|
||||||
|
|
||||||
|
@ -94,6 +94,9 @@ def test_invoicesimple_build_with_cufe(simple_invoice):
|
|||||||
simple_invoice.validate(invoice_validator)
|
simple_invoice.validate(invoice_validator)
|
||||||
assert invoice_validator.valid() == True
|
assert invoice_validator.valid() == True
|
||||||
xml = form.DIANInvoiceXML(simple_invoice)
|
xml = form.DIANInvoiceXML(simple_invoice)
|
||||||
|
cufe_extension = fe.DianXMLExtensionCUFE(simple_invoice)
|
||||||
|
xml.add_extension(cufe_extension)
|
||||||
|
xml.attach_extensions()
|
||||||
cufe = xml.get_element_text('/fe:Invoice/cbc:UUID')
|
cufe = xml.get_element_text('/fe:Invoice/cbc:UUID')
|
||||||
assert cufe != ''
|
assert cufe != ''
|
||||||
|
|
||||||
@ -139,6 +142,9 @@ def test_invoice_line_count_numeric(simple_invoice):
|
|||||||
|
|
||||||
def test_invoice_profileexecutionid(simple_invoice):
|
def test_invoice_profileexecutionid(simple_invoice):
|
||||||
xml_invoice = form.DIANInvoiceXML(simple_invoice)
|
xml_invoice = form.DIANInvoiceXML(simple_invoice)
|
||||||
|
cufe_extension = fe.DianXMLExtensionCUFE(simple_invoice)
|
||||||
|
xml_invoice.add_extension(cufe_extension)
|
||||||
|
xml_invoice.attach_extensions()
|
||||||
id_ = xml_invoice.get_element_text('/fe:Invoice/cbc:ProfileExecutionID', format_=int)
|
id_ = xml_invoice.get_element_text('/fe:Invoice/cbc:ProfileExecutionID', format_=int)
|
||||||
assert id_ == 2
|
assert id_ == 2
|
||||||
|
|
||||||
@ -190,9 +196,15 @@ def test_invoice_cufe(simple_invoice_without_lines):
|
|||||||
def issue_date(self, datetime_):
|
def issue_date(self, datetime_):
|
||||||
return '2019-01-16'
|
return '2019-01-16'
|
||||||
|
|
||||||
xml_invoice = FakeDIANInvoiceXML(simple_invoice,
|
xml_invoice = FakeDIANInvoiceXML(simple_invoice)
|
||||||
tipo_ambiente = form.DIANInvoiceXML.AMBIENTE_PRODUCCION,
|
|
||||||
clave_tecnica = '693ff6f2a553c3646a063436fd4dd9ded0311471')
|
cufe_extension = fe.DianXMLExtensionCUFE(
|
||||||
|
simple_invoice,
|
||||||
|
tipo_ambiente = fe.DianXMLExtensionCUFE.AMBIENTE_PRODUCCION,
|
||||||
|
clave_tecnica = '693ff6f2a553c3646a063436fd4dd9ded0311471'
|
||||||
|
)
|
||||||
|
xml_invoice.add_extension(cufe_extension)
|
||||||
|
xml_invoice.attach_extensions()
|
||||||
cufe = xml_invoice.get_element_text('/fe:Invoice/cbc:UUID')
|
cufe = xml_invoice.get_element_text('/fe:Invoice/cbc:UUID')
|
||||||
# RESOLUCION 004: pagina 689
|
# RESOLUCION 004: pagina 689
|
||||||
assert cufe == '8bb918b19ba22a694f1da11c643b5e9de39adf60311cf179179e9b33381030bcd4c3c3f156c506ed5908f9276f5bd9b4'
|
assert cufe == '8bb918b19ba22a694f1da11c643b5e9de39adf60311cf179179e9b33381030bcd4c3c3f156c506ed5908f9276f5bd9b4'
|
||||||
|
Loading…
Reference in New Issue
Block a user