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

View File

@ -324,21 +324,16 @@ class Finkok(object):
try: try:
result = client.service.query_pending( result = client.service.query_pending(
self._auth['USER'], self._auth['PASS'], uuid) 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 return result.status
except Fault as e: except Fault as e:
self.error = str(e) self.error = str(e)
return '' return ''
def cancel_xml(self, rfc, uuids, path_cer, path_key): def cancel_xml(self, rfc, uuid, cer, key):
for u in uuids: # ~ for u in uuids:
if not self._validate_uuid(u): # ~ if not self._validate_uuid(u):
return '' # ~ return ''
cer = self._load_file(path_cer)
key = self._load_file(path_key)
method = 'cancel' method = 'cancel'
client = Client( client = Client(
URL[method], transport=self._transport, plugins=self._plugins) URL[method], transport=self._transport, plugins=self._plugins)
@ -346,7 +341,7 @@ class Finkok(object):
sa = client.get_type('ns0:stringArray') sa = client.get_type('ns0:stringArray')
args = { args = {
'UUIDS': uuid_type(uuids=sa(string=uuids)), 'UUIDS': uuid_type(uuids=sa(string=uuid)),
'username': self._auth['USER'], 'username': self._auth['USER'],
'password': self._auth['PASS'], 'password': self._auth['PASS'],
'taxpayer_id': rfc, '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_XSLT, PATH_XSLTPROC, PATH_OPENSSL, PATH_TEMPLATES, PATH_MEDIA, PRE, \
PATH_XMLSEC, TEMPLATE_CANCEL, DEFAULT_SAT_PRODUCTO, DECIMALES, DIR_FACTURAS 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): def _call(args):
@ -416,7 +417,7 @@ class Certificado(object):
data['hasta'] = hasta.replace(tzinfo=None) data['hasta'] = hasta.replace(tzinfo=None)
return data return data
def _get_p12(self, password, rfc): def _get_p12(self, password, rfc, token):
tmp_cer = tempfile.mkstemp()[1] tmp_cer = tempfile.mkstemp()[1]
tmp_key = tempfile.mkstemp()[1] tmp_key = tempfile.mkstemp()[1]
tmp_p12 = tempfile.mkstemp()[1] tmp_p12 = tempfile.mkstemp()[1]
@ -428,7 +429,7 @@ class Certificado(object):
args = 'openssl pkcs12 -export -in "{}" -inkey "{}" -name "{}" ' \ args = 'openssl pkcs12 -export -in "{}" -inkey "{}" -name "{}" ' \
'-passout pass:"{}" -out "{}"' '-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) data = read_file(tmp_p12)
self._kill(tmp_cer) self._kill(tmp_cer)
@ -437,7 +438,7 @@ class Certificado(object):
return data return data
def _get_info_key(self, password, rfc): def _get_info_key(self, password, rfc, token):
data = {} data = {}
args = 'openssl pkcs8 -inform DER -in "{}" -passin pass:"{}"' args = 'openssl pkcs8 -inform DER -in "{}" -passin pass:"{}"'
@ -455,17 +456,23 @@ class Certificado(object):
self.error = 'Los archivos no son pareja' self.error = 'Los archivos no son pareja'
return data return data
args = 'openssl pkcs8 -inform DER -in "{}" -passin pass:"{}" | ' \ args = "openssl pkcs8 -inform DER -in '{}' -passin pass:'{}' | " \
'openssl rsa -des3 -passout pass:"{}"'.format( "openssl rsa -des3 -passout pass:'{}'".format(
self._path_key, password, _get_md5(rfc)) self._path_key, password, token)
key_enc = _call(args) key_enc = _call(args)
data['key'] = read_file(self._path_key) data['key'] = read_file(self._path_key)
data['key_enc'] = key_enc data['key_enc'] = key_enc
data['p12'] = self._get_p12(password, rfc) data['p12'] = self._get_p12(password, rfc, token)
return data 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: if not self._path_key or not self._path_cer:
self.error = 'Error en las rutas temporales del certificado' self.error = 'Error en las rutas temporales del certificado'
return {} return {}
@ -474,7 +481,7 @@ class Certificado(object):
if not data: if not data:
return {} return {}
llave = self._get_info_key(password, data['rfc']) llave = self._get_info_key(password, rfc, token)
if not llave: if not llave:
return {} return {}
@ -485,9 +492,15 @@ class Certificado(object):
return data return data
def make_xml(data, certificado): def make_xml(data, certificado, auth):
from .cfdi_xml import CFDI from .cfdi_xml import CFDI
token = _get_md5(certificado.rfc)
if USAR_TOKEN:
token = auth['PASS']
if AUTH['DEBUG']:
token = AUTH['PASS']
if DEBUG: if DEBUG:
data['emisor']['Rfc'] = certificado.rfc data['emisor']['Rfc'] = certificado.rfc
data['emisor']['RegimenFiscal'] = '603' data['emisor']['RegimenFiscal'] = '603'
@ -501,7 +514,7 @@ def make_xml(data, certificado):
'xml': save_temp(xml, 'w'), 'xml': save_temp(xml, 'w'),
'openssl': PATH_OPENSSL, 'openssl': PATH_OPENSSL,
'key': save_temp(certificado.key_enc, 'w'), 'key': save_temp(certificado.key_enc, 'w'),
'pass': _get_md5(certificado.rfc) 'pass': token,
} }
args = '"{xsltproc}" "{xslt}" "{xml}" | ' \ args = '"{xsltproc}" "{xslt}" "{xml}" | ' \
'"{openssl}" dgst -sha256 -sign "{key}" -passin pass:"{pass}" | ' \ '"{openssl}" dgst -sha256 -sign "{key}" -passin pass:"{pass}" | ' \
@ -517,13 +530,10 @@ def make_xml(data, certificado):
def timbra_xml(xml, auth): def timbra_xml(xml, auth):
from .pac import Finkok as PAC from .pac import Finkok as PAC
if DEBUG: if not DEBUG and not auth:
auth = {} msg = 'Sin datos para timbrar'
else: result = {'ok': True, 'error': msg}
if not auth: return result
msg = 'Sin datos para timbrar'
result = {'ok': True, 'error': msg}
return result
result = {'ok': True, 'error': ''} result = {'ok': True, 'error': ''}
pac = PAC(auth) pac = PAC(auth)
@ -1299,9 +1309,46 @@ def upload_file(rfc, opt, file_obj):
return {'status': 'error', 'ok': False} return {'status': 'error', 'ok': False}
def cancel_cfdi(uuid, pk12, rfc, auth): def cancel_xml(auth, uuid, certificado):
from .pac import Finkok as PAC 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') template = read_file(TEMPLATE_CANCEL, 'r')
data = { data = {
'rfc': rfc, 'rfc': rfc,
@ -1313,7 +1360,7 @@ def cancel_cfdi(uuid, pk12, rfc, auth):
data = { data = {
'xmlsec': PATH_XMLSEC, 'xmlsec': PATH_XMLSEC,
'pk12': save_temp(pk12), 'pk12': save_temp(pk12),
'pass': _get_md5(rfc), 'pass': token,
'template': save_temp(template, 'w'), 'template': save_temp(template, 'w'),
} }
args = '"{xmlsec}" --sign --pkcs12 "{pk12}" --pwd {pass} ' \ args = '"{xmlsec}" --sign --pkcs12 "{pk12}" --pwd {pass} ' \

View File

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

View File

@ -115,4 +115,6 @@ IMPUESTOS = {
'CEDULAR': '000', 'CEDULAR': '000',
} }
DEFAULT_SAT_PRODUCTO = '01010101' 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', view: 'tabview',
id: 'tab_options', id: 'tab_options',
multiview: true, multiview: true,
//~ tabbar: {options: [
//~ 'Plantillas',
//~ 'Otros']},
animate: true, animate: true,
cells: [ cells: [
{id: 'Plantillas', rows: options_templates}, {id: 'Plantillas', rows: options_templates},