forked from elmau/empresa-libre
Cancel by rfc of pac
This commit is contained in:
parent
348a7f6ecb
commit
a7945dba58
25
CHANGELOG.md
25
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+
|
- 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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -233,6 +233,8 @@ VALUES_PDF = {
|
||||||
RFCS = {
|
RFCS = {
|
||||||
'PUBLIC': 'XAXX010101000',
|
'PUBLIC': 'XAXX010101000',
|
||||||
'FOREIGN': 'XEXX010101000',
|
'FOREIGN': 'XEXX010101000',
|
||||||
|
'CVD110412TF6': 'finkok',
|
||||||
|
'SCD110105654': 'comercio',
|
||||||
}
|
}
|
||||||
|
|
||||||
URL = {
|
URL = {
|
||||||
|
|
Loading…
Reference in New Issue