import zeep from zeep.wsse.username import UsernameToken from .wsse.signature import BinarySignature import xmlsec from dataclasses import dataclass, asdict __all__ = ['DianClient'] class SOAPService: def get_wsdl(self): raise NotImplementedError() def get_service(self): raise NotImplementedError() def build_response(self, as_dict): raise NotImplementedError() def todict(self): return asdict(self) @dataclass class GetNumberingRangeResponse: @dataclass class NumberRangeResponse: ResolutionNumber: str ResolutionDate: str Prefix: str FromNumber: int ToNumber: int ValidateDateFrom: str ValidateDateTo: str TechnicalKey: str NumberRangeResponse: list[NumberRangeResponse] @classmethod def fromdict(cls, data): return cls( data['NumberRangeResponse'] ) @dataclass class GetNumberingRange(SOAPService): accountCode: str accountCodeT: str softwareCode: str def get_wsdl(self): return 'https://vpfe.dian.gov.co/WcfDianCustomerServices.svc?wsdl' def get_service(self): return 'GetNumberingRange' def build_response(self, as_dict): return GetNumberingRangeResponse.fromdict(as_dict) @dataclass class SendBillAsync(SOAPService): fileName: str contentFile: str def get_wsdl(self): return 'https://vpfe.dian.gov.co/WcfDianCustomerServices.svc?wsdl' def get_service(self): return 'SendBillAsync' def build_response(self, as_dict): return as_dict @dataclass class SendTestSetAsyncResponse: ZipKey: str ErrorMessageList: list[str] @classmethod def fromdict(cls, data): return cls( data['ZipKey'], data['ErrorMessageList'] or [] ) @dataclass class SendTestSetAsync(SOAPService): fileName: str contentFile: str testSetId: str = '' def get_wsdl(self): return 'https://vpfe.dian.gov.co/WcfDianCustomerServices.svc?wsdl' def get_service(self): return 'SendTestSetAsync' def build_response(self, as_dict): return SendTestSetAsyncResponse.fromdict(as_dict) @dataclass class SendBillSync(SOAPService): fileName: str contentFile: bytes def get_wsdl(self): return 'https://vpfe.dian.gov.co/WcfDianCustomerServices.svc?wsdl' def get_service(self): return 'SendBillSync' def build_response(self, as_dict): return as_dict @dataclass class GetStatusResponse: IsValid: bool StatusDescription: str StatusCode: int ErrorMessage: list[str] @classmethod def fromdict(cls, data): if data['ErrorMessage']: error_message = data['ErrorMessage']['string'] else: error_message = None return cls(data['IsValid'], data['StatusDescription'], data['StatusCode'], error_message) @dataclass class GetStatus(SOAPService): trackId: bytes def get_wsdl(self): return 'https://vpfe.dian.gov.co/WcfDianCustomerServices.svc?wsdl' def get_service(self): return 'GetStatus' def build_response(self, as_dict): return GetStatusResponse.fromdict(as_dict) @dataclass class GetStatusZip(SOAPService): trackId: bytes def get_wsdl(self): return 'https://vpfe.dian.gov.co/WcfDianCustomerServices.svc?wsdl' def get_service(self): return 'GetStatusZip' def build_response(self, as_dict): return GetStatusResponse.fromdict(as_dict[0]) @dataclass class SendNominaSync(SOAPService): contentFile: bytes def get_wsdl(self): return 'https://vpfe.dian.gov.co/WcfDianCustomerServices.svc?wsdl' def get_service(self): return 'SendNominaSync' def build_response(self, as_dict): return as_dict class Habilitacion: WSDL = 'https://vpfe-hab.dian.gov.co/WcfDianCustomerServices.svc?wsdl' class GetNumberingRange(GetNumberingRange): def get_wsdl(self): return Habilitacion.WSDL class SendBillAsync(SendBillAsync): def get_wsdl(self): return Habilitacion.WSDL class SendBillSync(SendBillSync): def get_wsdl(self): return Habilitacion.WSDL class SendTestSetAsync(SendTestSetAsync): def get_wsdl(self): return Habilitacion.WSDL class GetStatus(GetStatus): def get_wsdl(self): return Habilitacion.WSDL class GetStatusZip(GetStatusZip): def get_wsdl(self): return Habilitacion.WSDL class SendNominaSync(SendNominaSync): def get_wsdl(self): return Habilitacion.WSDL class DianGateway: def _open(self, service): raise NotImplementedError() def _remote_service(self, conn, service): return conn.service[service.get_service()] def _close(self, conn): return def request(self, service): if not isinstance(service, SOAPService): raise TypeError('service not type SOAPService') client = self._open(service) method = self._remote_service(client, service) resp = method(**service.todict()) self._close(client) return service.build_response(resp) class DianClient(DianGateway): def __init__(self, user, password): self._username = user self._password = password def _open(self, service): return zeep.Client( service.get_wsdl(), wsse=UsernameToken( self._username, self._password)) class DianSignatureClient(DianGateway): def __init__(self, private_key_path, public_key_path, password=None): self.private_key_path = private_key_path self.public_key_path = public_key_path self.password = password def _open(self, service): # RESOLUCCION 0004: pagina 756 client = zeep.Client(service.get_wsdl(), wsse=BinarySignature( self.private_key_path, self.public_key_path, self.password, signature_method=xmlsec.Transform.RSA_SHA256, digest_method=xmlsec.Transform.SHA256), ) return client