Agregar, editar y eliminar productos

This commit is contained in:
Mauricio Baeza 2017-10-04 00:11:49 -05:00
parent 52169cb77c
commit d396f5720e
14 changed files with 588 additions and 73 deletions

View File

@ -93,3 +93,27 @@ class AppPartners(object):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204
class AppProducts(object):
def __init__(self, db):
self._db = db
def on_get(self, req, resp):
values = req.params
req.context['result'] = self._db.get_products(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
#~ print ('VALUES', values)
req.context['result'] = self._db.product(values)
resp.status = falcon.HTTP_200
def on_delete(self, req, resp):
values = req.params
if self._db.delete('product', values['id']):
resp.status = falcon.HTTP_200
else:
resp.status = falcon.HTTP_204

View File

@ -12,7 +12,7 @@ import uuid
#~ import bcrypt
from settings import log, template_lookup, COMPANIES
from settings import log, template_lookup, COMPANIES, DB_SAT
#~ def _get_hash(password):
@ -86,6 +86,19 @@ def get_rfcs():
return values
def get_sat_key(table, key):
con = sqlite3.connect(DB_SAT)
cursor = con.cursor()
sql = 'SELECT key, description FROM {} WHERE key=?'.format(table)
cursor.execute(sql, (key,))
data = cursor.fetchone()
cursor.close()
con.close()
if data is None:
return {'ok': False, 'text': 'No se encontró la clave'}
return {'ok': True, 'text': data[1]}
def now():
return datetime.datetime.now()

View File

@ -12,7 +12,7 @@ from middleware import (
)
from models.db import StorageEngine
from controllers.main import (
AppLogin, AppLogout, AppAdmin, AppMain, AppValues, AppPartners
AppLogin, AppLogout, AppAdmin, AppMain, AppValues, AppPartners, AppProducts
)
from settings import DEBUG
@ -30,6 +30,7 @@ api.add_route('/admin', AppAdmin(db))
api.add_route('/main', AppMain(db))
api.add_route('/values/{table}', AppValues(db))
api.add_route('/partners', AppPartners(db))
api.add_route('/products', AppProducts(db))
if DEBUG:

View File

@ -20,13 +20,30 @@ class StorageEngine(object):
def _get_formapago(self, values):
return main.SATFormaPago.get_activos()
def _get_categorias(self, values):
return main.Categorias.get_all()
def _get_newkey(self, values):
return main.Productos.next_key()
def _get_unidades(self, values):
return main.SATUnidades.get_activos()
def _get_taxes(self, values):
return main.SATImpuestos.get_activos()
def _get_satkey(self, values):
return main.get_sat_key(values['key'])
def delete(self, table, id):
if table == 'partner':
return main.Socios.remove(id)
if table == 'product':
return main.Productos.remove(id)
return False
def get_partners(self, values):
return main.Socios.get(values)
return main.Socios.get_(values)
def partner(self, values):
id = int(values['id'])
@ -35,6 +52,16 @@ class StorageEngine(object):
return main.Socios.actualizar(values, id)
return main.Socios.add(values)
def get_products(self, values):
return main.Productos.get_(values)
def product(self, values):
id = int(values['id'])
del values['id']
if id:
return main.Productos.actualizar(values, id)
return main.Productos.add(values)

View File

@ -179,6 +179,15 @@ class Categorias(BaseModel):
(('categoria', 'padre'), True),
)
@classmethod
def get_all(cls):
rows = (Categorias.select(
Categorias.id,
Categorias.categoria.alias('value'),
Categorias.padre.alias('parent_id'))
).dicts()
return tuple(rows)
class CondicionesPago(BaseModel):
condicion = TextField(unique=True)
@ -199,6 +208,17 @@ class SATUnidades(BaseModel):
(('key', 'name'), True),
)
@classmethod
def get_activos(cls):
rows = (SATUnidades
.select(
SATUnidades.id,
SATUnidades.name.alias('value'))
.where(SATUnidades.activo==True)
.dicts()
)
return tuple(rows)
class SATFormaPago(BaseModel):
key = TextField(unique=True, index=True)
@ -265,6 +285,11 @@ class SATImpuestos(BaseModel):
(('key', 'factor', 'tipo', 'tasa'), True),
)
@classmethod
def get_activos(self):
rows = SATImpuestos.select().where(SATImpuestos.activo==True).dicts()
return tuple(rows)
class SATUsoCfdi(BaseModel):
key = TextField(index=True, unique=True)
@ -343,7 +368,7 @@ class Socios(BaseModel):
return fields
@classmethod
def get(cls, values):
def get_(cls, values):
if values:
id = int(values['id'])
row = Socios.select().where(Socios.id==id).dicts()[0]
@ -425,6 +450,129 @@ class Productos(BaseModel):
class Meta:
order_by = ('descripcion',)
@classmethod
def next_key(cls):
value = Productos.select(fn.Max(Productos.id)).scalar()
if value is None:
value = 1
else:
value += 1
return {'value': value}
@classmethod
def get_(cls, values):
if values:
id = int(values['id'])
row = (Productos
.select(
Productos.id,
Productos.es_activo.alias('es_activo_producto'),
Productos.categoria,
Productos.clave,
Productos.clave_sat,
Productos.descripcion,
Productos.unidad,
Productos.valor_unitario,
)
.where(Productos.id==id).dicts()[0]
)
obj = Productos.get(Productos.id==id)
taxes = [row.id for row in obj.impuestos]
return {'row': row, 'taxes': taxes}
rows = (Productos
.select(
Productos.id,
Productos.clave,
Productos.descripcion,
SATUnidades.name.alias('unidad'),
Productos.valor_unitario)
.join(SATUnidades)
.dicts()
)
return {'ok': True, 'rows': tuple(rows)}
def _clean(self, values):
taxes = util.loads(values.pop('taxes'))
fields = util.clean(values)
fields['es_activo'] = fields.pop('es_activo_producto')
fields['descripcion'] = util.spaces(fields['descripcion'])
fields['unidad'] = int(fields['unidad'])
fields['valor_unitario'] = fields['valor_unitario'].replace(
'$', '').replace(',', '')
fb = ('es_activo', 'inventario')
for name in fb:
fields[name] = bool(fields[name].replace('0', ''))
return fields, taxes
@classmethod
def add(cls, values):
fields, taxes = cls._clean(cls, values)
if Productos.select().where(Productos.clave==fields['clave']).exists():
msg = 'Clave ya existe'
return {'ok': False, 'msg': msg}
obj_taxes = SATImpuestos.select().where(SATImpuestos.id.in_(taxes))
with database_proxy.transaction():
obj = Productos.create(**fields)
obj.impuestos = obj_taxes
row = {
'id': obj.id,
'clave': obj.clave,
'descripcion': obj.descripcion,
'unidad': obj.unidad.name,
'valor_unitario': obj.valor_unitario,
}
data = {'ok': True, 'row': row, 'new': True}
return data
@classmethod
def actualizar(cls, values, id):
fields, taxes = cls._clean(cls, values)
obj_taxes = SATImpuestos.select().where(SATImpuestos.id.in_(taxes))
with database_proxy.transaction():
q = Productos.update(**fields).where(Productos.id==id)
try:
q.execute()
except IntegrityError:
msg = 'Ya existe un producto con esta clave'
data = {'ok': False, 'row': {}, 'new': False, 'msg': msg}
return data
obj = Productos.get(Productos.id==id)
obj.impuestos = obj_taxes
row = {
'id': obj.id,
'clave': obj.clave,
'descripcion': obj.descripcion,
'unidad': obj.unidad.name,
'valor_unitario': obj.valor_unitario,
}
data = {'ok': True, 'row': row, 'new': False}
return data
@classmethod
def remove(cls, id):
count = (FacturasDetalle
.select(fn.COUNT(FacturasDetalle.id)).join(Productos)
.where(Productos.id==id)
.count()
)
if count:
return False
with database_proxy.transaction():
obj = Productos.get(Productos.id==id)
obj.impuestos.clear()
obj.tags.clear()
q = Productos.delete().where(Productos.id==id)
return bool(q.execute())
class Facturas(BaseModel):
cliente = ForeignKeyField(Socios)
@ -553,6 +701,10 @@ def get_cp(cp):
return data
def get_sat_key(key):
return util.get_sat_key('products', key)
def _init_values():
data = (
{'key': 'version', 'value': VERSION},

View File

@ -18,6 +18,7 @@ PATH_TEMPLATES = os.path.abspath(os.path.join(BASE_DIR, '..', 'templates'))
PATH_CP = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'cp.db'))
COMPANIES = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'rfc.db'))
DB_SAT = os.path.abspath(os.path.join(BASE_DIR, '..', 'db', 'sat.db'))
template_lookup = TemplateLookup(directories=[PATH_TEMPLATES],
input_encoding='utf-8',

BIN
source/db/sat.db Normal file

Binary file not shown.

View File

@ -7,6 +7,13 @@
],
"regimen": "603"
},
{
"tabla": "Categorias",
"datos": [
{"categoria": "Productos"},
{"categoria": "Servicios"}
]
},
{
"tabla": "SATImpuestos",
"datos": [

View File

@ -1,7 +1,3 @@
var PUBLICO = "Público en general";
var RFC_PUBLICO = "XAXX010101000";
var RFC_EXTRANJERO = "XEXX010101000";
var PAIS = "México";
var controllers = {
@ -30,17 +26,18 @@ var 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);
$$("chk_automatica").attachEvent("onChange", chk_automatica_change)
$$("valor_unitario").attachEvent("onChange", valor_unitario_change)
//~ Invoices
$$("cmd_new_invoice").attachEvent("onItemClick", cmd_new_invoice_click);
$$("cmd_edit_invoice").attachEvent("onItemClick", cmd_edit_invoice_click);
$$("cmd_delete_invoice").attachEvent("onItemClick", cmd_delete_invoice_click);
$$("cmd_save_invoice").attachEvent("onItemClick", cmd_save_invoice_click);
$$("cmd_cancel_invoice").attachEvent("onItemClick", cmd_cancel_invoice_click);
//~ get_partners()
}
};
function get_partners(){
webix.ajax().get("/partners", {}, {
error: function(text, data, xhr) {
@ -56,6 +53,24 @@ function get_partners(){
});
}
function get_products(){
var grid = $$('grid_products')
webix.ajax().get('/products', {}, {
error: function(text, data, xhr) {
webix.message({type: 'error', text: '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';
@ -71,5 +86,26 @@ function multi_change(prevID, nextID){
if(active == 'partners_home'){
get_partners()
}
return
}
if(nextID == 'app_products'){
active = $$('multi_products').getActiveId()
if(active == 'products_home'){
get_products()
}
return
}
}
function get_taxes(){
webix.ajax().sync().get('/values/taxes', function(text, data){
var values = data.json()
table_taxes.clear()
table_taxes.insert(values)
$$("grid_product_taxes").clearAll()
$$("grid_product_taxes").parse(values, 'json')
})
}

View File

@ -1,43 +1,203 @@
function cmd_new_product_click(id, e, node){
$$('form_product').setValues({
id: 0, es_activo_producto: true})
add_config({'key': 'id_product', 'value': ''})
get_new_key()
get_taxes()
$$('grid_products').clearSelection()
$$('categoria').getList().load('/values/categorias')
$$('unidad').getList().load('/values/unidades')
$$("multi_products").setValue("product_new")
};
}
function cmd_edit_product_click(id, e, node){
$$("multi_products").setValue("product_new")
};
function cmd_delete_product_click(id, e, node){
webix.message({type:"success", text: "OK Delete"});
};
function cmd_save_product_click(id, e, node){
var form = this.getFormView();
if (!form.validate()) {
webix.message({ type:"error", text:"Valores inválidos" });
var grid = $$('grid_products')
var row = grid.getSelectedItem()
if(row == undefined){
webix.message({type: 'error', text: 'Selecciona un Producto'})
return
}
$$('form_product').clear();
$$("multi_products").setValue("products_home")
webix.message({type:"success", text: "Producto guardado correctamente"});
get_taxes()
$$('categoria').getList().load('/values/categorias')
$$('unidad').getList().load('/values/unidades')
webix.ajax().get('/products', {id:row['id']}, {
error: function(text, data, xhr) {
webix.message({type: 'error', text: 'Error al consultar'})
},
success: function(text, data, xhr){
var values = data.json()
$$('form_product').setValues(values.row)
add_config({'key': 'id_product', 'value': values.row.id})
for(i = 0; i < values.taxes.length; i++){
$$('grid_product_taxes').select(values.taxes[i], true)
}
}
})
$$('multi_products').setValue('product_new')
};
function delete_product(id){
webix.ajax().del('/products', {id:id}, function(text, xml, xhr){
var msg = 'Producto eliminado correctamente'
if(xhr.status == 200){
$$('grid_products').remove(id)
webix.message({type:'success', text:msg})
}else{
msg = 'No se pudo eliminar'
webix.message({type:'error', text:msg})
}
})
}
function cmd_delete_product_click(id, e, node){
var row = $$('grid_products').getSelectedItem()
if (row == undefined){
webix.message({type:'error', text: 'Selecciona un Producto'})
return
}
var msg = '¿Estás seguro de eliminar el Producto?<BR><BR>'
msg += '(' + row['clave'] + ') ' + row['descripcion']
msg += '<BR><BR>ESTA ACCIÓN NO SE PUEDE DESHACER<BR><BR>Se recomienda '
msg += 'solo desactivar el producto en vez de eliminar'
webix.confirm({
title: 'Eliminar Producto',
ok: 'Si',
cancel: 'No',
type: 'confirm-error',
text: msg,
callback:function(result){
if (result){
delete_product(row['id'])
}
}
})
}
function validate_sat_key_product(key, text){
var result = false
webix.ajax().sync().get('/values/satkey', {key:key}, function(text, data){
result = data.json()
})
if(text){
if(result.ok){
return '<b>' + result.text + '</b>'
}else{
return '<b><font color="red">' + result.text + '</font></b>'
}
}
return result.ok
}
function update_grid_products(values){
var msg = 'Producto agregado correctamente'
if(values.new){
$$('form_product').clear()
$$('grid_products').add(values.row)
}else{
msg = 'Producto actualizado correctamente'
$$("grid_products").updateItem(values.row['id'], values.row)
}
$$('multi_products').setValue('products_home')
webix.message({type: 'success', text: msg})
}
function cmd_save_product_click(id, e, node){
var msg = ''
var form = this.getFormView()
if(!form.validate()){
webix.message({type: 'error', text: 'Valores inválidos'})
return
}
var rows = $$('grid_product_taxes').getSelectedId(true, true)
if (rows.length == 0){
webix.message({type: 'error', text: 'Selecciona un impuesto'})
return
}
var values = form.getValues();
if (!validate_sat_key_product(values.clave_sat, false)){
webix.message({ type:'error', text:'La clave SAT no existe' })
return
}
values['taxes'] = JSON.stringify(rows)
webix.ajax().sync().post('products', values, {
error:function(text, data, XmlHttpRequest){
msg = 'Ocurrio un error, consulta a soporte técnico'
webix.message({type: 'error', text: msg})
},
success:function(text, data, XmlHttpRequest){
var values = data.json();
if (values.ok) {
update_grid_products(values)
}else{
webix.message({type:'error', text:values.msg})
}
}
})
}
function cmd_cancel_product_click(id, e, node){
$$("multi_products").setValue("products_home")
};
function chk_automatica_change(new_value, old_value){
var value = Boolean(new_value)
if (value){
var value = get_config('id_product')
if(value){
$$("clave").setValue(value)
$$("clave").refresh()
}else{
get_new_key()
}
$$("clave").config.readonly = true
$$('form_product').focus('clave_sat')
} else {
$$("clave").setValue('')
$$("clave").config.readonly = false
$$('form_product').focus('clave')
}
$$("clave").refresh()
}
function get_new_key(){
webix.ajax().get('/values/newkey', {
error: function(text, data, xhr) {
webix.message({type:'error', text: text})
},
success: function(text, data, xhr) {
var values = data.json();
$$("clave").setValue(values.value)
$$("clave").refresh()
}
})
}
function valor_unitario_change(new_value, old_value){
if(!isFinite(new_value)){
this.config.value = old_value
this.refresh()
}
}

View File

@ -1,3 +1,43 @@
var PUBLICO = "Público en general";
var RFC_PUBLICO = "XAXX010101000";
var RFC_EXTRANJERO = "XEXX010101000";
var PAIS = "México";
var db = new loki('data.db');
var table_config = db.addCollection('config')
var table_taxes = db.addCollection('taxes')
var table_pt = db.addCollection('productstaxes')
var table_totals = db.addCollection('totals', {unique: ['tax']})
function show(values){
webix.message(JSON.stringify(values, null, 2))
}
webix.protoUI({
$cssName: "text",
name: "currency",
$init:function(){
this.attachEvent("onItemClick", function(){
this.$setValue(this.config.raw, true)
})
this.attachEvent("onBlur", function(){
this.$setValue(this.config.value)
})
},
$render:function(){
this.$setValue(this.config.value)
},
$setValue:function(value, raw){
this.config.raw = value
if(!raw){
value = webix.i18n.priceFormat(value)
}
this.getInputNode().value = value
}
}, webix.ui.text)
webix.ui.datafilter.rowCount = webix.extend({
@ -68,3 +108,24 @@ function validate_email(email){
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
return re.test(email)
}
function add_config(args){
var key = table_config.findOne({key: args.key})
if(key===null){
table_config.insert(args)
}else{
key.value = args.value
table_config.update(key)
}
}
function get_config(value){
var key = table_config.findOne({key: value})
if(key===null){
return ''
}else{
return key.value
}
}

3
source/static/js/lokijs.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -12,14 +12,14 @@ var toolbar_products = [
var grid_products_cols = [
{ id: "id", header: "ID", width: 75},
{ id: "key", header: ["Clave", {content: "textFilter"}], width: 100,
{ id: "clave", header: ["Clave", {content: "textFilter"}], width: 100,
sort:"string" },
{ id: "description", header: ["Descripción", {content: "textFilter"}],
{ id: "descripcion", header: ["Descripción", {content: "textFilter"}],
fillspace:true, sort:"string" },
{ id: "unidad", header: ["Unidad", {content: "selectFilter"}], width: 150,
sort:"string" },
{ id: "price", header: ["Precio", {content: "numberFilter"}], width: 150,
sort:"int", format: webix.i18n.priceFormat, css: "cell_right" },
{ id: "valor_unitario", header: ["Precio", {content: "numberFilter"}], width: 150,
sort:"int", format: webix.i18n.priceFormat, css: "right" },
]
@ -34,51 +34,81 @@ var grid_products = {
columns: grid_products_cols,
}
var suggest_categories = {
view: "datasuggest",
type: "tree",
width: 400,
body: { data: [] },
}
var grid_product_taxes_cols = [
{id:"id", header:"ID", hidden: true},
{id:"name", header:'Nombre'},
{id:"tipo", header:'Tipo'},
{id:"tasa", header:'Tasa'},
]
var grid_product_taxes = {
view: 'datatable',
id: 'grid_product_taxes',
select: 'row',
multiselect: true,
adjust: true,
autoheight: true,
autowidth: true,
headermenu: true,
columns: grid_product_taxes_cols,
}
//~ webix.ui({
//~ view:"combo", suggest:{body:{view:"tree", data:[]}},
//~ on:{onChange:function(newv, oldv){
//~ webix.message("Value changed from: "+oldv+" to: "+newv);
//~ }}, width:300
//~ });
var controls_generals = [
{view: 'checkbox', id: 'es_activo_producto', name: 'es_activo_producto',
label: 'Activo: ', value: true,
bottomLabel: 'Se recomienda solo desactivar y no eliminar'},
{cols: [
{view: 'combo', id: 'categoria', name: 'categoria',
label: 'Categoría', labelPosition: 'top',
suggest: {body: {view: 'tree', data:[]}}},
{view: 'combo', id: 'categoria', name: 'categoria', label: 'Categoría',
labelPosition: 'top', options: suggest_categories},
{view: 'text', id: 'clave', name: 'clave', label: 'Clave',
labelPosition: 'top', readonly: true, required: true},
{view: 'checkbox', label: 'Automática', labelPosition: 'top',
value: true, maxWidth: 80},
{view: 'text', id: 'clave_sat', name: 'clave_sat', label: 'Clave SAT',
labelPosition: 'top', required: true},
{view: 'checkbox', id: 'chk_automatica', label: 'Automática',
labelPosition: 'top', value: true, maxWidth: 80},
{view: 'search', id: 'clave_sat', name: 'clave_sat', label: 'Clave SAT',
labelPosition: 'top', required: true, placeholder: 'Buscar clave...'},
]},
{view: 'text', id: 'codigo_barras', name: 'codigo_barras',
label: 'Código de Barras', labelPosition: 'top'},
{view: "textarea", id: "description", name: "description", height: 200,
{cols: [
{view: 'text', id: 'codigo_barras', name: 'codigo_barras',
label: 'Código de Barras', labelPosition: 'top', hidden: true},
{view: 'text', id: 'cuenta_predial', name: 'cuenta_predial',
label: 'Cuenta Predial', labelPosition: 'top', hidden: true},
{id: 'txt_col1'}]},
{view: "textarea", id: "descripcion", name: "descripcion", height: 200,
label: "Descripción", required: true, labelPosition: "top",
invalidMessage: "La Descripción es requerida" },
{cols: [{view: "text", id: "unidad", name: "unidad", label: "Unidad",
width: 300, labelWidth: 130, labelAlign: "right", required: true,
invalidMessage: "La Unidad es requerida" },{}]},
{cols: [{view: "search", type: "text", id: "price", name: "price",
label: "Valor Unitario", width: 300, labelWidth: 130,
labelAlign: "right", required: true, icon: "calculator",
invalidMessage: "Captura un valor númerico", inputAlign:"right" },{}]},
{minHeight: 5},
{cols: [
{view: 'checkbox', id: 'inventario', name: 'inventario',
label: 'Inventario', labelAlign: 'right', labelWidth: 130},
{view: 'counter', id: 'existencia', name: 'existencia',
label: 'Existencia', step: 5, value: 0, min: 0, disabled: true},
{view: 'counter', id: 'minimo', name: 'minimo',
label: 'Mínimo', step: 5, value: 0, min: 0, disabled: true},
{view: "richselect", id: "unidad", name: "unidad", label: "Unidad",
width: 300, labelWidth: 130, labelAlign: "right", required: true,
invalidMessage: "La Unidad es requerida", options: []},
{view: 'text', id: 'tags_producto', name: 'tags_producto',
labelAlign: 'right', label: 'Etiquetas',
placeholder: 'Separadas por comas'}
]},
{view: 'text', id: 'tags_producto', name: 'tags_producto', label: 'Etiquetas',
tooltip: 'Utiles para filtrados rápidos. Separa por comas.'},
{cols: [{view: "currency", type: "text", id: "valor_unitario",
name: "valor_unitario", label: "Valor Unitario", width: 300,
labelWidth: 130, labelAlign: "right", required: true,
invalidMessage: "Captura un valor númerico", inputAlign: "right" },{}]},
{cols: [
{view: 'checkbox', id: 'inventario', name: 'inventario', hidden: true,
label: 'Inventario', labelAlign: 'right', labelWidth: 130},
{view: 'counter', id: 'existencia', name: 'existencia', hidden: true,
label: 'Existencia', step: 5, value: 0, min: 0, disabled: true},
{view: 'counter', id: 'minimo', name: 'minimo', hidden: true,
label: 'Mínimo', step: 5, value: 0, min: 0, disabled: true},
{id: 'txt_col2'}]},
{cols:[{view:'label', label:'Impuestos', width: 300, align:'center'}, {}]},
{cols:[grid_product_taxes, {}]}
]
@ -88,7 +118,7 @@ var controls_products = [
tabbar: { options: ["Datos"]}, animate: true,
cells: [
{id: "Datos", rows: controls_generals},
]
],
},
{rows: [
{ template:"", type: "section" },
@ -110,9 +140,8 @@ var form_product = {
complexData: true,
elements: controls_products,
rules: {
description: function(value){ return value.trim() != ""; },
unidad: function(value){ return value.trim() != ""; },
price: function(value){ return value.trim() != ""; },
descripcion: function(value){ return value.trim() != ""; },
valor_unitario: function(value){ return value.trim() != "$"; },
}
}],
}

View File

@ -9,6 +9,7 @@
<link rel="stylesheet" href="/static/css/app.css" type="text/css">
<script src="/static/js/webix_debug.js" type="text/javascript" ></script>
<script src="/static/js/es-MX.js" type="text/javascript" ></script>
<script src="/static/js/lokijs.min.js" type="text/javascript" ></script>
<%block name="media"/>
</head>