Cancel by rfc of pac

This commit is contained in:
Mauricio Baeza 2021-01-02 22:23:33 -06:00
parent 348a7f6ecb
commit a7945dba58
6 changed files with 71 additions and 16 deletions

View File

@ -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+ - Error: Al parsear XML en Python 3.9+
- Mejora: Agregar versión de Empresa Libre a plantilla. - 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] v 1.39.1 [17-sep-2020]

View File

@ -231,7 +231,7 @@ class PACComercioDigital(object):
return headers 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: if DEBUG or not auth:
auth = AUTH auth = AUTH

View File

@ -47,7 +47,7 @@ logging.getLogger('zeep').setLevel(logging.ERROR)
TIMEOUT = 10 TIMEOUT = 10
DEBUG_SOAP = True DEBUG_SOAP = False
class DebugPlugin(Plugin): class DebugPlugin(Plugin):
@ -113,6 +113,10 @@ class PACFinkok(object):
self._error = 'UUID ' + self.CODE['NE'] self._error = 'UUID ' + self.CODE['NE']
return {} return {}
if ce == 'UUID Not Found':
self._error = 'UUID ' + self.CODE['NE']
return {}
if self.CODE['200'] != ce: if self.CODE['200'] != ce:
log.error('CodEstatus', type(ce), ce) log.error('CodEstatus', type(ce), ce)
return result return result
@ -228,8 +232,8 @@ class PACFinkok(object):
} }
return data return data
def cancel_xml(self, xml, auth={}): def cancel_xml(self, xml, auth={}, cfdi=''):
if not auth: if DEBUG or not auth:
auth = AUTH auth = AUTH
method = 'cancel' method = 'cancel'

View File

@ -50,7 +50,7 @@ from dateutil import parser
from .cfdi_xml import CFDI 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.cfdi_cert import SATCertificate
from .pacs import PACComercioDigital from .pacs import PACComercioDigital
@ -79,6 +79,11 @@ PACS = {
'finkok': PACFinkok, 'finkok': PACFinkok,
'comercio': PACComercioDigital, '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 #~ https://github.com/kennethreitz/requests/blob/v1.2.3/requests/structures.py#L37
class CaseInsensitiveDict(collections.MutableMapping): class CaseInsensitiveDict(collections.MutableMapping):
@ -645,6 +650,13 @@ def make_xml(data, certificado):
return cfdi.add_sello(stamp, cert.cer_txt) 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): def cancel_xml_sign(invoice, auth, certificado):
cert = SATCertificate(certificado.cer, certificado.key_enc.encode()) cert = SATCertificate(certificado.cer, certificado.key_enc.encode())
pac = PACS[auth['pac']]() pac = PACS[auth['pac']]()
@ -658,7 +670,7 @@ def cancel_xml_sign(invoice, auth, certificado):
tree = cert.sign_xml(tree) tree = cert.sign_xml(tree)
sign_xml = ET.tostring(tree).decode() 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: if pac.error:
result = {'ok': False, 'msg': pac.error, 'row': {}} result = {'ok': False, 'msg': pac.error, 'row': {}}
return result return result

View File

@ -404,7 +404,7 @@ class Configuracion(BaseModel):
return values return values
def _get_admin_products(self): def _get_admin_products(self, args={}):
fields = ( fields = (
'chk_config_cuenta_predial', 'chk_config_cuenta_predial',
'chk_config_codigo_barras', 'chk_config_codigo_barras',
@ -419,7 +419,7 @@ class Configuracion(BaseModel):
values = {r.clave: util.get_bool(r.valor) for r in data} values = {r.clave: util.get_bool(r.valor) for r in data}
return values return values
def _get_main_products(self): def _get_main_products(self, args={}):
fields = ( fields = (
'chk_config_cuenta_predial', 'chk_config_cuenta_predial',
'chk_config_codigo_barras', 'chk_config_codigo_barras',
@ -436,7 +436,7 @@ class Configuracion(BaseModel):
values['default_unidad'] = SATUnidades.get_default() values['default_unidad'] = SATUnidades.get_default()
return values return values
def _get_complements(self): def _get_complements(self, args={}):
fields = ( fields = (
'chk_config_ine', 'chk_config_ine',
'chk_config_edu', 'chk_config_edu',
@ -464,7 +464,7 @@ class Configuracion(BaseModel):
return values return values
def _get_folios(self): def _get_folios(self, args={}):
fields = ( fields = (
'chk_folio_custom', 'chk_folio_custom',
) )
@ -475,7 +475,7 @@ class Configuracion(BaseModel):
values = {r.clave: util.get_bool(r.valor) for r in data} values = {r.clave: util.get_bool(r.valor) for r in data}
return values return values
def _get_correo(self): def _get_correo(self, args={}):
fields = ('correo_servidor', 'correo_puerto', 'correo_ssl', fields = ('correo_servidor', 'correo_puerto', 'correo_ssl',
'correo_usuario', 'correo_copia', 'correo_asunto', 'correo_usuario', 'correo_copia', 'correo_asunto',
'correo_mensaje', 'correo_directo', 'correo_confirmacion') 'correo_mensaje', 'correo_directo', 'correo_confirmacion')
@ -486,7 +486,7 @@ class Configuracion(BaseModel):
values = {r.clave: r.valor for r in data} values = {r.clave: r.valor for r in data}
return values return values
def _get_admin_config_users(self): def _get_admin_config_users(self, args={}):
fields = ( fields = (
'chk_users_notify_access', 'chk_users_notify_access',
) )
@ -523,7 +523,7 @@ class Configuracion(BaseModel):
} }
return values return values
def _get_pac_auth(cls): def _get_pac_auth(cls, args={}):
pac = cls.get_('lst_pac').lower() pac = cls.get_('lst_pac').lower()
user = cls.get_(f'user_timbrado_{pac}') user = cls.get_(f'user_timbrado_{pac}')
token = cls.get_(f'token_timbrado_{pac}') token = cls.get_(f'token_timbrado_{pac}')
@ -534,6 +534,17 @@ class Configuracion(BaseModel):
data['pass'] = token data['pass'] = token
return data 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 @classmethod
def get_(cls, keys): def get_(cls, keys):
if isinstance(keys, str): if isinstance(keys, str):
@ -553,10 +564,11 @@ class Configuracion(BaseModel):
'correo', 'correo',
'admin_config_users', 'admin_config_users',
'pac_auth', 'pac_auth',
'auth_by_pac',
) )
opt = keys['fields'] opt = keys['fields']
if opt in options: if opt in options:
return getattr(cls, '_get_{}'.format(opt))(cls) return getattr(cls, f'_get_{opt}')(cls, keys)
if opt == 'pac': if opt == 'pac':
return cls._get_pac(cls, keys['pac']) return cls._get_pac(cls, keys['pac'])
@ -3857,7 +3869,9 @@ class Facturas(BaseModel):
msg = 'Solo es posible cancelar CFDI 3.3' msg = 'Solo es posible cancelar CFDI 3.3'
return {'ok': False, 'msg': msg} 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) certificado = Certificado.get(Certificado.es_fiel==False)
result = utils.cancel_xml_sign(invoice, auth, certificado) result = utils.cancel_xml_sign(invoice, auth, certificado)

View File

@ -233,6 +233,8 @@ VALUES_PDF = {
RFCS = { RFCS = {
'PUBLIC': 'XAXX010101000', 'PUBLIC': 'XAXX010101000',
'FOREIGN': 'XEXX010101000', 'FOREIGN': 'XEXX010101000',
'CVD110412TF6': 'finkok',
'SCD110105654': 'comercio',
} }
URL = { URL = {