Importar productos en lote
This commit is contained in:
parent
c1c50d5102
commit
65a3bc931f
|
@ -221,7 +221,7 @@ class AppProducts(object):
|
|||
|
||||
def on_post(self, req, resp):
|
||||
values = req.params
|
||||
req.context['result'] = self._db.product(values)
|
||||
req.context['result'] = self._db.products(values)
|
||||
resp.status = falcon.HTTP_200
|
||||
|
||||
def on_delete(self, req, resp):
|
||||
|
|
|
@ -914,6 +914,44 @@ class LIBO(object):
|
|||
|
||||
return self._read(path)
|
||||
|
||||
def _get_data(self, doc, name=0):
|
||||
try:
|
||||
sheet = doc.getSheets()[name]
|
||||
cursor = sheet.createCursorByRange(sheet['A1'])
|
||||
cursor.collapseToCurrentRegion()
|
||||
except KeyError:
|
||||
msg = 'Hoja no existe'
|
||||
return (), msg
|
||||
return cursor.getDataArray(), ''
|
||||
|
||||
def products(self, path):
|
||||
options = {'AsTemplate': True, 'Hidden': True}
|
||||
doc = self._doc_open(path, options)
|
||||
if doc is None:
|
||||
return ()
|
||||
|
||||
data, msg = self._get_data(doc)
|
||||
doc.close(True)
|
||||
|
||||
if len(data) == 1:
|
||||
msg = 'Sin datos para importar'
|
||||
return (), msg
|
||||
|
||||
fields = (
|
||||
'categoria',
|
||||
'clave',
|
||||
'clave_sat',
|
||||
'descripcion',
|
||||
'unidad',
|
||||
'valor_unitario',
|
||||
'inventario',
|
||||
'existencia',
|
||||
'codigo_barras',
|
||||
'impuestos',
|
||||
)
|
||||
rows = [dict(zip(fields, r)) for r in data[1:]]
|
||||
return rows, ''
|
||||
|
||||
|
||||
def to_pdf(data, emisor_rfc, ods=False):
|
||||
rfc = data['emisor']['rfc']
|
||||
|
@ -1343,6 +1381,15 @@ def upload_file(rfc, opt, file_obj):
|
|||
|
||||
name = '{}.sqlite'.format(rfc.lower())
|
||||
path = _join('/tmp', name)
|
||||
elif opt == 'products':
|
||||
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 = '{}_products.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}
|
||||
|
@ -2548,3 +2595,17 @@ class ImportFacturaLibre(object):
|
|||
def print_ticket(data, info):
|
||||
p = PrintTicket(info)
|
||||
return p.printer(data)
|
||||
|
||||
|
||||
def import_products(rfc):
|
||||
name = '{}_products.ods'.format(rfc.lower())
|
||||
path = _join(PATH_MEDIA, 'tmp', name)
|
||||
if not is_file(path):
|
||||
return ()
|
||||
|
||||
if APP_LIBO:
|
||||
app = LIBO()
|
||||
if app.is_running:
|
||||
return app.products(path)
|
||||
|
||||
return ()
|
|
@ -258,10 +258,15 @@ class StorageEngine(object):
|
|||
def get_products(self, values):
|
||||
return main.Productos.get_(values)
|
||||
|
||||
def product(self, values):
|
||||
def products(self, values):
|
||||
id = int(values.pop('id', '0'))
|
||||
if id:
|
||||
return main.Productos.actualizar(values, id)
|
||||
|
||||
opt = values.get('opt', '')
|
||||
if opt:
|
||||
return main.Productos.opt(values)
|
||||
|
||||
return main.Productos.add(values)
|
||||
|
||||
def invoice(self, values, user):
|
||||
|
|
|
@ -894,6 +894,13 @@ class Categorias(BaseModel):
|
|||
def exists(cls, filters):
|
||||
return Categorias.select().where(filters).exists()
|
||||
|
||||
@classmethod
|
||||
def get_by_id(cls, id):
|
||||
try:
|
||||
return Categorias.get(Categorias.id==id)
|
||||
except:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def get_all(cls):
|
||||
rows = (Categorias.select(
|
||||
|
@ -945,6 +952,13 @@ class SATUnidades(BaseModel):
|
|||
def __str__(self):
|
||||
return '{} ({})'.format(self.name, self.key)
|
||||
|
||||
@classmethod
|
||||
def get_by_name(self, name):
|
||||
try:
|
||||
return SATUnidades.get(SATUnidades.name==name)
|
||||
except:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def get_(self):
|
||||
rows = SATUnidades.select().dicts()
|
||||
|
@ -2167,6 +2181,81 @@ class Productos(BaseModel):
|
|||
|
||||
return {'ok': False}
|
||||
|
||||
def _validate_import(self, product):
|
||||
product['categoria'] = Categorias.get_by_id(int(product['categoria']))
|
||||
product['unidad'] = SATUnidades.get_by_name(product['unidad'])
|
||||
product['inventario'] = bool(product['inventario'])
|
||||
if not product['inventario']:
|
||||
product['existencia'] = 0.0
|
||||
|
||||
impuestos = product['impuestos'].split('|')
|
||||
if not impuestos:
|
||||
taxes = [SATImpuestos.select().where(SATImpuestos.id==6)]
|
||||
else:
|
||||
taxes = []
|
||||
for i in range(0, len(impuestos), 3):
|
||||
w = (
|
||||
(SATImpuestos.key == impuestos[i]) &
|
||||
(SATImpuestos.name == impuestos[i+1]) &
|
||||
(SATImpuestos.tasa == float(impuestos[i+2]))
|
||||
)
|
||||
try:
|
||||
taxes.append(SATImpuestos.get(w))
|
||||
except:
|
||||
pass
|
||||
|
||||
product['impuestos'] = taxes
|
||||
w = (Productos.clave==product['clave'])
|
||||
return product, w
|
||||
|
||||
def _import(self):
|
||||
emisor = Emisor.select()[0]
|
||||
rows, msg = util.import_products(emisor.rfc)
|
||||
if not rows:
|
||||
return {'ok': False, 'msg': msg}
|
||||
|
||||
np = 0
|
||||
ap = 0
|
||||
for p in rows:
|
||||
data, w = self._validate_import(self, p)
|
||||
if data['unidad'] is None:
|
||||
msg = 'Producto: {} - No se encontró la unidad'.format(data['clave'])
|
||||
log.error(msg)
|
||||
continue
|
||||
# ~ print (data)
|
||||
taxes = data.pop('impuestos')
|
||||
try:
|
||||
with database_proxy.transaction():
|
||||
if Productos.select().where(w).exists():
|
||||
q = Productos.update(**data).where(w)
|
||||
q.execute()
|
||||
obj = Productos.get(w)
|
||||
obj.impuestos = taxes
|
||||
log.info('Producto actualizado...')
|
||||
ap += 1
|
||||
else:
|
||||
obj = Productos.create(**data)
|
||||
obj.impuestos = taxes
|
||||
log.info('Producto agregado...')
|
||||
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 con problemas: {}'.format(len(rows) - np - ap)
|
||||
return {'ok': True, 'msg': msg}
|
||||
|
||||
@classmethod
|
||||
def opt(cls, values):
|
||||
if values['opt'] == 'import':
|
||||
return cls._import(cls)
|
||||
|
||||
return {'ok': False, 'msg': 'Sin opción'}
|
||||
|
||||
@classmethod
|
||||
def get_by(cls, values):
|
||||
clave = values.get('id', '')
|
||||
|
@ -2197,7 +2286,6 @@ class Productos(BaseModel):
|
|||
return {'ok': False}
|
||||
|
||||
name = values.get('name', '')
|
||||
print (name)
|
||||
if name:
|
||||
rows = (Productos
|
||||
.select(
|
||||
|
|
|
@ -7,6 +7,7 @@ var products_controllers = {
|
|||
$$("cmd_delete_product").attachEvent("onItemClick", cmd_delete_product_click)
|
||||
$$("cmd_save_product").attachEvent("onItemClick", cmd_save_product_click)
|
||||
$$("cmd_cancel_product").attachEvent("onItemClick", cmd_cancel_product_click)
|
||||
$$("cmd_import_products").attachEvent("onItemClick", cmd_import_products_click)
|
||||
$$("chk_automatica").attachEvent("onChange", chk_automatica_change)
|
||||
$$("valor_unitario").attachEvent("onChange", valor_unitario_change)
|
||||
$$('precio_con_impuestos').attachEvent('onChange', precio_con_impuestos_change)
|
||||
|
@ -311,3 +312,77 @@ function precio_con_impuestos_key_up(){
|
|||
}
|
||||
calcular_sin_impuestos(parseFloat(value), taxes)
|
||||
}
|
||||
|
||||
|
||||
function cmd_import_products_click(){
|
||||
win_import_products.init()
|
||||
$$('win_import_products').show()
|
||||
}
|
||||
|
||||
|
||||
function cmd_upload_products_click(){
|
||||
var form = $$('form_upload_products')
|
||||
|
||||
var values = form.getValues()
|
||||
|
||||
if(!$$('lst_upload_products').count()){
|
||||
$$('win_import_products').close()
|
||||
return
|
||||
}
|
||||
|
||||
if($$('lst_upload_products').count() > 1){
|
||||
msg = 'Selecciona solo un archivo'
|
||||
msg_error(msg)
|
||||
return
|
||||
}
|
||||
|
||||
var template = $$('up_products').files.getItem($$('up_products').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 Productos',
|
||||
ok: 'Si',
|
||||
cancel: 'No',
|
||||
type: 'confirm-error',
|
||||
text: msg,
|
||||
callback:function(result){
|
||||
if(result){
|
||||
$$('up_products').send()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function up_products_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_products').close()
|
||||
|
||||
webix.ajax().post('/products', {opt: 'import'}, {
|
||||
error: function(text, data, xhr) {
|
||||
msg = 'Error al importar'
|
||||
msg_error(msg)
|
||||
},
|
||||
success: function(text, data, xhr) {
|
||||
var values = data.json();
|
||||
if (values.ok){
|
||||
msg_ok(values.msg)
|
||||
get_products()
|
||||
}else{
|
||||
msg_error(values.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
|
@ -7,6 +7,9 @@ var toolbar_products = [
|
|||
autowidth: true, icon: "pencil"},
|
||||
{view: "button", id: "cmd_delete_product", label: "Eliminar",
|
||||
type: "iconButton", autowidth: true, icon: "minus"},
|
||||
{},
|
||||
{view: 'button', id: 'cmd_import_products', label: 'Importar',
|
||||
type: 'iconButton', autowidth: true, icon: 'upload'},
|
||||
]
|
||||
|
||||
|
||||
|
@ -213,3 +216,33 @@ var app_products = {
|
|||
}
|
||||
|
||||
|
||||
var body_import_products = {rows: [
|
||||
{view: 'form', id: 'form_upload_products', rows: [
|
||||
{cols: [{},
|
||||
{view: 'uploader', id: 'up_products', autosend: false,
|
||||
link: 'lst_upload_products', value: 'Seleccionar Archivo',
|
||||
upload: '/files/products'}, {}]},
|
||||
{cols: [
|
||||
{view: 'list', id: 'lst_upload_products', name: 'lst_upload_products',
|
||||
type: 'uploader', autoheight: true, borderless: true}]},
|
||||
{cols: [{}, {view: 'button', id: 'cmd_upload_products',
|
||||
label: 'Importar Productos'}, {}]},
|
||||
]},
|
||||
]}
|
||||
|
||||
|
||||
var win_import_products = {
|
||||
init: function(){
|
||||
webix.ui({
|
||||
view: 'window',
|
||||
id: 'win_import_products',
|
||||
width: 400,
|
||||
modal: true,
|
||||
position: 'center',
|
||||
head: 'Importar Productos',
|
||||
body: body_import_products,
|
||||
})
|
||||
$$('cmd_upload_products').attachEvent('onItemClick', cmd_upload_products_click)
|
||||
$$('up_products').attachEvent('onUploadComplete', up_products_upload_complete)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue