diff --git a/facho/facho.py b/facho/facho.py index e4e17e0..8418459 100644 --- a/facho/facho.py +++ b/facho/facho.py @@ -118,9 +118,12 @@ class FachoXML: # construir las extensiones o adicionar en caso de indicar for extension in self.extensions: xpath, elements = extension.build(self) - for new_element in elements: - elem = self.find_or_create_element('/'+ root_tag + xpath) - self.builder.append(elem, new_element) + if isinstance(elements, str): + elem = self.set_element('/'+ root_tag + xpath, elements) + else: + for new_element in elements: + elem = self.find_or_create_element('/'+ root_tag + xpath) + self.builder.append(elem, new_element) def fragment(self, xpath, append=False): parent = self.find_or_create_element(xpath, append=append) diff --git a/facho/fe/__init__.py b/facho/fe/__init__.py index cb73524..2b35d35 100644 --- a/facho/fe/__init__.py +++ b/facho/fe/__init__.py @@ -1,4 +1,5 @@ from .fe import FeXML from .fe import NAMESPACES from .fe import DianXMLExtensionSigner +from .fe import DianXMLExtensionSoftwareSecurityCode from .fe import DianZIP diff --git a/facho/fe/client/dian.py b/facho/fe/client/dian.py index b5951fa..b2281a8 100644 --- a/facho/fe/client/dian.py +++ b/facho/fe/client/dian.py @@ -76,7 +76,21 @@ class ConsultaResolucionesFacturacionPeticion(SOAPService): def build_response(self, as_dict): return ConsultaResolucionesFacturacionRespuesta.fromdict(as_dict) +@dataclass +class SendBillAsync: + fileName: str + contentFile: str + def get_wsdl(self): + return 'https://colombia-dian-webservices-input-sbx.azurewebsites.net/WcfDianCustomerServices.svc?wsdl' + + def get_service(self): + return 'SendBillAsync' + + def build_response(self, as_dict): + return {} + + class DianClient: def __init__(self, user, password): diff --git a/facho/fe/fe.py b/facho/fe/fe.py index 085d1e4..5300dfd 100644 --- a/facho/fe/fe.py +++ b/facho/fe/fe.py @@ -39,7 +39,23 @@ class FeXML(FachoXML): #self.find_or_create_element(self._cn) - + +class DianXMLExtensionSoftwareSecurityCode: + # RESOLUCION 0001: pagina 535 + + def __init__(self, id_software: str, pin: str, invoice_ident: str): + self.id_software = id_software + self.pin = pin + self.invoice_ident = invoice_ident + + def build(self, fachoxml): + dian_path = '/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareSecurityCode' + code = str(self.id_software) + str(self.pin) + str(self.invoice_ident) + m = hashlib.sha384() + m.update(code.encode('utf-8')) + return dian_path, m.hexdigest() + + class DianXMLExtensionSigner: POLICY_ID = 'https://facturaelectronica.dian.gov.co/politicadefirma/v2/politicadefirmav2.pdf' POLICY_NAME = 'Dian' diff --git a/tests/test_fe.py b/tests/test_fe.py index 939ddd6..956afce 100644 --- a/tests/test_fe.py +++ b/tests/test_fe.py @@ -35,3 +35,13 @@ def test_xmlsigned_with_passphrase_build(): assert elem is not None #assert elem.findall('ds:SignedInfo', fe.NAMESPACES) is not None + + +def test_dian_extension_software_security_code(): + security_code = fe.DianXMLExtensionSoftwareSecurityCode('idsoftware', '1234', '1') + xml = fe.FeXML('Invoice', + 'http://www.dian.gov.co/contratos/facturaelectronica/v1') + xml.add_extension(security_code) + xml.attach_extensions() + content = xml.get_element_text('/fe:Invoice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareSecurityCode') + assert content is not None