facho/fe/fe.py (DianZIP): creado zip nombrando segun DIAN

FossilOrigin-Name: 6df54d31796b2c119bd6b1bfe5f30fafa7adf0521907012509104f4b1e267d7e
This commit is contained in:
bit4bit@riseup.net 2020-05-23 20:04:57 +00:00
parent 10f021d0fd
commit 34032e8a45
6 changed files with 57 additions and 6 deletions

1
.gitignore vendored
View File

@ -3,6 +3,7 @@
__pycache__/ __pycache__/
*.py[cod] *.py[cod]
*$py.class *$py.class
*.bk
# C extensions # C extensions
*.so *.so

View File

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

View File

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

View File

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

View File

@ -128,7 +128,8 @@ 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
self.invoice_period_end = enddate self.invoice_period_end = enddate
@ -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:

View File

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