diff --git a/source/app/controllers/main.py b/source/app/controllers/main.py
index e55adf8..f739874 100644
--- a/source/app/controllers/main.py
+++ b/source/app/controllers/main.py
@@ -78,12 +78,13 @@ class AppPartners(object):
def on_get(self, req, resp):
values = req.params
+ print ('GET VALUES', values)
req.context['result'] = self._db.get_partners(values)
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
values = req.params
- #~ print ('VALUES', values)
+ print ('POST VALUES', values)
req.context['result'] = self._db.partner(values)
resp.status = falcon.HTTP_200
diff --git a/source/app/middleware.py b/source/app/middleware.py
index 3067cbb..127271c 100644
--- a/source/app/middleware.py
+++ b/source/app/middleware.py
@@ -40,6 +40,9 @@ class AuthMiddleware(object):
class JSONTranslator(object):
+ #~ def process_request(self, req, resp):
+ #~ pass
+
def process_response(self, req, resp, resource):
if 'result' not in req.context:
return
diff --git a/source/app/models/db.py b/source/app/models/db.py
index 82a9262..59a2a45 100644
--- a/source/app/models/db.py
+++ b/source/app/models/db.py
@@ -51,8 +51,11 @@ class StorageEngine(object):
return main.Productos.remove(id)
return False
- def _get_partner(self, values):
- return main.Socios.get_by(values)
+ def _get_client(self, values):
+ return main.Socios.get_by_client(values)
+
+ def _get_product(self, values):
+ return main.Productos.get_by(values)
def get_partners(self, values):
return main.Socios.get_(values)
diff --git a/source/app/models/main.py b/source/app/models/main.py
index 52799ad..0dfa407 100644
--- a/source/app/models/main.py
+++ b/source/app/models/main.py
@@ -416,12 +416,16 @@ class Socios(BaseModel):
return {'ok': True, 'rows': tuple(rows)}
@classmethod
- def get_by(cls, values):
+ def get_by_client(cls, values):
id = int(values.get('id', 0))
if id:
row = (Socios
- .select(Socios.id, Socios.nombre, Socios.rfc)
- .where(Socios.id==id).dicts())
+ .select(
+ Socios.id, Socios.nombre, Socios.rfc)
+ .where(
+ (Socios.id==id) & (Socios.es_cliente==True))
+ .dicts()
+ )
if len(row):
return {'ok': True, 'row': row[0]}
return {'ok': False}
@@ -430,11 +434,10 @@ class Socios(BaseModel):
if name:
rows = (Socios
.select(Socios.id, Socios.nombre, Socios.rfc)
- .where(
- Socios.rfc.contains(name) |
- Socios.nombre.contains(name))
+ .where((Socios.es_cliente==True) &
+ (Socios.rfc.contains(name) |
+ Socios.nombre.contains(name)))
.dicts())
- print (tuple(rows))
return tuple(rows)
return {'ok': False}
@@ -520,6 +523,35 @@ class Productos(BaseModel):
value += 1
return {'value': value}
+ @classmethod
+ def get_by(cls, values):
+ id = int(values.get('id', 0))
+ if id:
+ row = (Productos
+ .select(Productos.id, Productos.clave, Productos.descripcion,
+ SATUnidades.name.alias('unidad'), Productos.valor_unitario)
+ .join(SATUnidades).switch(Productos)
+ .where(Productos.id==id).dicts())
+ if len(row):
+ model_pt = Productos.impuestos.get_through_model()
+ taxes = tuple(model_pt
+ .select(
+ model_pt.productos_id.alias('product'),
+ model_pt.satimpuestos_id.alias('tax'))
+ .where(model_pt.productos_id==id).dicts())
+ return {'ok': True, 'row': row[0], 'taxes': taxes}
+ return {'ok': False}
+
+ name = values.get('name', '')
+ if name:
+ rows = (Products
+ .select(Products.id, Products.key, Products.description,
+ SATUnidades.name.alias('unit'), Products.price)
+ .join(SATUnidades).switch(Products)
+ .where(Products.description.contains(name)).dicts())
+ return tuple(rows)
+ return {'ok': False}
+
@classmethod
def get_(cls, values):
if values:
diff --git a/source/static/css/app.css b/source/static/css/app.css
index 0e5b00a..4e8eb53 100644
--- a/source/static/css/app.css
+++ b/source/static/css/app.css
@@ -30,6 +30,14 @@
font-size: 125%;
}
+.delete {
+ text-align: center;
+ font-weight: bold;
+ font-size: 250%;
+ color: red;
+}
+
+
.cmd_close_partner div button {
background-color: red !important;
border-color: red !important;
diff --git a/source/static/js/controller/invoices.js b/source/static/js/controller/invoices.js
index bcf5448..02e3892 100644
--- a/source/static/js/controller/invoices.js
+++ b/source/static/js/controller/invoices.js
@@ -52,6 +52,11 @@ function get_uso_cfdi(){
function default_config(){
+ webix.ajax().sync().get('/values/taxes', function(text, data){
+ var values = data.json()
+ table_taxes.clear()
+ table_taxes.insert(values)
+ })
get_series()
get_forma_pago()
get_monedas()
@@ -68,12 +73,12 @@ function cmd_new_invoice_click(id, e, node){
default_config()
form.adjust()
- form.setValues({id: 0, id_partner: 0, lbl_partner: 'Ninguno'})
+ form.setValues({id: 0, id_partner: 0, lbl_client: 'Ninguno'})
grid.clearAll()
grid_totals.clearAll()
grid_totals.add({id: 1, concepto: 'SubTotal', importe: 0})
$$('multi_invoices').setValue('invoices_new')
- form.focus('search_partner_id')
+ form.focus('search_client_id')
}
@@ -110,16 +115,16 @@ function cmd_close_invoice_click(id, e, node){
}
-function search_partner_by_id(id){
+function search_client_by_id(id){
var msg = ''
- webix.ajax().get('/values/partner', {'id': id}, {
+ webix.ajax().get('/values/client', {'id': id}, {
error: function(text, data, xhr) {
webix.message({type: 'error', text: 'Error al consultar'})
},
success: function(text, data, xhr){
var values = data.json()
if (values.ok){
- set_partner(values.row)
+ set_client(values.row)
}else{
msg = 'No se encontró un cliente con la clave: ' + id
webix.message({type:'error', text: msg})
@@ -130,31 +135,176 @@ function search_partner_by_id(id){
}
-function set_partner(row){
+function set_client(row){
var form = $$('form_invoice')
var html = ''
form.setValues({
- id_partner:row.id, search_partner_id:'', search_partner_name:''}, true)
+ id_partner:row.id, search_client_id:'', search_client_name:''}, true)
html += row.nombre + ' (' + row.rfc + ')'
- $$('lbl_partner').setValue(html)
+ $$('lbl_client').setValue(html)
form.focus('search_product_id')
}
-function grid_partners_found_click(obj){
- set_partner(obj)
+function grid_clients_found_click(obj){
+ set_client(obj)
}
-function search_partner_id_key_press(code, e){
+function search_client_id_key_press(code, e){
var value = this.getValue()
if(code == 13 && value.length > 0){
var id = parseInt(value, 10)
if (isNaN(id)){
webix.message({type:'error', text:'Captura una clave válida'});
}else{
- search_partner_by_id(id)
+ search_client_by_id(id)
}
}
}
+
+
+function calculate_taxes(){
+ var tmp = null
+ table_totals.clear()
+ var subtotal = 0
+ var total_iva = 0
+ var id = 2
+ var grid_totals = $$('grid_totals')
+
+ grid_totals.clearAll()
+ grid_totals.add({id: 1, concepto: 'SubTotal', importe: 0})
+
+ grid.eachRow(function(row){
+ var product = grid.getItem(row)
+ subtotal += parseFloat(product.importe)
+ query = table_pt.chain().find({'product': product.id}).data()
+ for(var tax of query){
+ tmp = table_totals.findOne({'tax': tax.tax})
+ if(tmp === null){
+ table_totals.insert(
+ {'tax': tax.tax, 'importe': parseFloat(product.importe)})
+ tmp = table_totals.findOne({'tax': tax.tax})
+ }else{
+ tmp.importe += parseFloat(product.importe)
+ table_totals.update(tmp)
+ }
+ }
+ })
+
+ var tax = null
+ var tipo = 'Traslado '
+ var concepto = ''
+ var total_tax = 0
+ query = table_totals.chain().data()
+ for(var t of query){
+ tax = table_taxes.findOne({'id': t.tax})
+ if(tax.tipo == 'E' || tax.tipo == 'R'){
+ continue
+ }
+ concepto = tipo + tax.name + ' (' + tax.tasa + ')'
+ total_tax = (tax.tasa * t.importe).round(2)
+ grid_totals.add({id: id, concepto: concepto, importe: total_tax})
+ id += 1
+ if(tax.name == 'IVA'){
+ total_iva += total_tax
+ }
+ }
+
+ tipo = 'Retención '
+ for(var t of query){
+ tax = table_taxes.findOne({'id': t.tax})
+ if(tax.tipo == 'E' || tax.tipo == 'T'){
+ continue
+ }
+ concepto = tipo + tax.name + ' (' + tax.tasa + ')'
+ if(tax.tasa == (2/3).round(6)){
+ total_tax = (tax.tasa * total_iva * -1).round(2)
+ concepto = tipo + tax.name + ' (2/3)'
+ }else{
+ total_tax = (tax.tasa * t.importe * -1).round(2)
+ }
+ grid_totals.add({id: id, concepto: concepto, importe: total_tax})
+ id += 1
+ }
+
+ row = {importe: subtotal}
+ grid_totals.updateItem(1, row)
+}
+
+
+function set_product(values){
+ var taxes = values.taxes
+ var values = values.row
+ var form = $$('form_invoice')
+ var row = grid.getItem(values.id)
+
+ values['delete'] = '-'
+ if (row == undefined){
+ values['cantidad'] = 1
+ values['importe'] = values['valor_unitario']
+ grid.add(values)
+ } else {
+ values['cantidad'] = parseFloat(row.cantidad) + 1
+ values['importe'] = values['valor_unitario'] * values['cantidad']
+ grid.updateItem(row.id, values)
+ }
+ form.setValues({search_product_id:'', search_product_name:''}, true)
+
+ for(var v of taxes){
+ var pt = table_pt.findOne(v)
+ if(pt === null){
+ table_pt.insert(v)
+ }
+ }
+ calculate_taxes()
+}
+
+
+function grid_products_found_click(obj){
+ search_product_by_id(obj.id)
+}
+
+
+function search_product_by_id(id){
+ var msg = ''
+
+ webix.ajax().get('/values/product', {'id': id}, {
+ error: function(text, data, xhr) {
+ webix.message({type: 'error', text: '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
+ webix.message({type: 'error', text: msg})
+ }
+ }
+ })
+
+}
+
+
+function search_product_id_key_press(code, e){
+ var value = this.getValue()
+ if(code == 13 && value.length > 0){
+ var id = parseInt(value, 10)
+ if (isNaN(id)){
+ webix.message({type: 'error', text: 'Captura una clave válida'});
+ }else{
+ search_product_by_id(id)
+ }
+ }
+}
+
+
+function grid_details_click(id, e, node){
+ if(id.column != 'delete'){
+ return
+ }
+ grid.remove(id.row)
+ calculate_taxes()
+}
diff --git a/source/static/js/controller/main.js b/source/static/js/controller/main.js
index b2d1317..396a881 100644
--- a/source/static/js/controller/main.js
+++ b/source/static/js/controller/main.js
@@ -21,11 +21,11 @@ var controllers = {
$$("rfc").attachEvent( "onBlur", rfc_lost_focus)
$$('multi').attachEvent('onViewChange', multi_change)
//~ Products
- $$("cmd_new_product").attachEvent("onItemClick", cmd_new_product_click);
- $$("cmd_edit_product").attachEvent("onItemClick", cmd_edit_product_click);
- $$("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_new_product").attachEvent("onItemClick", cmd_new_product_click)
+ $$("cmd_edit_product").attachEvent("onItemClick", cmd_edit_product_click)
+ $$("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
@@ -34,8 +34,11 @@ var controllers = {
$$("cmd_delete_invoice").attachEvent("onItemClick", cmd_delete_invoice_click)
$$('cmd_timbrar').attachEvent('onItemClick', cmd_timbrar_click)
$$('cmd_close_invoice').attachEvent('onItemClick', cmd_close_invoice_click)
- $$('search_partner_id').attachEvent('onKeyPress', search_partner_id_key_press)
- $$('grid_partners_found').attachEvent('onValueSuggest', grid_partners_found_click);
+ $$('search_client_id').attachEvent('onKeyPress', search_client_id_key_press)
+ $$('grid_clients_found').attachEvent('onValueSuggest', grid_clients_found_click)
+ $$('search_product_id').attachEvent('onKeyPress', search_product_id_key_press)
+ $$('grid_products_found').attachEvent('onValueSuggest', grid_products_found_click)
+ $$('grid_details').attachEvent('onItemClick', grid_details_click)
}
}
diff --git a/source/static/js/controller/util.js b/source/static/js/controller/util.js
index 952c10b..6ae639f 100644
--- a/source/static/js/controller/util.js
+++ b/source/static/js/controller/util.js
@@ -18,6 +18,11 @@ function show(values){
}
+Number.prototype.round = function(decimals){
+ return Number((Math.round(this + "e" + decimals) + "e-" + decimals))
+}
+
+
webix.protoUI({
$cssName: "text",
name: "currency",
diff --git a/source/static/js/ui/invoices.js b/source/static/js/ui/invoices.js
index fdc432c..8da3482 100644
--- a/source/static/js/ui/invoices.js
+++ b/source/static/js/ui/invoices.js
@@ -52,22 +52,22 @@ var grid_invoices = {
var grid_details_cols = [
{id: "id", header:"ID", hidden: true},
{id: 'delete', header: '', width: 30, css: 'delete'},
- {id: "key", header:{text: 'Clave', css: 'center'}, width: 100},
- {id: "description", header:{text: 'Descripción', css: 'center'},
+ {id: "clave", header:{text: 'Clave', css: 'center'}, width: 100},
+ {id: "descripcion", header:{text: 'Descripción', css: 'center'},
fillspace: true},
- {id: "unit", header:{text: 'Unidad', css: 'center'}, width: 100},
- {id: 'cant', header: {text: 'Cantidad', css: 'center'}, width: 100,
+ {id: "unidad", header:{text: 'Unidad', css: 'center'}, width: 100},
+ {id: 'cantidad', header: {text: 'Cantidad', css: 'center'}, width: 100,
format: webix.i18n.numberFormat, css:'right', editor: 'text'},
- {id: "price", header:{text: 'Valor Unitario', css: 'center'}, width: 100,
+ {id: "valor_unitario", header:{text: 'Valor Unitario', css: 'center'}, width: 100,
format: webix.i18n.priceFormat, css:'right', editor: 'text'},
{id: "importe", header:{text: 'Importe', css: 'center'}, width: 150, format: webix.i18n.priceFormat, css:'right'},
]
var grid_details = {
- view: "datatable",
- id: "grid_details",
- select: "row",
+ view: 'datatable',
+ id: 'grid_details',
+ select: 'row',
adjust: true,
autoheight: true,
columns: grid_details_cols,
@@ -100,8 +100,8 @@ var grid_totals = {
var suggest_partners = {
view: 'gridsuggest',
- id: 'grid_partners_found',
- name: 'grid_partners_found',
+ id: 'grid_clients_found',
+ name: 'grid_clients_found',
body: {
autoConfig: false,
header: false,
@@ -111,7 +111,7 @@ var suggest_partners = {
{id: 'rfc', adjust: 'data'}],
dataFeed:function(text){
if (text.length > 2){
- this.load('/values/partner?name=' + text)
+ this.load('/values/client?name=' + text)
}else{
this.hide()
}
@@ -215,21 +215,32 @@ var controls_generate = [
{cols: [ {rows:[
{view: 'fieldset', label: 'Buscar Cliente', body: {rows: [
{cols: [
- {view:"search", id:"search_partner_id", name:"search_partner_id",
+ {view:"search", id:"search_client_id", name:"search_client_id",
label:"por Clave", labelPosition:'top', maxWidth:200,
placeholder:'Captura la clave'},
- {view: 'search', id: 'search_partner_name',
- name: 'search_partner_name', label: 'por Nombre o RFC',
+ {view: 'search', id: 'search_client_name',
+ name: 'search_client_name', label: 'por Nombre o RFC',
labelPosition: 'top', suggest: suggest_partners,
placeholder: 'Captura al menos tres letras'},
]},
- {cols: [{view: "label", id: "lbl_partner_title", name: "lbl_partner_title", label: 'Seleccionado: ', autowidth:true},
- {view: "label", id: "lbl_partner", name: "lbl_partner", label: 'Ninguno'},
+ {cols: [{
+ view: 'label', id: 'lbl_client_title',
+ name: "lbl_client_title", label: 'Seleccionado: ',
+ autowidth:true},
+ {view: 'label', id: 'lbl_client', name: 'lbl_client',
+ label: 'Ninguno'},
]}
]}},
{view: 'fieldset', label: 'Buscar Producto', body: {rows: [
- {cols: [{view:"search", id:"search_product_id", name:"search_product_id", label:"por Clave", labelPosition:'top', maxWidth:200, placeholder:'Captura la clave'},
- {view:"search", id:"search_product_name", name:"search_product_name", label:"por Descripción", labelPosition:'top', suggest:suggest_products, placeholder:'Captura al menos tres letras'},
+ {cols: [
+ {view: "search", id: "search_product_id",
+ name: "search_product_id", label: "por Clave",
+ labelPosition:'top', maxWidth:200,
+ placeholder:'Captura la clave'},
+ {view: "search", id: "search_product_name",
+ name: "search_product_name", label: "por Descripción",
+ labelPosition:'top', suggest: suggest_products,
+ placeholder:'Captura al menos tres letras'},
]},
]}}
]},