diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fd5b75..991c7a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,30 @@ -v 1.40.0 [20-12-2020] +v 1.40.0 [04-ene-2021] ---------------------- - Error: Al parsear XML en Python 3.9+ - Mejora: Agregar versión de Empresa Libre a plantilla. + - Mejora: Sellado en memoria + - Mejora: Se agrega un segundo PAC y se refactoriza el timbrado. + +* **IMPORTANTE** + +Es necesario seguir una serie de pasos **obligatorios** para migrar a esta +versión, **no continues hasta seguir paso a paso** estas instrucciones. +**Antes** de comenzar ten a la mano tus certificados de sello para timbrar, es +necesario subirlos de nuevo. **NO actualices si no tienes tus certificados** +con su respectiva contraseña, te quedarás sin poder timbrar. + +1. Entra a la parte administrativa y toma de tus credenciales de timbrado en el +menú "Emisor" ficha "Otros Datos", usuario y token de timbrado. +1. Agregar nuevo requerimiento `pip install xmlsec` +1. Actualizar `git pull origin master` +1. Entrar a `app/controllers/pacs` y copiar `conf.py.example` a `conf.py` +1. Reiniciar el servicio: `sudo systemctl restart empresalibre` +1. Sube de nuevo tus certificados en el menú "Emisor" ficha "Certificado". +1. Ve al menú "Opciones", ficha "Otros". +1. Selecciona tu PAC, si tu usuario es un correo electrónico, invariablemente +debes seleccionar Finkok. +1. Establece las credenciales del punto 1. +1. Guarda los datos. v 1.39.1 [17-sep-2020] diff --git a/source/app/controllers/pacs/comerciodigital/comercio.py b/source/app/controllers/pacs/comerciodigital/comercio.py index cc38b90..b9aea73 100644 --- a/source/app/controllers/pacs/comerciodigital/comercio.py +++ b/source/app/controllers/pacs/comerciodigital/comercio.py @@ -231,7 +231,7 @@ class PACComercioDigital(object): return headers - def cancel_xml(self, cfdi, xml, auth={}, info={'tipo': 'cfdi3.3'}): + def cancel_xml(self, xml, auth={}, cfdi='', info={'tipo': 'cfdi3.3'}): if DEBUG or not auth: auth = AUTH diff --git a/source/app/controllers/pacs/finkok/finkok.py b/source/app/controllers/pacs/finkok/finkok.py index 8c7e16b..76c936a 100644 --- a/source/app/controllers/pacs/finkok/finkok.py +++ b/source/app/controllers/pacs/finkok/finkok.py @@ -47,7 +47,7 @@ logging.getLogger('zeep').setLevel(logging.ERROR) TIMEOUT = 10 -DEBUG_SOAP = True +DEBUG_SOAP = False class DebugPlugin(Plugin): @@ -113,6 +113,10 @@ class PACFinkok(object): self._error = 'UUID ' + self.CODE['NE'] return {} + if ce == 'UUID Not Found': + self._error = 'UUID ' + self.CODE['NE'] + return {} + if self.CODE['200'] != ce: log.error('CodEstatus', type(ce), ce) return result @@ -228,8 +232,8 @@ class PACFinkok(object): } return data - def cancel_xml(self, xml, auth={}): - if not auth: + def cancel_xml(self, xml, auth={}, cfdi=''): + if DEBUG or not auth: auth = AUTH method = 'cancel' diff --git a/source/app/controllers/utils.py b/source/app/controllers/utils.py index 76275ff..3300f05 100644 --- a/source/app/controllers/utils.py +++ b/source/app/controllers/utils.py @@ -50,7 +50,7 @@ from dateutil import parser from .cfdi_xml import CFDI -from settings import DEBUG, DB_COMPANIES, PATHS, TEMPLATE_CANCEL +from settings import DEBUG, DB_COMPANIES, PATHS, TEMPLATE_CANCEL, RFCS from .pacs.cfdi_cert import SATCertificate from .pacs import PACComercioDigital @@ -79,6 +79,11 @@ PACS = { 'finkok': PACFinkok, 'comercio': PACComercioDigital, } +NS_CFDI = { + 'cfdi': 'http://www.sat.gob.mx/cfd/3', + 'tdf': 'http://www.sat.gob.mx/TimbreFiscalDigital', +} + #~ https://github.com/kennethreitz/requests/blob/v1.2.3/requests/structures.py#L37 class CaseInsensitiveDict(collections.MutableMapping): @@ -645,6 +650,13 @@ def make_xml(data, certificado): return cfdi.add_sello(stamp, cert.cer_txt) +def get_pac_by_rfc(cfdi): + tree = ET.fromstring(cfdi.encode()) + path = 'string(//cfdi:Complemento/tdf:TimbreFiscalDigital/@RfcProvCertif)' + rfc_pac = tree.xpath(path, namespaces=NS_CFDI) + return RFCS[rfc_pac] + + def cancel_xml_sign(invoice, auth, certificado): cert = SATCertificate(certificado.cer, certificado.key_enc.encode()) pac = PACS[auth['pac']]() @@ -658,7 +670,7 @@ def cancel_xml_sign(invoice, auth, certificado): tree = cert.sign_xml(tree) sign_xml = ET.tostring(tree).decode() - result = pac.cancel_xml(invoice.xml, sign_xml, auth) + result = pac.cancel_xml(sign_xml, auth, invoice.xml) if pac.error: result = {'ok': False, 'msg': pac.error, 'row': {}} return result diff --git a/source/app/models/main.py b/source/app/models/main.py index 255a754..5e5d3db 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -404,7 +404,7 @@ class Configuracion(BaseModel): return values - def _get_admin_products(self): + def _get_admin_products(self, args={}): fields = ( 'chk_config_cuenta_predial', 'chk_config_codigo_barras', @@ -419,7 +419,7 @@ class Configuracion(BaseModel): values = {r.clave: util.get_bool(r.valor) for r in data} return values - def _get_main_products(self): + def _get_main_products(self, args={}): fields = ( 'chk_config_cuenta_predial', 'chk_config_codigo_barras', @@ -436,7 +436,7 @@ class Configuracion(BaseModel): values['default_unidad'] = SATUnidades.get_default() return values - def _get_complements(self): + def _get_complements(self, args={}): fields = ( 'chk_config_ine', 'chk_config_edu', @@ -464,7 +464,7 @@ class Configuracion(BaseModel): return values - def _get_folios(self): + def _get_folios(self, args={}): fields = ( 'chk_folio_custom', ) @@ -475,7 +475,7 @@ class Configuracion(BaseModel): values = {r.clave: util.get_bool(r.valor) for r in data} return values - def _get_correo(self): + def _get_correo(self, args={}): fields = ('correo_servidor', 'correo_puerto', 'correo_ssl', 'correo_usuario', 'correo_copia', 'correo_asunto', 'correo_mensaje', 'correo_directo', 'correo_confirmacion') @@ -486,7 +486,7 @@ class Configuracion(BaseModel): values = {r.clave: r.valor for r in data} return values - def _get_admin_config_users(self): + def _get_admin_config_users(self, args={}): fields = ( 'chk_users_notify_access', ) @@ -523,7 +523,7 @@ class Configuracion(BaseModel): } return values - def _get_pac_auth(cls): + def _get_pac_auth(cls, args={}): pac = cls.get_('lst_pac').lower() user = cls.get_(f'user_timbrado_{pac}') token = cls.get_(f'token_timbrado_{pac}') @@ -534,6 +534,17 @@ class Configuracion(BaseModel): data['pass'] = token return data + def _get_auth_by_pac(cls, args): + pac = args['pac'] + user = cls.get_(f'user_timbrado_{pac}') + token = cls.get_(f'token_timbrado_{pac}') + data = {} + if pac and user and token: + data['pac'] = pac + data['user'] = user + data['pass'] = token + return data + @classmethod def get_(cls, keys): if isinstance(keys, str): @@ -553,10 +564,11 @@ class Configuracion(BaseModel): 'correo', 'admin_config_users', 'pac_auth', + 'auth_by_pac', ) opt = keys['fields'] if opt in options: - return getattr(cls, '_get_{}'.format(opt))(cls) + return getattr(cls, f'_get_{opt}')(cls, keys) if opt == 'pac': return cls._get_pac(cls, keys['pac']) @@ -3857,7 +3869,9 @@ class Facturas(BaseModel): msg = 'Solo es posible cancelar CFDI 3.3' return {'ok': False, 'msg': msg} - auth = Configuracion.get_({'fields': 'pac_auth'}) + pac = utils.get_pac_by_rfc(invoice.xml) + auth = Configuracion.get_({'fields': 'auth_by_pac', 'pac': pac}) + certificado = Certificado.get(Certificado.es_fiel==False) result = utils.cancel_xml_sign(invoice, auth, certificado) diff --git a/source/app/settings.py b/source/app/settings.py index aeda751..9b7af37 100644 --- a/source/app/settings.py +++ b/source/app/settings.py @@ -233,6 +233,8 @@ VALUES_PDF = { RFCS = { 'PUBLIC': 'XAXX010101000', 'FOREIGN': 'XEXX010101000', + 'CVD110412TF6': 'finkok', + 'SCD110105654': 'comercio', } URL = {