facho/fe/fe.py (DianZIP): creado zip nombrando segun DIAN
FossilOrigin-Name: 6df54d31796b2c119bd6b1bfe5f30fafa7adf0521907012509104f4b1e267d7e
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ | |||||||
| __pycache__/ | __pycache__/ | ||||||
| *.py[cod] | *.py[cod] | ||||||
| *$py.class | *$py.class | ||||||
|  | *.bk | ||||||
|  |  | ||||||
| # C extensions | # C extensions | ||||||
| *.so | *.so | ||||||
|   | |||||||
| @@ -191,3 +191,6 @@ class FachoXML: | |||||||
|  |  | ||||||
|     def tostring(self): |     def tostring(self): | ||||||
|         return self.builder.tostring(self.root) |         return self.builder.tostring(self.root) | ||||||
|  |  | ||||||
|  |     def __str__(self): | ||||||
|  |         return self.tostring() | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
| from .fe import DianXMLExtensionSigner | from .fe import DianXMLExtensionSigner | ||||||
| from .fe import FeXML | from .fe import FeXML | ||||||
| from .fe import NAMESPACES | from .fe import NAMESPACES | ||||||
|  | from .fe import DianZIP | ||||||
|   | |||||||
| @@ -6,8 +6,10 @@ import xmlsig | |||||||
| import xades | import xades | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
| import OpenSSL | import OpenSSL | ||||||
|  | import zipfile | ||||||
| import warnings | import warnings | ||||||
|  | import hashlib | ||||||
|  | from contextlib import contextmanager | ||||||
|  |  | ||||||
| NAMESPACES = { | NAMESPACES = { | ||||||
|     'fe': 'http://www.dian.gov.co/contratos/facturaelectronica/v1', |     'fe': 'http://www.dian.gov.co/contratos/facturaelectronica/v1', | ||||||
| @@ -124,3 +126,29 @@ class DianXMLExtensionSigner: | |||||||
|         return (dian_path, [signature]) |         return (dian_path, [signature]) | ||||||
|          |          | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class DianZIP: | ||||||
|  |      | ||||||
|  |     # RESOLUCION 0001: pagina 540 | ||||||
|  |     MAX_FILES = 50 | ||||||
|  |      | ||||||
|  |     def __init__(self, file_like): | ||||||
|  |         self.zipfile = zipfile.ZipFile(file_like, mode='w') | ||||||
|  |         self.num_files = 0 | ||||||
|  |  | ||||||
|  |     def add_invoice_xml(self, name, xml_data): | ||||||
|  |         self.num_files += 1 | ||||||
|  |         # TODO cual es la norma para los nombres de archivos? | ||||||
|  |         m = hashlib.sha256() | ||||||
|  |         m.update(name.encode('utf-8')) | ||||||
|  |         filename = m.hexdigest() + '.xml' | ||||||
|  |         with self.zipfile.open(filename, 'w') as fp: | ||||||
|  |             fp.write(xml_data.encode('utf-8')) | ||||||
|  |  | ||||||
|  |         return filename | ||||||
|  |  | ||||||
|  |     def __enter__(self): | ||||||
|  |         return self | ||||||
|  |  | ||||||
|  |     def __exit__(self, type, value, traceback): | ||||||
|  |         return self.zipfile.close() | ||||||
|   | |||||||
| @@ -128,6 +128,7 @@ class Invoice(DataValidator): | |||||||
|         self.invoice_customer = None |         self.invoice_customer = None | ||||||
|         self.invoice_supplier = None |         self.invoice_supplier = None | ||||||
|         self.invoice_lines = [] |         self.invoice_lines = [] | ||||||
|  |         self.errors = [] | ||||||
|          |          | ||||||
|     def set_period(self, startdate, enddate): |     def set_period(self, startdate, enddate): | ||||||
|         self.invoice_period_start = startdate |         self.invoice_period_start = startdate | ||||||
| @@ -151,7 +152,11 @@ class Invoice(DataValidator): | |||||||
|     def validate(self): |     def validate(self): | ||||||
|         errors_customer = [('customer.%s' % (field), err) for field, err in self.invoice_customer.validate()] |         errors_customer = [('customer.%s' % (field), err) for field, err in self.invoice_customer.validate()] | ||||||
|         errors_supplier = [('supplier.%s' % (field), err) for field, err in self.invoice_customer.validate()] |         errors_supplier = [('supplier.%s' % (field), err) for field, err in self.invoice_customer.validate()] | ||||||
|         return errors_customer + errors_supplier |         self.errors = errors_customer + errors_supplier | ||||||
|  |  | ||||||
|  |     def valid(self): | ||||||
|  |         self.validate() | ||||||
|  |         return not self.errors | ||||||
|  |  | ||||||
|     def _calculate_legal_monetary_total(self): |     def _calculate_legal_monetary_total(self): | ||||||
|         for invline in self.invoice_lines: |         for invline in self.invoice_lines: | ||||||
|   | |||||||
| @@ -7,6 +7,8 @@ | |||||||
|  |  | ||||||
| import pytest | import pytest | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
|  | import io | ||||||
|  | import zipfile | ||||||
|  |  | ||||||
| import facho.fe.form as form | import facho.fe.form as form | ||||||
| from facho import fe | from facho import fe | ||||||
| @@ -50,7 +52,7 @@ def simple_invoice(): | |||||||
|  |  | ||||||
|  |  | ||||||
| def test_invoicesimple_build(simple_invoice): | def test_invoicesimple_build(simple_invoice): | ||||||
|     assert simple_invoice.validate() == [] |     assert simple_invoice.valid() == True | ||||||
|     xml = form.DIANInvoiceXML(simple_invoice) |     xml = form.DIANInvoiceXML(simple_invoice) | ||||||
|  |  | ||||||
|     supplier_name = xml.get_element_text('/fe:Invoice/fe:AccountingSupplierParty/fe:Party/cac:PartyName/cbc:Name') |     supplier_name = xml.get_element_text('/fe:Invoice/fe:AccountingSupplierParty/fe:Party/cac:PartyName/cbc:Name') | ||||||
| @@ -67,14 +69,14 @@ def test_invoicesimple_build(simple_invoice): | |||||||
|  |  | ||||||
|  |  | ||||||
| def test_invoicesimple_build_with_cufe(simple_invoice): | def test_invoicesimple_build_with_cufe(simple_invoice): | ||||||
|     assert simple_invoice.validate() == [] |     assert simple_invoice.valid() == True | ||||||
|     xml = form.DIANInvoiceXML(simple_invoice) |     xml = form.DIANInvoiceXML(simple_invoice) | ||||||
|     cufe = xml.get_element_text('/fe:Invoice/cbc:UUID') |     cufe = xml.get_element_text('/fe:Invoice/cbc:UUID') | ||||||
|     assert cufe != '' |     assert cufe != '' | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_invoicesimple_xml_signed(simple_invoice): | def test_invoicesimple_xml_signed(simple_invoice): | ||||||
|     assert simple_invoice.validate() == [] |     assert simple_invoice.valid() == True | ||||||
|     xml = form.DIANInvoiceXML(simple_invoice) |     xml = form.DIANInvoiceXML(simple_invoice) | ||||||
|  |  | ||||||
|     signer = fe.DianXMLExtensionSigner('./tests/example.p12') |     signer = fe.DianXMLExtensionSigner('./tests/example.p12') | ||||||
| @@ -84,3 +86,14 @@ def test_invoicesimple_xml_signed(simple_invoice): | |||||||
|  |  | ||||||
|     elem = xml.find_or_create_element('/fe:Invoice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/ds:Signature') |     elem = xml.find_or_create_element('/fe:Invoice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/ds:Signature') | ||||||
|     assert elem.text is not None |     assert elem.text is not None | ||||||
|  |  | ||||||
|  | def test_invoicesimple_zip(simple_invoice): | ||||||
|  |     xml_invoice = form.DIANInvoiceXML(simple_invoice) | ||||||
|  |      | ||||||
|  |     zipdata = io.BytesIO() | ||||||
|  |     with fe.DianZIP(zipdata) as dianzip: | ||||||
|  |         name_invoice = dianzip.add_invoice_xml(simple_invoice.invoice_ident, str(xml_invoice)) | ||||||
|  |  | ||||||
|  |     with zipfile.ZipFile(zipdata) as dianzip: | ||||||
|  |         xml_data = dianzip.open(name_invoice).read().decode('utf-8') | ||||||
|  |         assert xml_data == str(xml_invoice) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user