Fix: SigningTime tz='America/Bogota'
This commit is contained in:
parent
9297be22e0
commit
d04596ed3a
@ -1,6 +1,5 @@
|
|||||||
# This file is part of facho. The COPYRIGHT file at the top level of
|
# This file is part of facho. The COPYRIGHT file at the top level of
|
||||||
# this repository contains the full copyright notices and license terms.
|
# this repository contains the full copyright notices and license terms.
|
||||||
|
|
||||||
from ..facho import FachoXML, FachoXMLExtension, LXMLBuilder
|
from ..facho import FachoXML, FachoXMLExtension, LXMLBuilder
|
||||||
import uuid
|
import uuid
|
||||||
import xmlsig
|
import xmlsig
|
||||||
@ -15,6 +14,7 @@ from .data.dian import codelist
|
|||||||
from . import form
|
from . import form
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
# from pathlib import Path
|
# from pathlib import Path
|
||||||
|
from dateutil import tz
|
||||||
|
|
||||||
from cryptography.hazmat.primitives.serialization import pkcs12
|
from cryptography.hazmat.primitives.serialization import pkcs12
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ SCHEME_AGENCY_ATTRS = {
|
|||||||
POLICY_ID = 'https://facturaelectronica.dian.gov.co/politicadefirma/v2/politicadefirmav2.pdf'
|
POLICY_ID = 'https://facturaelectronica.dian.gov.co/politicadefirma/v2/politicadefirmav2.pdf'
|
||||||
POLICY_NAME = u'Política de firma para facturas electrónicas de la República de Colombia.'
|
POLICY_NAME = u'Política de firma para facturas electrónicas de la República de Colombia.'
|
||||||
|
|
||||||
|
Bogota = tz.gettz('America/Bogota')
|
||||||
# NAMESPACES = {
|
# NAMESPACES = {
|
||||||
# 'atd': 'urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2',
|
# 'atd': 'urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2',
|
||||||
# 'nomina': 'dian:gov:co:facturaelectronica:NominaIndividual',
|
# 'nomina': 'dian:gov:co:facturaelectronica:NominaIndividual',
|
||||||
@ -158,9 +158,11 @@ class DianXMLExtensionCUDFE(FachoXMLExtension):
|
|||||||
|
|
||||||
# #DIAN 1.8.-2021: FAD03
|
# #DIAN 1.8.-2021: FAD03
|
||||||
# fachoxml.set_element('./cbc:ProfileID', 'DIAN 2.1: Factura Electrónica de Venta')
|
# fachoxml.set_element('./cbc:ProfileID', 'DIAN 2.1: Factura Electrónica de Venta')
|
||||||
fachoxml.set_element('./cbc:ProfileExecutionID', self._tipo_ambiente_int())
|
fachoxml.set_element(
|
||||||
|
'./cbc:ProfileExecutionID', self._tipo_ambiente_int())
|
||||||
#DIAN 1.7.-2020: FAB36
|
#DIAN 1.7.-2020: FAB36
|
||||||
fachoxml.set_element('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:QRCode',
|
fachoxml.set_element(
|
||||||
|
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:QRCode',
|
||||||
self._get_qrcode(cufe))
|
self._get_qrcode(cufe))
|
||||||
|
|
||||||
def issue_time(self, datetime_):
|
def issue_time(self, datetime_):
|
||||||
@ -177,7 +179,8 @@ class DianXMLExtensionCUDFE(FachoXMLExtension):
|
|||||||
build_vars['HoraFac'] = self.issue_time(invoice.invoice_issue)
|
build_vars['HoraFac'] = self.issue_time(invoice.invoice_issue)
|
||||||
# PAG 601
|
# PAG 601
|
||||||
build_vars['ValorBruto'] = invoice.invoice_legal_monetary_total.line_extension_amount
|
build_vars['ValorBruto'] = invoice.invoice_legal_monetary_total.line_extension_amount
|
||||||
build_vars['ValorTotalPagar'] = invoice.invoice_legal_monetary_total.payable_amount
|
build_vars['ValorTotalPagar'
|
||||||
|
] = invoice.invoice_legal_monetary_total.payable_amount
|
||||||
ValorImpuestoPara = defaultdict(lambda: form.Amount(0.0))
|
ValorImpuestoPara = defaultdict(lambda: form.Amount(0.0))
|
||||||
build_vars['CodImpuesto1'] = '01'
|
build_vars['CodImpuesto1'] = '01'
|
||||||
build_vars['CodImpuesto2'] = '04'
|
build_vars['CodImpuesto2'] = '04'
|
||||||
@ -207,7 +210,8 @@ class DianXMLExtensionCUDFE(FachoXMLExtension):
|
|||||||
|
|
||||||
|
|
||||||
class DianXMLExtensionCUFE(DianXMLExtensionCUDFE):
|
class DianXMLExtensionCUFE(DianXMLExtensionCUDFE):
|
||||||
def __init__(self, invoice, clave_tecnica = '', tipo_ambiente = AMBIENTE_PRUEBAS):
|
def __init__(
|
||||||
|
self, invoice, clave_tecnica='', tipo_ambiente=AMBIENTE_PRUEBAS):
|
||||||
self.tipo_ambiente = tipo_ambiente
|
self.tipo_ambiente = tipo_ambiente
|
||||||
self.clave_tecnica = clave_tecnica
|
self.clave_tecnica = clave_tecnica
|
||||||
self.invoice = invoice
|
self.invoice = invoice
|
||||||
@ -243,6 +247,7 @@ class DianXMLExtensionCUFE(DianXMLExtensionCUDFE):
|
|||||||
'%d' % build_vars['TipoAmb'],
|
'%d' % build_vars['TipoAmb'],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class DianXMLExtensionCUDE(DianXMLExtensionCUDFE):
|
class DianXMLExtensionCUDE(DianXMLExtensionCUDFE):
|
||||||
def __init__(self, invoice, software_pin, tipo_ambiente = AMBIENTE_PRUEBAS):
|
def __init__(self, invoice, software_pin, tipo_ambiente = AMBIENTE_PRUEBAS):
|
||||||
self.tipo_ambiente = tipo_ambiente
|
self.tipo_ambiente = tipo_ambiente
|
||||||
@ -280,6 +285,7 @@ class DianXMLExtensionCUDE(DianXMLExtensionCUDFE):
|
|||||||
'%d' % build_vars['TipoAmb'],
|
'%d' % build_vars['TipoAmb'],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class DianXMLExtensionCUDS(DianXMLExtensionCUDFE):
|
class DianXMLExtensionCUDS(DianXMLExtensionCUDFE):
|
||||||
def __init__(self, invoice, software_pin, tipo_ambiente = AMBIENTE_PRUEBAS):
|
def __init__(self, invoice, software_pin, tipo_ambiente = AMBIENTE_PRUEBAS):
|
||||||
self.tipo_ambiente = tipo_ambiente
|
self.tipo_ambiente = tipo_ambiente
|
||||||
@ -313,6 +319,7 @@ class DianXMLExtensionCUDS(DianXMLExtensionCUDFE):
|
|||||||
'%d' % build_vars['TipoAmb'],
|
'%d' % build_vars['TipoAmb'],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class DianXMLExtensionSoftwareProvider(FachoXMLExtension):
|
class DianXMLExtensionSoftwareProvider(FachoXMLExtension):
|
||||||
# RESOLUCION 0004: pagina 108
|
# RESOLUCION 0004: pagina 108
|
||||||
|
|
||||||
@ -322,7 +329,8 @@ class DianXMLExtensionSoftwareProvider(FachoXMLExtension):
|
|||||||
self.id_software = id_software
|
self.id_software = id_software
|
||||||
|
|
||||||
def build(self, fexml):
|
def build(self, fexml):
|
||||||
software_provider = fexml.fragment('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareProvider')
|
software_provider = fexml.fragment(
|
||||||
|
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareProvider')
|
||||||
provider_id_attrs = SCHEME_AGENCY_ATTRS.copy()
|
provider_id_attrs = SCHEME_AGENCY_ATTRS.copy()
|
||||||
provider_id_attrs.update({'schemeID': self.dv})
|
provider_id_attrs.update({'schemeID': self.dv})
|
||||||
#DIAN 1.7.-2020: FAB23
|
#DIAN 1.7.-2020: FAB23
|
||||||
@ -371,7 +379,9 @@ class DianXMLExtensionSigner:
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def _element_extension_content(self, fachoxml):
|
def _element_extension_content(self, fachoxml):
|
||||||
return fachoxml.builder.xpath(fachoxml.root, './ext:UBLExtensions/ext:UBLExtension[2]/ext:ExtensionContent')
|
return fachoxml.builder.xpath(
|
||||||
|
fachoxml.root,
|
||||||
|
'./ext:UBLExtensions/ext:UBLExtension[2]/ext:ExtensionContent')
|
||||||
|
|
||||||
def sign_xml_string(self, document):
|
def sign_xml_string(self, document):
|
||||||
xml = LXMLBuilder.from_string(document)
|
xml = LXMLBuilder.from_string(document)
|
||||||
@ -400,14 +410,16 @@ class DianXMLExtensionSigner:
|
|||||||
|
|
||||||
id_keyinfo = "xmldsig-%s-KeyInfo" % (id_uuid)
|
id_keyinfo = "xmldsig-%s-KeyInfo" % (id_uuid)
|
||||||
xmlsig.template.add_reference(
|
xmlsig.template.add_reference(
|
||||||
signature, xmlsig.constants.TransformSha256, uri="#%s" % (id_keyinfo), name="xmldsig-%s-ref1" % (id_uuid),
|
signature, xmlsig.constants.TransformSha256, uri="#%s" % (
|
||||||
|
id_keyinfo), name="xmldsig-%s-ref1" % (id_uuid),
|
||||||
)
|
)
|
||||||
ki = xmlsig.template.ensure_key_info(signature, name=id_keyinfo)
|
ki = xmlsig.template.ensure_key_info(signature, name=id_keyinfo)
|
||||||
data = xmlsig.template.add_x509_data(ki)
|
data = xmlsig.template.add_x509_data(ki)
|
||||||
xmlsig.template.x509_data_add_certificate(data)
|
xmlsig.template.x509_data_add_certificate(data)
|
||||||
xmlsig.template.add_key_value(ki)
|
xmlsig.template.add_key_value(ki)
|
||||||
|
|
||||||
qualifying = xades.template.create_qualifying_properties(signature, 'XadesObjects', 'xades')
|
qualifying = xades.template.create_qualifying_properties(
|
||||||
|
signature, 'XadesObjects', 'xades')
|
||||||
xades.utils.ensure_id(qualifying)
|
xades.utils.ensure_id(qualifying)
|
||||||
|
|
||||||
id_props = "xmldsig-%s-signedprops" % (id_uuid)
|
id_props = "xmldsig-%s-signedprops" % (id_uuid)
|
||||||
@ -415,10 +427,12 @@ class DianXMLExtensionSigner:
|
|||||||
signature, xmlsig.constants.TransformSha256, uri="#%s" % (id_props),
|
signature, xmlsig.constants.TransformSha256, uri="#%s" % (id_props),
|
||||||
uri_type="http://uri.etsi.org/01903#SignedProperties"
|
uri_type="http://uri.etsi.org/01903#SignedProperties"
|
||||||
)
|
)
|
||||||
xmlsig.template.add_transform(props_ref, xmlsig.constants.TransformInclC14N)
|
xmlsig.template.add_transform(
|
||||||
|
props_ref, xmlsig.constants.TransformInclC14N)
|
||||||
|
|
||||||
# TODO assert with http://www.sic.gov.co/hora-legal-colombiana
|
# TODO assert with http://www.sic.gov.co/hora-legal-colombiana
|
||||||
props = xades.template.create_signed_properties(qualifying, name=id_props, datetime=datetime.now())
|
props = xades.template.create_signed_properties(
|
||||||
|
qualifying, name=id_props, datetime=datetime.now(tz=Bogota))
|
||||||
xades.template.add_claimed_role(props, "supplier")
|
xades.template.add_claimed_role(props, "supplier")
|
||||||
|
|
||||||
policy = xades.policy.GenericPolicyId(
|
policy = xades.policy.GenericPolicyId(
|
||||||
@ -456,19 +470,18 @@ class DianXMLExtensionAuthorizationProvider(FachoXMLExtension):
|
|||||||
def build(self, fexml):
|
def build(self, fexml):
|
||||||
attrs = {'schemeID': '4', 'schemeName': '31'}
|
attrs = {'schemeID': '4', 'schemeName': '31'}
|
||||||
attrs.update(SCHEME_AGENCY_ATTRS)
|
attrs.update(SCHEME_AGENCY_ATTRS)
|
||||||
|
|
||||||
authorization_provider = fexml.fragment('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:AuthorizationProvider')
|
authorization_provider = fexml.fragment('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:AuthorizationProvider')
|
||||||
authorization_provider.set_element('./sts:AuthorizationProviderID',
|
authorization_provider.set_element('./sts:AuthorizationProviderID',
|
||||||
'800197268',
|
'800197268',
|
||||||
**attrs)
|
**attrs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DianXMLExtensionInvoiceSource(FachoXMLExtension):
|
class DianXMLExtensionInvoiceSource(FachoXMLExtension):
|
||||||
# CAB13
|
# CAB13
|
||||||
def build(self, fexml):
|
def build(self, fexml):
|
||||||
dian_path = '/fe:CreditNote/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource/cbc:IdentificationCode'
|
dian_path = '/fe:CreditNote/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource/cbc:IdentificationCode'
|
||||||
fexml.set_element(dian_path, 'CO',
|
fexml.set_element(
|
||||||
|
dian_path, 'CO',
|
||||||
listAgencyID="6",
|
listAgencyID="6",
|
||||||
listAgencyName="United Nations Economic Commission for Europe",
|
listAgencyName="United Nations Economic Commission for Europe",
|
||||||
listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1")
|
listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1")
|
||||||
@ -501,16 +514,15 @@ class DianXMLExtensionInvoiceAuthorization(FachoXMLExtension):
|
|||||||
invoice_control.set_element('/sts:InvoiceControl/sts:AuthorizedInvoices/sts:To',
|
invoice_control.set_element('/sts:InvoiceControl/sts:AuthorizedInvoices/sts:To',
|
||||||
self.to)
|
self.to)
|
||||||
|
|
||||||
fexml.set_element('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource/cbc:IdentificationCode',
|
fexml.set_element(
|
||||||
|
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource/cbc:IdentificationCode',
|
||||||
'CO',
|
'CO',
|
||||||
#DIAN 1.7.-2020: FAB15
|
# DIAN 1.7.-2020: FAB15
|
||||||
listAgencyID="6",
|
listAgencyID="6",
|
||||||
#DIAN 1.7.-2020: FAB16
|
# DIAN 1.7.-2020: FAB16
|
||||||
listAgencyName="United Nations Economic Commission for Europe",
|
listAgencyName="United Nations Economic Commission for Europe",
|
||||||
#DIAN 1.7.-2020: FAB17
|
# DIAN 1.7.-2020: FAB17
|
||||||
listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1"
|
listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1")
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DianZIP:
|
class DianZIP:
|
||||||
@ -519,7 +531,8 @@ class DianZIP:
|
|||||||
MAX_FILES = 50
|
MAX_FILES = 50
|
||||||
|
|
||||||
def __init__(self, file_like):
|
def __init__(self, file_like):
|
||||||
self.zipfile = zipfile.ZipFile(file_like, mode='w', compression=zipfile.ZIP_DEFLATED)
|
self.zipfile = zipfile.ZipFile(
|
||||||
|
file_like, mode='w', compression=zipfile.ZIP_DEFLATED)
|
||||||
self.num_files = 0
|
self.num_files = 0
|
||||||
|
|
||||||
def add_xml(self, name, xml_data):
|
def add_xml(self, name, xml_data):
|
||||||
@ -540,7 +553,6 @@ class DianZIP:
|
|||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
"""
|
"""
|
||||||
Facilita el uso de esta manera:
|
Facilita el uso de esta manera:
|
||||||
|
|
||||||
f = open('xxx', 'rb')
|
f = open('xxx', 'rb')
|
||||||
with DianZIP(f) as zip:
|
with DianZIP(f) as zip:
|
||||||
zip.add_invoice_xml('name', 'data xml')
|
zip.add_invoice_xml('name', 'data xml')
|
||||||
@ -563,7 +575,7 @@ class DianXMLExtensionSignerVerifier:
|
|||||||
def verify_string(self, document):
|
def verify_string(self, document):
|
||||||
# Obtener FachoXML
|
# Obtener FachoXML
|
||||||
xml = LXMLBuilder.from_string(document)
|
xml = LXMLBuilder.from_string(document)
|
||||||
fachoxml = FachoXML(xml,nsmap=NAMESPACES)
|
fachoxml = FachoXML(xml, nsmap=NAMESPACES)
|
||||||
|
|
||||||
# Obtener Signature
|
# Obtener Signature
|
||||||
signature = fachoxml.builder.xpath(fachoxml.root, '//ds:Signature')
|
signature = fachoxml.builder.xpath(fachoxml.root, '//ds:Signature')
|
||||||
|
Loading…
Reference in New Issue
Block a user