import base64
import requests
from lxml import etree
from lxml.etree import Element
from . import PAC, Accept, Document, CancelReason, CancelationAcknowledgment, Environment, TaxpayerStatus, AcceptRejectAcknowledgment
from .. import ResponseError
from .. import ScalarMap
from .. import __version__, Signer
from ..cfdi import CFDI
from ..create.cancela import cancelacionretencion, cancelacion
from ..create.cancela.aceptacionrechazo import SolicitudAceptacionRechazo
from ..transform.helpers import simple_element
parser = etree.XMLParser(remove_blank_text=True, huge_tree=True)
[docs]class RequestTransaction(ScalarMap):
"""
:param requestor:
:param transaction:
:param country:
:param entity:
:param user:
:param user_name:
:param data1:
:param data2:
:param data3:
"""
tag = '{http://www.fact.com.mx/schema/ws}RequestTransaction'
def __init__(
self,
requestor: str = None,
transaction: str = None,
country: str = None,
entity: str = None,
user: str = None,
user_name: str = None,
data1: str | bytes = None,
data2: str | bytes = None,
data3: str | bytes = None,
):
super().__init__({
'Requestor': requestor,
'Transaction': transaction,
'Country': country,
'Entity': entity,
'User': user,
'UserName': user_name,
'Data1': data1,
'Data2': data2,
'Data3': data3,
})
[docs]def request_transaction(data):
self = Element('{%s}%s' % ('http://www.fact.com.mx/schema/ws', 'RequestTransaction'), nsmap={'ws': 'http://www.fact.com.mx/schema/ws'})
el = data.get('Requestor')
self.append(simple_element('{http://www.fact.com.mx/schema/ws}Requestor', nsmap={'ws': 'http://www.fact.com.mx/schema/ws'}, text=el))
el = data.get('Transaction')
self.append(simple_element('{http://www.fact.com.mx/schema/ws}Transaction', nsmap={'ws': 'http://www.fact.com.mx/schema/ws'}, text=el))
el = data.get('Country')
self.append(simple_element('{http://www.fact.com.mx/schema/ws}Country', nsmap={'ws': 'http://www.fact.com.mx/schema/ws'}, text=el))
el = data.get('Entity')
self.append(simple_element('{http://www.fact.com.mx/schema/ws}Entity', nsmap={'ws': 'http://www.fact.com.mx/schema/ws'}, text=el))
el = data.get('User')
self.append(simple_element('{http://www.fact.com.mx/schema/ws}User', nsmap={'ws': 'http://www.fact.com.mx/schema/ws'}, text=el))
el = data.get('UserName')
self.append(simple_element('{http://www.fact.com.mx/schema/ws}UserName', nsmap={'ws': 'http://www.fact.com.mx/schema/ws'}, text=el))
el = data.get('Data1')
self.append(simple_element('{http://www.fact.com.mx/schema/ws}Data1', nsmap={'ws': 'http://www.fact.com.mx/schema/ws'}, text=el))
el = data.get('Data2')
self.append(simple_element('{http://www.fact.com.mx/schema/ws}Data2', nsmap={'ws': 'http://www.fact.com.mx/schema/ws'}, text=el))
el = data.get('Data3')
self.append(simple_element('{http://www.fact.com.mx/schema/ws}Data3', nsmap={'ws': 'http://www.fact.com.mx/schema/ws'}, text=el))
return self
[docs]def soap_envelope(payload):
self = Element('{%s}%s' % ('http://schemas.xmlsoap.org/soap/envelope/', 'Envelope'), nsmap={'soapenv': 'http://schemas.xmlsoap.org/soap/envelope/'})
self.append(etree.Element('{http://schemas.xmlsoap.org/soap/envelope/}Header', nsmap={'soapenv': 'http://schemas.xmlsoap.org/soap/envelope/'}))
el = etree.Element('{http://schemas.xmlsoap.org/soap/envelope/}Body', nsmap={'soapenv': 'http://schemas.xmlsoap.org/soap/envelope/'})
el.append(payload)
self.append(el)
return self
[docs]class MYSuite(PAC):
"""
PAC de facturacion MYSuite
Documentacion: https://soporte.mysuitemex.com/portal/es/kb/soporte-especializado
"""
RFC = "MSE090205D9A"
def __init__(self, requestor: str, country: str, user_name: str, environment=Environment.PRODUCTION):
super().__init__(environment=environment)
self.requestor = requestor
self.country = country
self.user_name = user_name
[docs] def issue(self, cfdi: CFDI, accept: Accept = Accept.XML) -> Document:
raise NotImplementedError()
[docs] def stamp(self, cfdi: CFDI, accept: Accept = Accept.XML) -> Document:
if accept & Accept.PDF:
raise NotImplementedError("accept PDF not supported")
rfc = cfdi["Emisor"]["Rfc"]
xml = soap_envelope(
request_transaction(
data=RequestTransaction(
requestor=self.requestor,
transaction="TIMBRAR",
country=self.country,
entity=rfc,
user=self.requestor,
user_name=f"{self.country}.{rfc}.{self.user_name}",
data1=base64.b64encode(cfdi.xml_bytes())
)
)
)
match self.environment:
case Environment.PRODUCTION:
host = "https://www.mysuitecfdi.com"
case Environment.TEST:
host = "https://www.mysuitetest.com"
case _:
raise NotImplementedError("Not Supported CFDI")
r = requests.post(
url=f"{host}/mx.com.fact.wsfront/FactWSFront.asmx",
headers={
"User-Agent": __version__.__user_agent__,
"Content-Type": "text/xml; charset=utf-8",
"SOAPAction": "http://www.fact.com.mx/schema/ws/RequestTransaction"
},
data=etree.tostring(xml, xml_declaration=False, encoding="UTF-8", pretty_print=True)
)
if not r.ok:
raise ResponseError(r)
result = etree.fromstring(
r.content,
parser=parser
)
res_data_1 = result.find('{*}Body/{*}RequestTransactionResponse/{*}RequestTransactionResult/{*}ResponseData/{*}ResponseData1').text
res_data_1 = base64.b64decode(res_data_1)
timbre = CFDI.from_string(res_data_1)
if cfdi.get("Complemento") is None:
cfdi["Complemento"] = {}
cfdi["Complemento"]["TimbreFiscalDigital"] = timbre
return Document(
document_id=timbre['UUID'],
xml=cfdi.xml_bytes() if accept & Accept.XML 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()
[docs] def status(self, cfdi: CFDI) -> dict:
raise NotImplementedError()
[docs] def validate(self, cfdi: CFDI):
raise NotImplementedError()
[docs] def cancel_comprobante(self, cancelation: cancelacion.Cancelacion) -> CancelationAcknowledgment:
raise NotImplementedError()
[docs] def cancel_retencion(self, cancelation: cancelacionretencion.Cancelacion) -> CancelationAcknowledgment:
raise NotImplementedError()
[docs] def accept_reject(self, request: SolicitudAceptacionRechazo) -> AcceptRejectAcknowledgment:
raise NotImplementedError()
[docs] def pending(self, rfc: str) -> list[str]:
raise NotImplementedError()
[docs] def list_69b(self, rfc: str) -> TaxpayerStatus | None:
raise NotImplementedError()