Cancelar facturas con xml firmado

This commit is contained in:
Mauricio Baeza 2017-10-29 16:53:10 -06:00
parent f958227f56
commit 9e50033b9f
7 changed files with 135 additions and 10 deletions

View File

@ -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:

View File

@ -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):

View File

@ -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'])

View File

@ -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

View File

@ -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 = {

View File

@ -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?<BR><BR> \
ESTA ACCIÓN NO SE PUEDE DESHACER'
webix.confirm({

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Cancelacion RfcEmisor="{rfc}" Fecha="{fecha}" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://cancelacfd.sat.gob.mx">
<Folios>
<UUID>{uuid}</UUID>
</Folios>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue />
</Reference>
</SignedInfo>
<SignatureValue />
<KeyInfo>
<X509Data>
<X509SubjectName />
<X509IssuerSerial />
<X509Certificate />
</X509Data>
<KeyValue />
</KeyInfo>
</Signature>
</Cancelacion>