Merge branch 'develop'

Envío de nómina por correo
Actualización de catalogos del SAT
This commit is contained in:
Mauricio Baeza 2019-04-23 23:12:25 -05:00
commit abbb4cc608
12 changed files with 166 additions and 39 deletions

View File

@ -1,3 +1,9 @@
v 1.31.0 [23-abr-2019]
----------------------
- Error: Validar cantidad o valor unitario cero en tickets
- Mejora: Envío de nómina por correo al empleado
v 1.30.0 [24-mar-2019]
----------------------
- Mejora: Se actualizan los catálogos de Nómina

View File

@ -1 +1 @@
1.30.0
1.31.0

View File

@ -498,7 +498,8 @@ class AppNomina(object):
def on_post(self, req, resp):
values = req.params
req.context['result'] = self._db.nomina(values)
session = req.env['beaker.session']
req.context['result'] = self._db.nomina(values, session['userobj'])
resp.status = falcon.HTTP_200
def on_delete(self, req, resp):

View File

@ -32,6 +32,9 @@ def get_template(req, resp, resource):
class AuthMiddleware(object):
def process_response(self, req, resp, resource):
pass
def process_resource(self, req, resp, resource, params):
session = req.env['beaker.session']
user = session.get('userobj', None)

View File

@ -33,11 +33,6 @@ class StorageEngine(object):
def get_nomina(self, values):
return main.CfdiNomina.get_by(values)
def nomina(self, values):
opt = values.pop('opt')
if opt == 'cancel':
return main.CfdiNomina.cancel(int(values['id']))
def empresa_agregar(self, values):
return main.empresa_agregar(values['alta_rfc'], False)
@ -461,3 +456,7 @@ class StorageEngine(object):
def partners_accounts_bank(self, values):
return main.SociosCuentasBanco.post(values)
def nomina(self, values, user):
return main.CfdiNomina.post(values, user)

View File

@ -7593,6 +7593,94 @@ class CfdiNomina(BaseModel):
class Meta:
order_by = ('fecha',)
def _cancel(self, values, user):
id = int(values['id'])
msg = 'Recibo cancelado correctamente'
auth = Emisor.get_auth()
certificado = Certificado.select()[0]
obj = CfdiNomina.get(CfdiNomina.id==id)
if obj.uuid is None:
msg = 'Solo se pueden cancelar recibos timbrados'
return {'ok': False, 'msg': msg}
data, result = util.cancel_xml(auth, obj.uuid, certificado)
if data['ok']:
data['msg'] = 'Recibo cancelado correctamente'
data['row']['estatus'] = 'Cancelado'
obj.estatus = data['row']['estatus']
obj.error = ''
obj.cancelada = True
obj.fecha_cancelacion = result['Fecha']
obj.acuse = result['Acuse'] or ''
else:
obj.error = data['msg']
obj.save()
return data
def _send_mail(self, values, user):
id = int(values['id'])
msg = 'Recibo de Nómina enviado correctamente'
result = {'ok': True, 'msg': msg}
config = Configuracion.get_({'fields': 'correo'})
contra = Configuracion.get_('correo_contra')
if not config:
msg = 'Envío de correo no configurado.'
result['ok'] = False
result['msg'] = msg
return result
emisor = Emisor.select()[0]
obj = CfdiNomina.get(CfdiNomina.id==id)
to = obj.empleado.correo
if not to:
msg = 'El empleado no tiene correo.'
result['ok'] = False
result['msg'] = msg
return result
name = '{}{}_{}'.format(obj.serie, obj.folio, obj.empleado.rfc)
values = self._get_not_in_xml(self, obj, emisor)
data = util.get_data_from_xml(obj, values)
doc = util.to_pdf(data, emisor.rfc)
files = (
(obj.xml, f'{name}.xml'),
(doc, f'{name}.pdf'),
)
message = subject = f"Enviamos tu recibo de nómina"
server = {
'server': config['correo_servidor'],
'port': config['correo_puerto'],
'ssl': utils.to_bool(config['correo_ssl']),
'user': config['correo_usuario'],
'pass': utils.decrypt(contra, emisor.rfc),
}
mail = {
'to': to,
'copy': config.get('correo_copia', ''),
'subject': subject,
'message': message,
'files': files,
}
data= {
'server': server,
'mail': mail,
}
r = utils.send_mail(data)
if not r['ok']:
result['ok'] = False
result['msg'] = r['msg']
return result
@classmethod
def post(cls, values, user):
opt = values.pop('opt')
return getattr(cls, '_{}'.format(opt))(cls, values, user)
def _get_serie(self):
serie = Configuracion.get_('txt_config_nomina_serie')
if not serie:
@ -8029,6 +8117,7 @@ class CfdiNomina(BaseModel):
CfdiNomina.id,
CfdiNomina.serie,
CfdiNomina.folio,
CfdiNomina.uuid,
CfdiNomina.fecha,
CfdiNomina.estatus,
CfdiNomina.fecha_pago,
@ -8509,32 +8598,6 @@ class CfdiNomina(BaseModel):
data += [{'id': int(r.year), 'value': int(r.year)} for r in rows]
return tuple(data)
@classmethod
def cancel(cls, id):
msg = 'Recibo cancelado correctamente'
auth = Emisor.get_auth()
certificado = Certificado.select()[0]
obj = CfdiNomina.get(CfdiNomina.id==id)
if obj.uuid is None:
msg = 'Solo se pueden cancelar recibos timbrados'
return {'ok': False, 'msg': msg}
data, result = util.cancel_xml(auth, obj.uuid, certificado)
if data['ok']:
data['msg'] = 'Recibo cancelado correctamente'
data['row']['estatus'] = 'Cancelado'
obj.estatus = data['row']['estatus']
obj.error = ''
obj.cancelada = True
obj.fecha_cancelacion = result['Fecha']
obj.acuse = result['Acuse'] or ''
else:
obj.error = data['msg']
obj.save()
return data
class CfdiNominaDetalle(BaseModel):
cfdi = ForeignKeyField(CfdiNomina)

