Facturar productos en lote

This commit is contained in:
Mauricio Baeza 2018-01-19 22:16:19 -06:00
parent 65a3bc931f
commit 7873a4376c
7 changed files with 275 additions and 48 deletions

View File

@ -928,7 +928,7 @@ class LIBO(object):
options = {'AsTemplate': True, 'Hidden': True}
doc = self._doc_open(path, options)
if doc is None:
return ()
return (), 'No se pudo abrir la plantilla'
data, msg = self._get_data(doc)
doc.close(True)
@ -952,6 +952,22 @@ class LIBO(object):
rows = [dict(zip(fields, r)) for r in data[1:]]
return rows, ''
def invoice(self, path):
options = {'AsTemplate': True, 'Hidden': True}
doc = self._doc_open(path, options)
if doc is None:
return (), 'No se pudo abrir la plantilla'
data, msg = self._get_data(doc)
doc.close(True)
if len(data) == 1:
msg = 'Sin datos para importar'
return (), msg
rows = tuple(data[1:])
return rows, ''
def to_pdf(data, emisor_rfc, ods=False):
rfc = data['emisor']['rfc']
@ -1390,6 +1406,15 @@ def upload_file(rfc, opt, file_obj):
name = '{}_products.ods'.format(rfc.lower())
path = _join(PATH_MEDIA, 'tmp', name)
elif opt == 'invoiceods':
tmp = file_obj.filename.split('.')
ext = tmp[-1].lower()
if ext != 'ods':
msg = 'Extensión de archivo incorrecta, selecciona un archivo ODS'
return {'status': 'server', 'name': msg, 'ok': False}
name = '{}_invoice.ods'.format(rfc.lower())
path = _join(PATH_MEDIA, 'tmp', name)
if save_file(path, file_obj.file.read()):
return {'status': 'server', 'name': file_obj.filename, 'ok': True}
@ -2601,11 +2626,26 @@ def import_products(rfc):
name = '{}_products.ods'.format(rfc.lower())
path = _join(PATH_MEDIA, 'tmp', name)
if not is_file(path):
return ()
return (), 'No se encontró la plantilla'
if APP_LIBO:
app = LIBO()
if app.is_running:
return app.products(path)
return ()
return (), 'No se encontro LibreOffice'
def import_invoice(rfc):
name = '{}_invoice.ods'.format(rfc.lower())
path = _join(PATH_MEDIA, 'tmp', name)
if not is_file(path):
return (), 'No se encontró la plantilla'
if APP_LIBO:
app = LIBO()
if app.is_running:
return app.invoice(path)
return (), 'No se encontro LibreOffice'

View File

@ -25,6 +25,9 @@ class StorageEngine(object):
return getattr(self, '_get_{}'.format(table))(values, session)
return getattr(self, '_get_{}'.format(table))(values)
def _get_importinvoice(self, values):
return main.import_invoice()
def _get_main(self, values):
return main.config_main()

View File

