Agregar método alternativo para cancelar

This commit is contained in:
Mauricio Baeza 2017-12-23 22:21:35 -06:00
parent 9796ffb6f3
commit 3a10c146d2
6 changed files with 115 additions and 43 deletions

View File

@ -49,6 +49,7 @@ def finkok(debug):
PASS = FINKOK['PASS']
TOKEN = ''
auth = {
'DEBUG': debug,
'USER': '',
'PASS': TOKEN or PASS,
'RESELLER': {'USER': USER, 'PASS': PASS}
@ -58,6 +59,7 @@ def finkok(debug):
PASS = ''
TOKEN = '5c9a88da105bff9a8c430cb713f6d35269f51674bdc5963c1501b7316366'
auth = {
'DEBUG': debug,
'USER': USER,
'PASS': TOKEN or PASS,
'RESELLER': {

View File

@ -324,21 +324,16 @@ class Finkok(object):
try:
result = client.service.query_pending(
self._auth['USER'], self._auth['PASS'], uuid)
#~ print (result.date)
#~ tree = parseString(unescape(result.xml))
#~ response = tree.toprettyxml(encoding='utf-8').decode('utf-8')
return result.status
except Fault as e:
self.error = str(e)
return ''
def cancel_xml(self, rfc, uuids, path_cer, path_key):
for u in uuids:
if not self._validate_uuid(u):
return ''
def cancel_xml(self, rfc, uuid, cer, key):
# ~ for u in uuids:
# ~ if not self._validate_uuid(u):
# ~ return ''
cer = self._load_file(path_cer)
key = self._load_file(path_key)
method = 'cancel'
client = Client(
URL[method], transport=self._transport, plugins=self._plugins)
@ -346,7 +341,7 @@ class Finkok(object):
sa = client.get_type('ns0:stringArray')
args = {
'UUIDS': uuid_type(uuids=sa(string=uuids)),
'UUIDS': uuid_type(uuids=sa(string=uuid)),
'username': self._auth['USER'],
'password': self._auth['PASS'],
'taxpayer_id': rfc,

View File

@ -41,7 +41,8 @@ from settings import DEBUG, MV, log, template_lookup, COMPANIES, DB_SAT, \
PATH_XSLT, PATH_XSLTPROC, PATH_OPENSSL, PATH_TEMPLATES, PATH_MEDIA, PRE, \
PATH_XMLSEC, TEMPLATE_CANCEL, DEFAULT_SAT_PRODUCTO, DECIMALES, DIR_FACTURAS
from settings import SEAFILE_SERVER
from settings import SEAFILE_SERVER, USAR_TOKEN
from .configpac import AUTH
def _call(args):
@ -416,7 +417,7 @@ class Certificado(object):
data['hasta'] = hasta.replace(tzinfo=None)
return data
def _get_p12(self, password, rfc):
def _get_p12(self, password, rfc, token):
tmp_cer = tempfile.mkstemp()[1]
tmp_key = tempfile.mkstemp()[1]
tmp_p12 = tempfile.mkstemp()[1]
@ -428,7 +429,7 @@ class Certificado(object):
args = 'openssl pkcs12 -export -in "{}" -inkey "{}" -name "{}" ' \
'-passout pass:"{}" -out "{}"'
_call(args.format(tmp_cer, tmp_key, rfc, _get_md5(rfc), tmp_p12))
_call(args.format(tmp_cer, tmp_key, rfc, token, tmp_p12))
data = read_file(tmp_p12)
self._kill(tmp_cer)
@ -437,7 +438,7 @@ class Certificado(object):
return data
def _get_info_key(self, password, rfc):
def _get_info_key(self, password, rfc, token):
data = {}
args = 'openssl pkcs8 -inform DER -in "{}" -passin pass:"{}"'
@ -455,17 +456,23 @@ class Certificado(object):
self.error = 'Los archivos no son pareja'
return data
args = 'openssl pkcs8 -inform DER -in "{}" -passin pass:"{}" | ' \
'openssl rsa -des3 -passout pass:"{}"'.format(
self._path_key, password, _get_md5(rfc))
args = "openssl pkcs8 -inform DER -in '{}' -passin pass:'{}' | " \
"openssl rsa -des3 -passout pass:'{}'".format(
self._path_key, password, token)
key_enc = _call(args)
data['key'] = read_file(self._path_key)
data['key_enc'] = key_enc
data['p12'] = self._get_p12(password, rfc)
data['p12'] = self._get_p12(password, rfc, token)
return data
def validate(self, password, rfc):
def validate(self, password, rfc, auth):
token = _get_md5(rfc)
if USAR_TOKEN:
token = auth['PASS']
if AUTH['DEBUG']:
token = AUTH['PASS']
if not self._path_key or not self._path_cer:
self.error = 'Error en las rutas temporales del certificado'
return {}
@ -474,7 +481,7 @@ class Certificado(object):
if not data:
return {}
llave = self._get_info_key(password, data['rfc'])
llave = self._get_info_key(password, rfc, token)
if not llave:
return {}
@ -485,9 +492,15 @@ class Certificado(object):
return data
def make_xml(data, certificado):
def make_xml(data, certificado, auth):
from .cfdi_xml import CFDI
token = _get_md5(certificado.rfc)
if USAR_TOKEN:
token = auth['PASS']
if AUTH['DEBUG']:
token = AUTH['PASS']
if DEBUG:
data['emisor']['Rfc'] = certificado.rfc
data['emisor']['RegimenFiscal'] = '603'
@ -501,7 +514,7 @@ def make_xml(data, certificado):
'xml': save_temp(xml, 'w'),
'openssl': PATH_OPENSSL,
'key': save_temp(certificado.key_enc, 'w'),
'pass': _get_md5(certificado.rfc)
'pass': token,
}
args = '"{xsltproc}" "{xslt}" "{xml}" | ' \
'"{openssl}" dgst -sha256 -sign "{key}" -passin pass:"{pass}" | ' \
@ -517,13 +530,10 @@ def make_xml(data, certificado):
def timbra_xml(xml, auth):
from .pac import Finkok as PAC
if DEBUG:
auth = {}
else:
if not auth:
msg = 'Sin datos para timbrar'
result = {'ok': True, 'error': msg}
return result
if not DEBUG and not auth:
msg = 'Sin datos para timbrar'
result = {'ok': True, 'error': msg}
return result
result = {'ok': True, 'error': ''}
pac = PAC(auth)
@ -1299,9 +1309,46 @@ def upload_file(rfc, opt, file_obj):
return {'status': 'error', 'ok': False}
def cancel_cfdi(uuid, pk12, rfc, auth):
def cancel_xml(auth, uuid, certificado):
from .pac import Finkok as PAC
if DEBUG:
auth = {}
else:
if not auth:
msg = 'Sin datos para cancelar'
data = {'ok': False, 'error': msg}
return data, result
msg = 'Factura cancelada correctamente'
data = {'ok': True, 'msg': msg, 'row': {'estatus': 'Cancelada'}}
pac = PAC(auth)
result = pac.cancel_xml(certificado.rfc, str(uuid).upper(),
certificado.cer_pem.encode(), certificado.key_enc.encode())
if result:
codes = {None: '',
'Could not get UUID Text': 'UUID no encontrado',
'Invalid Passphrase': 'Contraseña inválida',
}
if not result['CodEstatus'] is None:
data['ok'] = False
data['msg'] = codes.get(result['CodEstatus'], result['CodEstatus'])
else:
data['ok'] = False
data['msg'] = pac.error
return data, result
def cancel_signature(uuid, pk12, rfc, auth):
from .pac import Finkok as PAC
token = _get_md5(rfc)
if USAR_TOKEN:
token = auth['PASS']
if AUTH['DEBUG']:
token = AUTH['PASS']
template = read_file(TEMPLATE_CANCEL, 'r')
data = {
'rfc': rfc,
@ -1313,7 +1360,7 @@ def cancel_cfdi(uuid, pk12, rfc, auth):
data = {
'xmlsec': PATH_XMLSEC,
'pk12': save_temp(pk12),
'pass': _get_md5(rfc),
'pass': token,
'template': save_temp(template, 'w'),
}
args = '"{xmlsec}" --sign --pkcs12 "{pk12}" --pwd {pass} ' \

View File

@ -16,7 +16,8 @@ if __name__ == '__main__':
from controllers import util
from settings import log, VERSION, PATH_CP, COMPANIES, PRE, CURRENT_CFDI, \
INIT_VALUES, DEFAULT_PASSWORD, DECIMALES, IMPUESTOS, DEFAULT_SAT_PRODUCTO
INIT_VALUES, DEFAULT_PASSWORD, DECIMALES, IMPUESTOS, DEFAULT_SAT_PRODUCTO, \
CANCEL_SIGNATURE
FORMAT = '{0:.2f}'
@ -639,7 +640,8 @@ class Certificado(BaseModel):
obj = cls.get_(cls)
paths = Configuracion.get_({'fields': 'path_cer'})
cert = util.Certificado(paths)
data = cert.validate(values['contra'], session['rfc'])
auth = Emisor.get_auth()
data = cert.validate(values['contra'], session['rfc'], auth)
if data:
msg = 'Certificado guardado correctamente'
q = Certificado.update(**data).where(Certificado.id==obj.id)
@ -2202,11 +2204,34 @@ class Facturas(BaseModel):
@classmethod
def cancel(cls, id):
if CANCEL_SIGNATURE:
return cls._cancel_signature(cls, id)
return cls._cancel_xml(cls, id)
def _cancel_xml(self, id):
msg = 'Factura cancelada correctamente'
auth = Emisor.get_auth()
certificado = Certificado.select()[0]
obj = Facturas.get(Facturas.id==id)
data, result = util.cancel_cfdi(
data, result = util.cancel_xml(auth, obj.uuid, certificado)
if data['ok']:
obj.estatus = 'Cancelada'
obj.error = ''
obj.cancelada = True
obj.fecha_cancelacion = result['Fecha']
obj.acuse = result['Acuse']
self._actualizar_saldo_cliente(self, obj, True)
else:
obj.error = data['msg']
obj.save()
return data
def _cancel_signature(self, id):
msg = 'Factura cancelada correctamente'
auth = Emisor.get_auth()
certificado = Certificado.select()[0]
obj = Facturas.get(Facturas.id==id)
data, result = util.cancel_signature(
obj.uuid, certificado.p12, certificado.rfc, auth)
if data['ok']:
obj.estatus = 'Cancelada'
@ -2214,6 +2239,7 @@ class Facturas(BaseModel):
obj.cancelada = True
obj.fecha_cancelacion = result['Fecha']
obj.acuse = result['Acuse']
self._actualizar_saldo_cliente(self, obj, True)
else:
obj.error = data['msg']
obj.save()
@ -2395,7 +2421,7 @@ class Facturas(BaseModel):
return
@util.run_in_thread
def _actualizar_saldo_cliente(self, invoice):
def _actualizar_saldo_cliente(self, invoice, cancel=False):
if invoice.tipo_comprobante == 'T':
return
@ -2406,6 +2432,9 @@ class Facturas(BaseModel):
if invoice.tipo_comprobante == 'E':
importe *= -1
if cancel:
importe *= -1
q = (Socios
.update(saldo_cliente=Socios.saldo_cliente + importe)
.where(Socios.id==invoice.cliente.id)
@ -2811,7 +2840,7 @@ class Facturas(BaseModel):
data = {'ok': True, 'row': row, 'new': True, 'error': False, 'msg': msg}
return data
def _make_xml(self, invoice):
def _make_xml(self, invoice, auth):
emisor = Emisor.select()[0]
certificado = Certificado.select()[0]
@ -3000,7 +3029,7 @@ class Facturas(BaseModel):
'donativo': donativo,
'complementos': complementos,
}
return util.make_xml(data, certificado)
return util.make_xml(data, certificado, auth)
@classmethod
def get_status_sat(cls, id):
@ -3047,13 +3076,13 @@ class Facturas(BaseModel):
@classmethod
def timbrar(cls, id):
auth = Emisor.get_auth()
obj = Facturas.get(Facturas.id == id)
obj.xml = cls._make_xml(cls, obj)
obj.xml = cls._make_xml(cls, obj, auth)
obj.estatus = 'Generada'
obj.save()
enviar_correo = util.get_bool(Configuracion.get_('correo_directo'))
auth = Emisor.get_auth()
anticipo = False
msg = 'Factura timbrada correctamente'

View File

@ -115,4 +115,6 @@ IMPUESTOS = {
'CEDULAR': '000',
}
DEFAULT_SAT_PRODUCTO = '01010101'
DIR_FACTURAS = 'facturas'
DIR_FACTURAS = 'facturas'
USAR_TOKEN = False
CANCEL_SIGNATURE = True

View File

@ -511,9 +511,6 @@ var tab_options = {
view: 'tabview',
id: 'tab_options',
multiview: true,
//~ tabbar: {options: [
//~ 'Plantillas',
//~ 'Otros']},
animate: true,
cells: [
{id: 'Plantillas', rows: options_templates},