import base64
from typing import Sequence
import requests
from requests.auth import HTTPBasicAuth
from . import PAC, Accept, Document, CancelReason, CancelationAcknowledgment, Environment
from .. import __version__, ResponseError, Signer
from ..cfdi import CFDI
[docs]class Prodigia(PAC):
"""
PAC de facturacion Prodigia
Documentacion: https://www.prodigia.com.mx/api-de-integracion-para-timbrado-de-cfdi
"""
RFC = "PPD101129EA3"
def __init__(self, user: str, password: str, contrato: str, environment=Environment.PRODUCTION):
super().__init__(environment=environment)
self.auth = HTTPBasicAuth(user, password)
self.contrato = contrato
def _request(self, path, data, method="post", opciones: Sequence[str] = None):
host = "https://timbrado.pade.mx/servicio/rest"
params = [
("contrato", self.contrato),
("opciones", 'RESPUESTA_JSON'),
("opciones", 'REGRESAR_CON_ERROR_307_XML'),
]
if opciones:
for o in opciones:
params.append(
('opciones', o)
)
r = requests.request(
method=method,
url=f"{host}/{path}",
headers={
"User-Agent": __version__.__user_agent__,
"Content-Type": "application/xml",
# Nota: Header de Accept genera errores
},
data=data,
auth=self.auth,
params=params
)
if r.ok:
return r
else:
raise ResponseError(r)
[docs] def issue(self, cfdi: CFDI, accept: Accept = Accept.XML) -> Document:
pass
[docs] def stamp(self, cfdi: CFDI, accept: Accept = Accept.XML) -> Document:
match (self.environment, cfdi.tag):
case Environment.PRODUCTION, '{http://www.sat.gob.mx/cfd/4}Comprobante':
path = "timbrado40"
case Environment.TEST, '{http://www.sat.gob.mx/cfd/4}Comprobante':
path = "timbrado40Prueba"
case _:
raise NotImplementedError("Not Supported CFDI")
opciones = []
if accept & Accept.PDF:
opciones.append("GENERAR_PDF")
res = self._request(
path=path,
data=cfdi.xml_bytes(),
opciones=opciones
).json()
servicio_timbrado = res['servicioTimbrado']
if not servicio_timbrado['timbradoOk']:
raise ResponseError(res)
return Document(
document_id=servicio_timbrado['UUID'],
xml=base64.b64decode(servicio_timbrado['xmlBase64']) if accept & Accept.XML else None,
pdf=base64.b64decode(servicio_timbrado['pdfBase64']) if accept & Accept.PDF else None
)
[docs] def cancel(self, cfdi: CFDI, reason: CancelReason, substitution_id: str = None, signer: Signer = None) -> CancelationAcknowledgment:
raise NotImplementedError()
[docs] def recover(self, document_id: str, accept: Accept = Accept.XML) -> Document:
raise NotImplementedError()
[docs] def rfc_valid(self, rfc: str | list[str]) -> bool | list[bool]:
raise NotImplementedError()