From 3e6dc1eda215f430dc4506e8fb9d5aa9113aa942 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Sat, 23 Dec 2017 00:02:25 -0600 Subject: [PATCH 1/4] Fix 72 --- source/app/controllers/util.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index 505bb4f..fe28172 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -1753,14 +1753,15 @@ class ImportFacturaLibre(object): rows = self._cursor.fetchall() tasas = { + '0': 0.0, '16': 0.16, '16.00': 0.16, '11': 0.11, '-10': 0.10, - '0': 0.0, - '-2/3': 0.106667, '-0.5': 0.005, + '-2/3': 0.106667, '-10.6666': 0.106667, + '-10.666666': 0.106667, } data = [] From 2229f5c63f57bddd064ac18eecb62924118b048d Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Sat, 23 Dec 2017 11:55:01 -0600 Subject: [PATCH 2/4] Fix 72 --- source/app/controllers/util.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index fe28172..78f1eb7 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -1758,10 +1758,12 @@ class ImportFacturaLibre(object): '16.00': 0.16, '11': 0.11, '-10': 0.10, + '-2': 0.02, '-0.5': 0.005, '-2/3': 0.106667, '-10.6666': 0.106667, '-10.666666': 0.106667, + '-10.66660': 0.106667, } data = [] From 9796ffb6f307e75f14701dbe3361f01ba84fdea1 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Sat, 23 Dec 2017 17:54:47 -0600 Subject: [PATCH 3/4] Ajuste ventana de folios en Admin --- source/static/js/ui/admin.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index 3578bb3..e53b8e6 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -294,7 +294,6 @@ var form_emisor = { labelWidth: 150, labelAlign: 'right' }, - //~ autoheight: true, rules: { emisor_nombre: function(value){return value.trim() != ''}, } @@ -403,11 +402,10 @@ var controls_folios = [ { view: 'tabview', id: 'tab_folios', - tabbar: {options: ['Folios']}, + //~ tabbar: {options: ['Folios']}, animate: true, cells: [ {id: 'Folios', rows: emisor_folios}, - {}, ] } ] @@ -421,7 +419,6 @@ var controls_correo = [ animate: true, cells: [ {id: 'Correo Electrónico', rows: emisor_correo}, - {}, ] } ] @@ -429,16 +426,17 @@ var controls_correo = [ var form_folios = { type: 'space', + responsive: true, cols: [{ view: 'form', id: 'form_folios', complexData: true, + scroll: true, elements: controls_folios, elementsConfig: { labelWidth: 100, labelAlign: 'right' }, - autoheight: true, rules: { folio_serie: function(value){return value.trim() != ''}, folio_inicio: function(value){return value > 0}, @@ -930,7 +928,6 @@ var app_folios = { {view: 'template', id: 'th_folios', type: 'header', template: 'Folios'}, form_folios, - {}, ], } From 3a10c146d2bea057bc0d8750c632c50876e1e31b Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Sat, 23 Dec 2017 22:21:35 -0600 Subject: [PATCH 4/4] =?UTF-8?q?Agregar=20m=C3=A9todo=20alternativo=20para?= =?UTF-8?q?=20cancelar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/app/controllers/configpac.py | 2 + source/app/controllers/pac.py | 15 ++--- source/app/controllers/util.py | 89 ++++++++++++++++++++++------- source/app/models/main.py | 45 ++++++++++++--- source/app/settings.py | 4 +- source/static/js/ui/admin.js | 3 - 6 files changed, 115 insertions(+), 43 deletions(-) diff --git a/source/app/controllers/configpac.py b/source/app/controllers/configpac.py index 1f559f5..9e0cae1 100644 --- a/source/app/controllers/configpac.py +++ b/source/app/controllers/configpac.py @@ -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': { diff --git a/source/app/controllers/pac.py b/source/app/controllers/pac.py index b99fb87..31acc20 100644 --- a/source/app/controllers/pac.py +++ b/source/app/controllers/pac.py @@ -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, diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py index 78f1eb7..6077607 100644 --- a/source/app/controllers/util.py +++ b/source/app/controllers/util.py @@ -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} ' \ diff --git a/source/app/models/main.py b/source/app/models/main.py index 7ff1479..a2b89a4 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -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' diff --git a/source/app/settings.py b/source/app/settings.py index 58a0807..df168c1 100644 --- a/source/app/settings.py +++ b/source/app/settings.py @@ -115,4 +115,6 @@ IMPUESTOS = { 'CEDULAR': '000', } DEFAULT_SAT_PRODUCTO = '01010101' -DIR_FACTURAS = 'facturas' \ No newline at end of file +DIR_FACTURAS = 'facturas' +USAR_TOKEN = False +CANCEL_SIGNATURE = True \ No newline at end of file diff --git a/source/static/js/ui/admin.js b/source/static/js/ui/admin.js index e53b8e6..3cd09a1 100644 --- a/source/static/js/ui/admin.js +++ b/source/static/js/ui/admin.js @@ -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},