@ -104,6 +104,62 @@ def validar_timbrar():
return {'ok': True, 'msg': msg}
def _get_taxes_product(id):
model_pt = Productos.impuestos.get_through_model()
impuestos = tuple(model_pt
.select(
model_pt.productos_id.alias('product'),
model_pt.satimpuestos_id.alias('tax'))
.where(model_pt.productos_id==id).dicts())
return impuestos
def import_invoice():
log.info('Importando factura...')
emisor = Emisor.select()[0]
rows, msg = util.import_invoice(emisor.rfc)
if not rows:
return {'ok': False, 'msg': msg}
# ~ clave, descripcion, precio, cantidad
products = {}
for row in rows:
try:
obj = Productos.get(Productos.clave==row[0])
if obj.id in products:
vu = round(row[2], 2)
cant = round(row[3], 2)
pf = products[obj.id]['valor_unitario'] - float(obj.descuento)
products[obj.id]['cantidad'] += cant
products[obj.id]['importe'] = round(
pf * products[obj.id]['cantidad'], DECIMALES)
if vu != products[obj.id]['valor_unitario']:
msg = 'Precio diferente en producto: {}'.format(row[0])
return {'ok': False, 'msg': msg}
else:
vu = round(row[2], 2)
cant = round(row[3], 2)
pf = vu - float(obj.descuento)
p = {
'id': obj.id,
'delete': '-',
'clave': obj.clave,
'descripcion': obj.descripcion,
'unidad': obj.unidad.name,
'cantidad': cant,
'valor_unitario': vu,
'descuento': obj.descuento,
'importe': round(pf * cant, DECIMALES),
'taxes': _get_taxes_product(obj.id),
}
products[obj.id] = p
except Productos.DoesNotExist:
pass
log.info('Factura importada...')
return {'ok': True, 'rows': tuple(products.values())}
def get_doc(type_doc, id, rfc):
types = {
'xml': 'application/xml',
@ -2231,21 +2287,21 @@ class Productos(BaseModel):
q.execute()
obj = Productos.get(w)
obj.impuestos = taxes
log.info('Producto actualizado...')
log.info('\tProducto actualizado: {}'.format(data['clave']))
ap += 1
else:
obj = Productos.create(**data)
obj.impuestos = taxes
log.info('Producto agregado...')
log.info('\tProducto agregado: {}'.format(data['clave']))
np += 1
except Exception as e:
msg = 'Error al importar producto: {}'.format(data['clave'])
log.error(msg)
log.error(e)
msg = 'Productos encontrados: {}\n'.format(len(rows))
msg += 'Productos agregados: {}\n'.format(np)
msg += 'Productos actualizados: {}'.format(ap)
msg = 'Productos encontrados: {}<BR>'.format(len(rows))
msg += 'Productos agregados: {}<BR>'.format(np)
msg += 'Productos actualizados: {}<BR>'.format(ap)
msg += 'Productos con problemas: {}'.format(len(rows) - np - ap)
return {'ok': True, 'msg': msg}

View File

@ -43,6 +43,7 @@ var invoices_controllers = {
$$('cmd_cfdi_notes').attachEvent('onItemClick', cmd_cfdi_notes_click)
$$('cmd_admin_invoice_notes').attachEvent('onItemClick', cmd_admin_invoice_notes_click)
$$('cmd_import_invoice').attachEvent('onItemClick', cmd_import_invoice_click)
webix.extend($$('grid_invoices'), webix.ProgressBar)
}
@ -858,7 +859,6 @@ function set_product(values){
table_pt.insert(v)
}
}
//~ calculate_taxes()
calcular_impuestos()
}
@ -868,25 +868,6 @@ function grid_products_found_click(obj){
}
//~ function search_product_by_id(id){
//~ webix.ajax().get('/values/product', {'id': id}, {
//~ error: function(text, data, xhr) {
//~ msg_error('Error al consultar')
//~ },
//~ success: function(text, data, xhr){
//~ var values = data.json()
//~ if (values.ok){
//~ set_product(values)
//~ } else {
//~ msg = 'No se encontró un producto con la clave: ' + id
//~ msg_error(msg)
//~ }
//~ }
//~ })
//~ }
function search_product_by_key(key){
webix.ajax().get('/values/productokey', {'key': key}, {
error: function(text, data, xhr) {
@ -1864,4 +1845,112 @@ function cmd_admin_invoice_notes_click(){
to_end('invoice_notes')
})
cfg_invoice['notes'] = true
}
}
function cmd_import_invoice_click(){
win_import_invoice.init()
$$('win_import_invoice').show()
}
function cmd_upload_invoice_click(){
var form = $$('form_upload_invoice')
var values = form.getValues()
if(!$$('lst_upload_invoice').count()){
$$('win_import_invoice').close()
return
}
if($$('lst_upload_invoice').count() > 1){
msg = 'Selecciona solo un archivo'
msg_error(msg)
return
}
var template = $$('up_invoice').files.getItem($$('up_invoice').files.getFirstId())
if(template.type.toLowerCase() != 'ods'){
msg = 'Archivo inválido.\n\nSe requiere un archivo ODS'
msg_error(msg)
return
}
msg = '¿Estás seguro de importar este archivo?'
webix.confirm({
title: 'Importar Factura',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if(result){
$$('up_invoice').send()
}
}
})
}
function add_import_product_taxes(taxes){
//~ var taxes = values.taxes
//~ var row = grid.getItem(values.id)
//~ values['delete'] = '-'
//~ if (row == undefined){
//~ grid.add(values)
//~ } else {
//~ values['cantidad'] = parseFloat(row.cantidad) + parseFloat(values['cantidad'])
//~ values['valor_unitario'] = parseFloat(row.valor_unitario)
//~ values['descuento'] = parseFloat(row.descuento)
//~ var precio_final = values['valor_unitario'] - values['descuento']
//~ values['importe'] = (precio_final * values['cantidad']).round(DECIMALES)
//~ grid.updateItem(row.id, values)
//~ }
for(var v of taxes){
var pt = table_pt.findOne(v)
if(pt === null){
table_pt.insert(v)
}
}
}
function up_invoice_upload_complete(response){
if(response.status != 'server'){
msg = 'Ocurrio un error al subir el archivo'
msg_error(msg)
return
}
msg = 'Archivo subido correctamente.\n\nComenzando importación.'
msg_ok(msg)
$$('win_import_invoice').close()
webix.ajax().get('/values/importinvoice', {
error: function(text, data, xhr) {
msg_error('Error al consultar')
},
success: function(text, data, xhr){
var values = data.json()
if (values.ok){
for(var p of values.rows){
add_import_product_taxes(p.taxes)
}
grid.clearAll()
grid.parse(values.rows, 'json')
grid.refresh()
calcular_impuestos()
}else{
webix.alert({
title: 'Error al importar',
text: values.msg,
type: 'alert-error',
})
}
}
})
}