View File

@ -47,7 +47,7 @@ except ImportError:
DEBUG = DEBUG
VERSION = '1.30.0'
VERSION = '1.31.0'
EMAIL_SUPPORT = ('soporte@empresalibre.mx',)
TITLE_APP = '{} v{}'.format(TITLE_APP, VERSION)

View File

@ -390,6 +390,42 @@ function timbrar_nomina(){
}
function send_mail(row){
if(!row.uuid){
msg_error('La nómina no esta timbrada')
return
}
var data = {'opt': 'send_mail', 'id': row.id}
msg = '¿Estás seguro de enviar por correo este Recibo de Nómina?'
webix.confirm({
title: 'Enviar Recibo de Nómina',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
webix.ajax().post('/nomina', data, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
msg_error(msg)
},
success:function(text, data, XmlHttpRequest){
values = data.json();
if(values.ok){
msg_ok(values.msg)
}else{
msg_error(values.msg)
}
}
})
}
}
})
}
function grid_nomina_click(id, e, node){
var row = this.getItem(id)
@ -397,8 +433,8 @@ function grid_nomina_click(id, e, node){
location = '/doc/nomxml/' + row.id
}else if(id.column == 'pdf'){
window.open('/doc/nompdf/' + row.id, '_blank')
//~ }else if(id.column == 'email'){
//~ enviar_correo(row)
}else if(id.column == 'email'){
send_mail(row)
}
}

View File

@ -408,6 +408,14 @@ function grid_tickets_details_before_edit_stop(state, editor){
grid.editCancel()
grid.unblockEvent()
return true
}else if(cantidad <= 0){
msg = 'La cantidad no puede cero o menor'
msg_error(msg)
grid.blockEvent()
state.value = state.old
grid.editCancel()
grid.unblockEvent()
return true
}
var valor_unitario = parseFloat(row['valor_unitario'])
var descuento = parseFloat(row['descuento'])
@ -423,6 +431,14 @@ function grid_tickets_details_before_edit_stop(state, editor){
grid.editCancel()
grid.unblockEvent()
return true
}else if(valor_unitario <= 0){
msg = 'El valor unitario no puede cero o menor'
msg_error(msg)
grid.blockEvent()
state.value = state.old
grid.editCancel()
grid.unblockEvent()
return true
}
var cantidad = parseFloat(row['cantidad'])
var descuento = parseFloat(row['descuento'])
@ -784,4 +800,4 @@ function grid_tdetails_render(data){
})
var id = $$('grid_ticket_total_up').getFirstId()
$$('grid_ticket_total_up').updateItem(id, {total: total})
}
}

View File

@ -518,7 +518,7 @@ var emisor_correo = [
{view: 'button', id: 'cmd_guardar_correo', label: 'Guardar Configuración',
autowidth: true, type: 'form'},
{maxWidth: 50},
{view: 'button', id: 'cmd_clean_email', label: 'Limpiar Configuración',
{view: 'button', id: 'cmd_clean_email', label: 'Borrar Configuración',
autowidth: true, type: 'form'},
{}]}
]

View File

@ -273,7 +273,7 @@ var grid_invoices_cols = [
{id: 'html', header: 'HTML', adjust: 'data', template: get_icon('html')},
{id: 'ods', header: 'ODS', adjust: 'data', template: get_icon('table')},
{id: 'zip', header: 'ZIP', adjust: 'data', template: get_icon('zip')},
{id: 'email', header: '', adjust: 'data', template: get_icon('email')}
{id: 'email', header: '@', adjust: 'data', template: get_icon('email')}
]

View File

@ -48,6 +48,8 @@ var grid_cols_nomina = [
{id: "serie", header: ["Serie"], adjust: "header"},
{id: 'folio', header: ['Folio', {content: 'numberFilter'}], adjust: 'header',
sort: 'int', css: 'right', footer: {text: 'Recibos', colspan: 3}},
{id: "uuid", header: ["UUID", {content: "textFilter"}], adjust: "data",
sort:"string", hidden:true},
{id: "fecha", header: ["Fecha y Hora"], adjust: "data", sort: "string"},
{id: "estatus", header: ["Estatus", {content: "selectFilter"}],
adjust: "data", sort:"string"},
@ -60,6 +62,7 @@ var grid_cols_nomina = [
fillspace:true, sort:"string"},
{id: 'xml', header: 'XML', adjust: 'data', template: get_icon('xml')},
{id: 'pdf', header: 'PDF', adjust: 'data', template: get_icon('pdf')},
{id: 'email', header: '@', adjust: 'data', template: get_icon('email')}
]