diff --git a/source/app/controllers/pac.py b/source/app/controllers/pac.py
index 89c37a3..b99fb87 100644
--- a/source/app/controllers/pac.py
+++ b/source/app/controllers/pac.py
@@ -371,7 +371,7 @@ class Finkok(object):
if os.path.isfile(file_xml):
root = etree.parse(file_xml).getroot()
else:
- root = etree.fromstring(file_xml)
+ root = etree.fromstring(file_xml.encode())
xml = etree.tostring(root)
@@ -385,8 +385,12 @@ class Finkok(object):
'store_pending': True,
}
- result = client.service.cancel_signature(**args)
- return result
+ try:
+ result = client.service.cancel_signature(**args)
+ return result
+ except Fault as e:
+ self.error = str(e)
+ return ''
def get_acuse(self, rfc, uuids, type_acuse='C'):
for u in uuids:
diff --git a/source/app/controllers/util.py b/source/app/controllers/util.py
index 275a4f6..318fe6f 100644
--- a/source/app/controllers/util.py
+++ b/source/app/controllers/util.py
@@ -33,7 +33,8 @@ from dateutil import parser
from .helper import CaseInsensitiveDict, NumLet, SendMail, TemplateInvoice
from settings import DEBUG, 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
#~ def _get_hash(password):
@@ -1048,6 +1049,52 @@ def get_date(value, next_day=False):
return d
+def cancel_cfdi(uuid, pk12, rfc, auth):
+ from .pac import Finkok as PAC
+
+ template = read_file(TEMPLATE_CANCEL, 'r')
+ data = {
+ 'rfc': rfc,
+ 'fecha': datetime.datetime.now().isoformat()[:19],
+ 'uuid': str(uuid).upper(),
+ }
+ template = template.format(**data)
+
+ data = {
+ 'xmlsec': PATH_XMLSEC,
+ 'pk12': _save_temp(pk12),
+ 'pass': _get_md5(rfc),
+ 'template': _save_temp(template, 'w'),
+ }
+ args = '"{xmlsec}" --sign --pkcs12 "{pk12}" --pwd {pass} ' \
+ '"{template}"'.format(**data)
+ xml_sign = _call(args)
+
+ if DEBUG:
+ auth = {}
+ else:
+ if not auth:
+ msg = 'Sin datos para cancelar'
+ result = {'ok': False, 'error': msg}
+ return result
+
+ msg = 'Factura cancelada correctamente'
+ data = {'ok': True, 'msg': msg, 'row': {'estatus': 'Cancelada'}}
+ pac = PAC(auth)
+ result = pac.cancel_signature(xml_sign)
+ if result:
+ codes = {None: '',
+ 'Could not get UUID Text': 'UUID no encontrado'}
+ 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
+
+
class ImportFacturaLibre(object):
def __init__(self, path):
diff --git a/source/app/models/db.py b/source/app/models/db.py
index 9cf565c..ba042c8 100644
--- a/source/app/models/db.py
+++ b/source/app/models/db.py
@@ -32,6 +32,9 @@ class StorageEngine(object):
def send_email(self, values, session):
return main.Facturas.send(values['id'], session['rfc'])
+ def _get_cancelinvoice(self, values):
+ return main.Facturas.cancel(values['id'])
+
def _get_statussat(self, values):
return main.Facturas.get_status_sat(values['id'])
diff --git a/source/app/models/main.py b/source/app/models/main.py
index 654ac54..cb8e79f 100644
--- a/source/app/models/main.py
+++ b/source/app/models/main.py
@@ -723,7 +723,7 @@ class Socios(BaseModel):
.where((Socios.id==id) & (Socios.es_cliente==True))
.dicts()
)
- print (id, row)
+ #~ print (id, row)
if len(row):
return {'ok': True, 'row': row[0]}
return {'ok': False}
@@ -1009,6 +1009,8 @@ class Facturas(BaseModel):
notas = TextField(default='')
pagada = BooleanField(default=False)
cancelada = BooleanField(default=False)
+ fecha_cancelacion = DateTimeField(null=True)
+ acuse = TextField(default='')
donativo = BooleanField(default=False)
tipo_relacion = TextField(default='')
error = TextField(default='')
@@ -1016,6 +1018,25 @@ class Facturas(BaseModel):
class Meta:
order_by = ('fecha',)
+ @classmethod
+ def cancel(cls, id):
+ msg = 'Factura cancelada correctamente'
+ auth = Emisor.get_auth()
+ certificado = Certificado.select()[0]
+ obj = Facturas.get(Facturas.id==id)
+ data, result = util.cancel_cfdi(
+ obj.uuid, certificado.p12, certificado.rfc, auth)
+ if data['ok']:
+ obj.estatus = 'Cancelada'
+ obj.error = ''
+ obj.cancelada = True
+ obj.fecha_cancelacion = result['Fecha']
+ obj.acuse = result['Acuse']
+ else:
+ obj.error = data['msg']
+ obj.save()
+ return data
+
@classmethod
def filter_years(cls):
data = [{'id': -1, 'value': 'Todos'}]
@@ -1025,7 +1046,8 @@ class Facturas(BaseModel):
.order_by(Facturas.fecha.year)
.scalar(as_tuple=True)
)
- data += [{'id': int(row), 'value': int(row)} for row in rows]
+ if not rows is None:
+ data += [{'id': int(row), 'value': int(row)} for row in rows]
return tuple(data)
@classmethod
@@ -1489,7 +1511,7 @@ class Facturas(BaseModel):
auth = Emisor.get_auth()
- error = False
+ #~ error = False
msg = 'Factura timbrada correctamente'
result = util.timbra_xml(obj.xml, auth)
if result['ok']:
@@ -1498,10 +1520,10 @@ class Facturas(BaseModel):
obj.fecha_timbrado = result['fecha']
obj.estatus = 'Timbrada'
obj.error = ''
- obj.save()
+ #~ obj.save()
row = {'uuid': obj.uuid, 'estatus': 'Timbrada'}
else:
- error = True
+ #~ error = True
msg = result['error']
obj.estatus = 'Error'
obj.error = msg
diff --git a/source/app/settings.py b/source/app/settings.py
index 1776135..ad1d0cf 100644
--- a/source/app/settings.py
+++ b/source/app/settings.py
@@ -25,6 +25,8 @@ DB_SAT = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'sat.db'))
IV = 'valores_iniciales.json'
INIT_VALUES = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', IV))
+CT = 'cancel_template.xml'
+TEMPLATE_CANCEL = os.path.abspath(os.path.join(PATH_TEMPLATES, CT))
PATH_XSLT = os.path.abspath(os.path.join(BASE_DIR, '..', 'xslt'))
PATH_BIN = os.path.abspath(os.path.join(BASE_DIR, '..', 'bin'))
@@ -69,9 +71,11 @@ log = Logger(LOG_NAME)
PATH_XSLTPROC = 'xsltproc'
PATH_OPENSSL = 'openssl'
+PATH_XMLSEC = 'xmlsec1'
if 'win' in sys.platform:
PATH_XSLTPROC = os.path.join(PATH_BIN, 'xsltproc.exe')
PATH_OPENSSL = os.path.join(PATH_BIN, 'openssl.exe')
+ PATH_XMLSEC = os.path.join(PATH_BIN, 'xmlsec.exe')
PRE = {
diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js
index 29dd002..91b6ae2 100644
--- a/source/static/js/controller/invoices.js
+++ b/source/static/js/controller/invoices.js
@@ -695,7 +695,19 @@ function grid_invoices_click(id, e, node){
function send_cancel(id){
- show(id)
+ webix.ajax().get('/values/cancelinvoice', {id: id}, function(text, data){
+ var values = data.json()
+ if(values.ok){
+ msg_sucess(values.msg)
+ gi.updateItem(id, values.row)
+ }else{
+ webix.alert({
+ title: 'Error al Cancelar',
+ text: values.msg,
+ type: 'alert-error'
+ })
+ }
+ })
}
function cmd_invoice_cancelar_click(){
@@ -715,6 +727,11 @@ function cmd_invoice_cancelar_click(){
return
}
+ if(row.estatus == 'Cancelada'){
+ msg_error('La factura ya esta cancelada')
+ return
+ }
+
msg = '¿Estás seguro de enviar a cancelar esta factura?
\
ESTA ACCIÓN NO SE PUEDE DESHACER'
webix.confirm({
diff --git a/source/templates/cancel_template.xml b/source/templates/cancel_template.xml
new file mode 100644
index 0000000..6b58286
--- /dev/null
+++ b/source/templates/cancel_template.xml
@@ -0,0 +1,28 @@
+
+
+
+ {uuid}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+