se extrae generacion de CUFE a DianXMLExtensionCUFE

FossilOrigin-Name: 964ad19875edfc7f6990f795396eeeb4c66809eefe93303b1479d6c3ad2e2484
This commit is contained in:
bit4bit@riseup.net 2020-05-28 00:49:14 +00:00
parent 5d564ef149
commit 52632babb2
6 changed files with 93 additions and 72 deletions

View File

@ -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:

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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'