View File

@ -75,23 +75,6 @@ function get_partners(){
}
function get_products(){
var grid = $$('grid_products')
webix.ajax().get('/products', {}, {
error: function(text, data, xhr) {
msg_error('Error al consultar')
},
success: function(text, data, xhr) {
var values = data.json();
grid.clearAll();
if (values.ok){
grid.parse(values.rows, 'json');
};
}
});
}
function menu_user_click(id, e, node){
if (id == 1){
window.location = '/logout';

View File

@ -50,6 +50,24 @@ function get_categorias(){
}
function get_products(){
var grid = $$('grid_products')
webix.ajax().get('/products', {}, {
error: function(text, data, xhr) {
msg_error('Error al consultar')
},
success: function(text, data, xhr) {
var values = data.json();
grid.clearAll();
if (values.ok){
grid.parse(values.rows, 'json');
grid.refresh()
};
}
});
}
function cmd_new_product_click(id, e, node){
get_taxes()
configurar_productos(true)
@ -378,8 +396,11 @@ function up_products_upload_complete(response){
success: function(text, data, xhr) {
var values = data.json();
if (values.ok){
msg_ok(values.msg)
get_products()
webix.alert({
title: 'Importación terminada',
text: values.msg,
})
}else{
msg_error(values.msg)
}

View File

@ -201,6 +201,8 @@ var toolbar_invoices_generate = {view: 'toolbar', elements: [{},
type: 'iconButton', autowidth: true, icon: 'commenting-o'},
{view: 'button', id: 'cmd_cfdi_relacionados', label: 'CFDI Relacionados',
type: 'iconButton', autowidth: true, icon: 'file-o'},
{view: 'button', id: 'cmd_import_invoice', label: 'Importar',
type: 'iconButton', autowidth: true, icon: 'upload'},
{view: 'checkbox', id: 'chk_cfdi_anticipo', labelRight: 'Es Anticipo',
labelWidth: 0, width: 100, hidden: true},
{view: 'checkbox', id: 'chk_cfdi_donativo', labelRight: 'Es Donativo',
@ -277,7 +279,8 @@ var grid_invoices = {
var grid_details_cols = [
{id: "id", header:"ID", hidden: true},
{id: 'delete', header: '', width: 30, css: 'delete'},
{id: "clave", header:{text: 'Clave', css: 'center'}, width: 100},
{id: "clave", header:{text: 'Clave', css: 'center'}, width: 100,
adjust: 'data'},
{id: "clave_sat", hidden: true},
{id: "descripcion", header:{text: 'Descripción', css: 'center'},
fillspace: true, editor: 'popup'},
@ -661,3 +664,35 @@ var app_invoices = {
multi_invoices
],
}
var body_upload_invoice = {rows: [
{view: 'form', id: 'form_upload_invoice', rows: [
{cols: [{},
{view: 'uploader', id: 'up_invoice', autosend: false,
link: 'lst_upload_invoice', value: 'Seleccionar Archivo',
upload: '/files/invoiceods'}, {}]},
{cols: [
{view: 'list', id: 'lst_upload_invoice', name: 'lst_upload_invoice',
type: 'uploader', autoheight: true, borderless: true}]},
{cols: [{}, {view: 'button', id: 'cmd_upload_invoice',
label: 'Importar Factura'}, {}]},
]},
]}
var win_import_invoice = {
init: function(){
webix.ui({
view: 'window',
id: 'win_import_invoice',
width: 400,
modal: true,
position: 'center',
head: 'Importar Factura de Plantilla',
body: body_upload_invoice,
})
$$('cmd_upload_invoice').attachEvent('onItemClick', cmd_upload_invoice_click)
$$('up_invoice').attachEvent('onUploadComplete', up_invoice_upload_complete)
}
}