Fix: SigningTime tz='America/Bogota'
This commit is contained in:
		| @@ -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') | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user