From 234566b6af8e0d17408280d1662b300b64b75815 Mon Sep 17 00:00:00 2001
From: Juan Ferrer Toribio <juan.ferrer.toribio@gmail.com>
Date: Wed, 19 Apr 2017 08:16:37 +0200
Subject: [PATCH] Backup

---
 forms/cms/home/home.js              |   2 +
 forms/ecomerce/catalog/catalog.js   | 192 ++++++++++++++----------
 forms/ecomerce/catalog/style.css    |   5 -
 forms/ecomerce/catalog/ui.xml       | 221 +++++++++++++---------------
 forms/ecomerce/checkout/checkout.js |  10 +-
 forms/ecomerce/confirm/ui.xml       |   2 +-
 js/db/connection.js                 |   7 +
 js/db/form.js                       |   4 +-
 js/db/model.js                      |  82 ++++++-----
 js/db/query.js                      |   2 +-
 js/hedera/app.js                    |  30 +++-
 js/hedera/form.js                   |  32 ++--
 js/hedera/gui.js                    |  81 ++++++----
 js/hedera/login.js                  |  39 +++--
 js/hedera/report.html               |  18 ---
 js/htk/assistant.js                 |   2 +-
 js/htk/column.js                    |   2 +-
 js/htk/column/button.js             |   2 +-
 js/htk/dialog.js                    |   4 +-
 js/htk/field.js                     | 146 +++++++++---------
 js/htk/field/button.js              |   2 +-
 js/htk/field/combo.js               |   8 +-
 js/htk/field/radio.js               |   2 +-
 js/htk/field/table.js               |   2 +-
 js/htk/image-editor.js              |   4 +-
 js/htk/popup.js                     |   2 +-
 js/htk/repeater.js                  |   2 +-
 js/sql/field.js                     |   8 +-
 js/sql/filter-item.js               |  69 ++++++++-
 js/sql/filter.js                    |  56 ++++---
 js/sql/list.js                      |   2 +-
 js/sql/object.js                    |   1 +
 js/sql/operation.js                 |  10 +-
 js/sql/search-tags.js               |   2 +-
 js/sql/string.js                    |   8 +-
 js/sql/value.js                     |  98 ++++++------
 js/vn/builder.js                    |   5 +-
 js/vn/hash.js                       |  11 +-
 js/vn/json-connection.js            |  10 +-
 js/vn/json-model.js                 |  90 +++++++++++
 js/vn/lot-iface.js                  |  11 +-
 js/vn/lot-query.js                  | 125 ++++++++++++++++
 js/vn/lot.js                        |   6 +-
 js/vn/model-iface.js                | 157 ++++++++++++++++++++
 js/vn/model-proxy.js                |  86 +++++++++++
 js/vn/object.js                     |  74 ++++++----
 js/vn/param-iface.js                | 140 ++++++++++++++++++
 js/vn/param.js                      | 156 ++++++--------------
 js/vn/value.js                      |  68 +++++++--
 js/vn/vn.js                         |   5 +
 rest/edi/sql/supplier.sql           |   1 +
 51 files changed, 1435 insertions(+), 669 deletions(-)
 delete mode 100755 js/hedera/report.html
 create mode 100644 js/vn/json-model.js
 create mode 100644 js/vn/lot-query.js
 create mode 100644 js/vn/model-iface.js
 create mode 100644 js/vn/model-proxy.js
 create mode 100644 js/vn/param-iface.js

diff --git a/forms/cms/home/home.js b/forms/cms/home/home.js
index 4a672b3d..1337ae3a 100644
--- a/forms/cms/home/home.js
+++ b/forms/cms/home/home.js
@@ -1,3 +1,4 @@
+(function () {
 
 Hedera.Home = new Class
 ({
@@ -9,3 +10,4 @@ Hedera.Home = new Class
 	}
 });
 
+})();
diff --git a/forms/ecomerce/catalog/catalog.js b/forms/ecomerce/catalog/catalog.js
index 432a9203..d8894d72 100644
--- a/forms/ecomerce/catalog/catalog.js
+++ b/forms/ecomerce/catalog/catalog.js
@@ -1,3 +1,9 @@
+(function () {
+	
+var View = {
+	LIST: 0,
+	GRID: 1
+};
 
 Hedera.Catalog = new Class
 ({
@@ -32,12 +38,14 @@ Hedera.Catalog = new Class
 	{
 		document.body.appendChild (this.$('right-panel'));
 	
-		this.$('items-model').setInfo ('a', 'Articles', 'vn2008', ['item_id']);
+		this.$('items').setInfo ('a', 'Articles', 'vn2008', ['item_id']);
 		
 		if (localStorage.getItem ('hederaView'))
 			this.setView (parseInt (localStorage.getItem ('hederaView')));
 		else
-			this.setView (Hedera.Catalog.View.GRID);
+			this.setView (View.GRID);
+
+		this.hash.on ('change', this.onHashChange, this);
 	}
 	
 	,deactivate: function ()
@@ -45,17 +53,18 @@ Hedera.Catalog = new Class
 		this.hideMenu ();
 		this.gui.$('top-bar').style.backgroundColor = '';
 		Vn.Node.remove (this.$('right-panel'));
+		this.hash.disconnect ('change', this.onHashChange, this);
 	}
 	
 	,setView: function (view)
 	{
-		if (view === Hedera.Catalog.View.GRID)
+		if (view === View.GRID)
 		{
 			this.$('view-button').setProperties ({
 				icon: 'view-list',
 				tip: _('List view')
 			});
-			this.view = Hedera.Catalog.View.GRID;
+			this.view = View.GRID;
 			var className = 'grid-view';
 		}
 		else
@@ -64,7 +73,7 @@ Hedera.Catalog = new Class
 				icon: 'view-grid',
 				tip: _('Grid view')
 			});
-			this.view = Hedera.Catalog.View.LIST;
+			this.view = View.LIST;
 			var className = 'list-view';
 		}
 		
@@ -75,8 +84,8 @@ Hedera.Catalog = new Class
 	
 	,onSwitchViewClick: function ()
 	{
-		this.setView (this.view === Hedera.Catalog.View.LIST ?
-			Hedera.Catalog.View.GRID : Hedera.Catalog.View.LIST);
+		this.setView (this.view === View.LIST ?
+			View.GRID : View.LIST);
 	}
 	
 	,onBasketReady: function (form)
@@ -103,23 +112,44 @@ Hedera.Catalog = new Class
 			Db.Model.SortWay.ASC : Db.Model.SortWay.DESC;
 		
 		if (sortField)
-			this.$('items-model').sortByName (sortField, sortWay);
+			this.$('items').sortByName (sortField, sortWay);
 		
 		this.hideMenu ();
 	}
 	
-	,onFilterChange: function (param, newValue)
+	,shouldRefresh: function ()
 	{
-		if (newValue)
-			this.hideMenu ();
+		var params = this.hash.params;
+
+		if (params.search)
+			return true;
+
+		var refresh = params.realm && (
+			params.type ||
+			params.color ||
+			params.origin ||
+			params.category ||
+			params.producer
+		);
+
+		return refresh;
+	}
+
+	,onHashChange: function ()
+	{
+		if (!this.shouldRefresh ())
+			return;
+	
+		this.$('items').refresh ();
+		this.hideMenu ();
 	}
 	
 	,realmRenderer: function (builder, form)
 	{
 		var link = builder.$('link');
 		link.href = this.hash.make ({
-			'form': this.hash.get ('form'),
-			'realm': form.get ('id')
+			form: this.hash.get ('form'),
+			realm: form.get ('id')
 		});
 		
 		var img = builder.$('image');
@@ -147,36 +177,25 @@ Hedera.Catalog = new Class
 
 	,onTypeChange: function (param, newValue)
 	{
-		this.onFilterChange (param, newValue);
 		this.refreshTitle ();
 		this.refreshFilter (undefined, newValue);
 	}
 	
 	,refreshFilter: function (realm, type)
 	{
-		var batch = this.$('filter-batch');
-		batch.block ();
-		this.$('realm-value').value = realm;
-		this.$('type-value').value = type;
-		this.$('search').value = undefined;
-		this.$('color').value = undefined;
-		this.$('origin').value = undefined;
-		this.$('category').value = undefined;
-		this.$('producer').value = undefined;
-		batch.unblock ();
-		batch.changed ();
+		this.hash.params = {
+			form: this.hash.get ('form'),
+			realm: realm,
+			type: type
+		};
 	}
 	
 	,refreshTitleColor: function ()
 	{
-		var realms = this.$('realms-model');
-
-		if (!realms.ready)
-			return;
-
+		var realms = this.$('realms');
+		var realm = this.hash.get ('realm');
 		var color = null;
-		var realm = this.$('realm').value;
-		
+
 		if (realm)
 		{
 			var row = realms.search ('id', realm);
@@ -190,14 +209,10 @@ Hedera.Catalog = new Class
 	
 	,refreshTitle: function ()
 	{
-		var types = this.$('types-model');
-		
-		if (!types.ready)
-			return;
-		
+		var types = this.$('types');
+		var type = this.hash.get ('type');		
 		var title = _('Catalog');
-		var type = this.$('type').value;
-		
+
 		if (type)
 		{
 			var row = types.search ('tipo_id', type);
@@ -251,7 +266,7 @@ Hedera.Catalog = new Class
 		if (this.isGuest ())
 			return;
 
-		this.hash.setAll ({'form': 'ecomerce/basket'});
+		this.hash.params = {form: 'ecomerce/basket'};
 	}
 	
 	,onConfigureClick: function ()
@@ -259,7 +274,7 @@ Hedera.Catalog = new Class
 		if (this.isGuest ())
 			return;
 
-		this.hash.setAll ({'form': 'ecomerce/checkout'});
+		this.hash.params = {form: 'ecomerce/checkout'};
 	}
 
 	,onAddItemClick: function (button, form)
@@ -273,26 +288,22 @@ Hedera.Catalog = new Class
 		this.$('card-popup').show (button.node);
 	}
 	
-	,onAddLotClick: function (column, value, row)
+	,onAddLotClick: function (column, value, rowIndex)
 	{
-		var model = this.$('item-lots');
-		var grouping = model.get (row, 'grouping');
-		var warehouse = model.get (row, 'warehouse_id');
-		var available = model.get (row, 'available');
-
-		var lotAmount = this.items[warehouse];
+		var row = this.$('item-lots').getObject (rowIndex);
+		var lotAmount = this.items[row.warehouse];
 
 		if (lotAmount === undefined)
 			lotAmount = 0;
 		
-		if (lotAmount < available)
+		if (lotAmount < row.available)
 		{
-			var newAmount = lotAmount + grouping;
+			var newAmount = lotAmount + row.grouping;
 		
-			if (newAmount > available)
-				newAmount = available;
+			if (newAmount > row.available)
+				newAmount = row.available;
 		
-			this.items[warehouse] = newAmount;
+			this.items[row.warehouse] = newAmount;
 			this.$('amount').value += newAmount - lotAmount;
 		}
 		else
@@ -302,21 +313,22 @@ Hedera.Catalog = new Class
 	,onConfirmClick: function ()
 	{
 		var sql = '';
-		var batch = new Sql.Batch ();
-		var query = new Sql.String ({query: 'CALL basket_item_add (#warehouse, #item, #amount);'});
+		var query = 'CALL basket_item_add (#warehouse, #item, #amount);';
 		var amountSum = 0;
 
 		for (var warehouse in this.items)
 		{
 			var amount = this.items[warehouse];
 			amountSum += amount;
-		
-			batch.addValue ('warehouse', warehouse);
-			batch.addValue ('item', this.$('card-item').value);
-			batch.addValue ('amount', amount);
-			sql += query.render (batch);
+			
+			var params = {
+				item: this.$('card-item').value,
+				warehouse: warehouse,
+				amount: amount
+			};
+			sql += this.conn.render (query, params);
 		}
-	
+
 		if (amountSum > 0)
 		{
 			this.conn.execQuery (sql);
@@ -348,19 +360,10 @@ Hedera.Catalog = new Class
 	}
 });
 
-Hedera.Catalog.extend
-({
-	View: {
-		LIST: 0,
-		GRID: 1
-	}
-});
-
 Vn.Filter = new Class
 ({
 	Extends: Htk.Field
 	,Tag: 'vn-filter'
-	,Child: 'model'
 	,Properties:
 	{
 		model:
@@ -368,9 +371,9 @@ Vn.Filter = new Class
 			type: Db.Model
 			,set: function (x)
 			{
-				x.batch = this._batch;
 				this._model = x;
 				this._select.model = x;
+				this._refreshLot ();
 			}
 			,get: function ()
 			{
@@ -396,13 +399,40 @@ Vn.Filter = new Class
 			,set: function (x)
 			{
 				this._filter = x;
-				this._batch.addObject ('filter', x);
+				this._modelLot.assign ({filter: x});
+				this._refreshLot ();
 			}
 			,get: function ()
 			{
 				return this._filter;
 			}
 		},
+		lot:
+		{
+			type: Vn.LotIface
+			,set: function (x)
+			{
+				this._modelLot.source = x;
+				this._setLot (x);
+			}
+			,get: function ()
+			{
+				return this._lot;
+			}
+		},
+		name:
+		{
+			type: String
+			,set: function (x)
+			{
+				this._modelLot.fields = [x];
+				this._onLotChange ();
+			}
+			,get: function ()
+			{
+				return this._name;
+			}
+		}
 	}
 	
 	,_valueColumnIndex: 0
@@ -422,9 +452,22 @@ Vn.Filter = new Class
 		this._ul = this.createElement ('ul');
 		this.node.appendChild (this._ul);
 		
-		this._batch = new Sql.Batch ();
+		this._modelLot = new Vn.LotQuery ({
+			type: Vn.LotQuery.Type.EXCLUDE
+		});
 		this.parent (props);
 	}
+
+	,_refreshLot: function ()
+	{
+		if (!this._model)
+			return;
+
+		if (this._filter)
+			this._model.lot = this._modelLot;
+		else
+			this._model.lot = null;
+	}
 	
 	,_onMouseDown: function ()
 	{
@@ -465,9 +508,7 @@ Vn.Filter = new Class
 	
 	,_changeValue: function (newValue)
 	{
-		this._batch.block ();
 		this.value = newValue;
-		this._batch.unblock ();
 	}
 	
 	,_onTimeout: function ()
@@ -535,3 +576,4 @@ Vn.Filter = new Class
 	}
 });
 
+})();
diff --git a/forms/ecomerce/catalog/style.css b/forms/ecomerce/catalog/style.css
index 4a0e7a65..b44fc3bf 100644
--- a/forms/ecomerce/catalog/style.css
+++ b/forms/ecomerce/catalog/style.css
@@ -60,9 +60,6 @@
 .right-panel .realm-msg
 {
 	margin-top: 1em;
-/*	box-shadow: 0 0 .3em rgba(1, 1, 1, .5);
-	border-radius: 50%;
-	overflow: hidden;*/
 }
 .right-panel .realm-msg > h1
 {
@@ -70,8 +67,6 @@
 	text-align: center;
 	padding: 2.5em 0;
 	color: #777;
-/*	background-color: #009688;
-	color: white;*/
 }
 .right-panel h2
 {
diff --git a/forms/ecomerce/catalog/ui.xml b/forms/ecomerce/catalog/ui.xml
index 0b2a7cf0..b783c1ac 100755
--- a/forms/ecomerce/catalog/ui.xml
+++ b/forms/ecomerce/catalog/ui.xml
@@ -1,45 +1,62 @@
 <vn>
 <vn-group>
 	<vn-lot id="card-lot"/>
-	<vn-param id="realm" on-changed="onRealmChange"/>
-	<vn-param id="type" on-changed="onTypeChange"/>
-	<vn-param id="search" on-changed="onFilterChange"/>
-	<vn-param id="color" on-changed="onFilterChange"/>
-	<vn-param id="origin" on-changed="onFilterChange"/>
-	<vn-param id="category" on-changed="onFilterChange"/>
-	<vn-param id="producer" on-changed="onFilterChange"/>
-	<vn-hash-param key="realm" param="realm"/>
-	<vn-hash-param key="type" param="type"/>
+	<vn-param lot="hash" name="realm" on-changed="onRealmChange"/>
+	<vn-param lot="hash" name="type" on-changed="onTypeChange"/>
 	<sql-filter type="AND" id="filter">
-		<sql-filter-item type="EQUAL" primary="false" id="op-realm">
-			<sql-field name="reino_id" target="t"/>
-			<sql-value id="realm-value"/>
-		</sql-filter-item>
-		<sql-filter-item type="EQUAL" id="op-type">
-			<sql-field name="tipo_id" target="a"/>
-			<sql-value id="type-value"/>
-		</sql-filter-item>
-		<sql-filter-item type="LIKE" id="op-name">
-			<sql-field name="Article"/>
-			<sql-search-tags param="search"/>
-		</sql-filter-item>
-		<sql-filter-item type="EQUAL" id="op-color">
-			<sql-field name="Color"/>
-			<sql-value param="color"/>
-		</sql-filter-item>
-		<sql-filter-item type="EQUAL" id="op-origin">
-			<sql-field name="id_origen"/>
-			<sql-value param="origin"/>
-		</sql-filter-item>
-		<sql-filter-item type="EQUAL" id="op-category">
-			<sql-field name="Categoria"/>
-			<sql-value param="category"/>
-		</sql-filter-item>
-		<sql-filter-item type="EQUAL" id="op-producer">
-			<sql-field name="producer_id"/>
-			<sql-value param="producer"/>
-		</sql-filter-item>
+		<sql-filter-item type="EQUAL"
+			field="reino_id" target="t"
+			param="realm"/>
+		<sql-filter-item type="EQUAL"
+			field="tipo_id" target="a"
+			param="type"/>
+		<sql-filter type="OR">
+			<sql-filter-item type="LIKE"
+				field="Article" target="a"
+				param="search"/>
+			<sql-filter-item type="EQUAL"
+				field="Id_Article" target="a"
+				param="search"/>
+		</sql-filter>
+		<sql-filter-item type="EQUAL"
+			field="Color" target="a"
+			param="color"/>
+		<sql-filter-item type="EQUAL"
+			field="id_origen" target="a"
+			param="origin"/>
+		<sql-filter-item type="EQUAL"
+			field="Categoria" target="a"
+			param="category"/>
+		<sql-filter-item type="EQUAL"
+			field="producer_id" target="a"
+			param="producer"/>
 	</sql-filter>
+	<db-model
+		id="items"
+		result-index="2"
+		lot="hash"
+		auto-load="false"
+		on-status-changed="onItemsChange">
+		CREATE TEMPORARY TABLE tmp.bionic_calc
+			(INDEX (item_id))
+			ENGINE = MEMORY
+			SELECT a.Id_Article item_id
+				FROM vn2008.Articles a
+					JOIN vn2008.Tipos t ON t.tipo_id = a.tipo_id
+				WHERE #filter;
+		CALL bionic_calc ();
+		SELECT a.Id_Article item_id, a.description, b.available, b.price,
+				b.producer, a.Foto, a.Article, a.Categoria, a.Medida,
+				IF(a.Tallos > 1, a.Tallos, NULL) Tallos, c.str color
+			FROM tmp.bionic_item b
+				JOIN vn2008.Articles a ON a.Id_Article = b.item_id
+				LEFT JOIN vn2008.producer p ON p.producer_id = a.producer_id
+				LEFT JOIN vn_locale.color_view c ON c.color_id = a.Color
+				LEFT JOIN vn_locale.origin_view o ON o.origin_id = a.id_origen
+			WHERE b.available > 0
+			ORDER BY a.Article, a.Medida
+			LIMIT 40;
+	</db-model>
 	<db-form id="basket" on-ready="onBasketReady">
 		<db-model property="model">
 			SELECT o.id, o.date_send, ag.description agency, v.code method
@@ -53,35 +70,7 @@
 			FROM basket_item
 			GROUP BY warehouse_id
 	</db-query>
-	<db-model
-		id="items-model"
-		result-index="2"
-		on-status-changed="onItemsChange">
-		CREATE TEMPORARY TABLE tmp.bionic_calc
-			(INDEX (item_id))
-			ENGINE=MEMORY
-			SELECT a.Id_Article item_id FROM vn2008.Articles a
-				JOIN vn2008.Tipos t ON t.tipo_id = a.tipo_id
-				WHERE #filter;
-			CALL bionic_calc ();
-			SELECT a.Id_Article item_id, a.description, b.available, b.price,
-			b.producer, a.Foto, a.Article, a.Categoria, a.Medida,
-			IF(a.Tallos > 1, a.Tallos, NULL) Tallos, c.str color
-				FROM tmp.bionic_item b
-					JOIN vn2008.Articles a ON a.Id_Article = b.item_id
-		    		LEFT JOIN vn2008.producer p ON p.producer_id = a.producer_id
-					LEFT JOIN vn_locale.color_view c ON c.color_id = a.Color
-					LEFT JOIN vn_locale.origin_view o ON o.origin_id = a.id_origen
-				WHERE b.available > 0
-				ORDER BY a.Article, a.Medida
-			LIMIT 400;
-		<sql-batch property="batch" id="filter-batch">
-			<custom>
-				<item name="filter" object="filter"/>
-			</custom>
-		</sql-batch>
-	</db-model>
-	<db-form id="card" model="items-model"/>
+	<db-form id="card" model="items"/>
 	<db-form id="card-extend">
 		<db-model
 			property="model"
@@ -96,8 +85,8 @@
 	<db-model
 		id="item-lots"
 		result-index="1"
-		on-status-changed-after="onStatusChange"
-		batch="card-batch">
+		lot="card-lot"
+		on-status-changed-after="onStatusChange">
 		CALL bionic_from_item (#item);
 		SELECT p.warehouse_id, w.name warehouse, p.grouping, p.price, p.rate, l.available
 			FROM tmp.bionic_lot l
@@ -122,15 +111,15 @@
 		tip="_Switch view"
 		on-click="onSwitchViewClick"/>
 	<htk-search-entry
-		param="search"/>
+		lot="hash"
+		name="search"/>
 </div>
 <div id="main" class="catalog">
-	<div id="main" class="main">
 	<htk-repeater
 		id="grid-view"
 		empty-message="_Choose filter from right menu"
 		form-id="item"
-		model="items-model">
+		model="items">
 		<custom>
 		<div class="card item-box">
 			<htk-image
@@ -174,7 +163,6 @@
 		</div>
 		</custom>
 	</htk-repeater>
-	</div>
 </div>
 <div id="right-panel" class="right-panel" on-click="onRightPanelClick">
 	<div class="basket-info">
@@ -193,12 +181,10 @@
 		<div class="categories">
 			<div class="realms">
 				<htk-repeater
-					model="realms-model"
-					form-id="realm-form"
 					renderer="realmRenderer"
 					class="realms-box">
 					<db-model
-						id="realms-model"
+						id="realms"
 						property="model"
 						on-status-changed="refreshTitleColor">
 						SELECT r.id, l.str name, r.color
@@ -222,12 +208,14 @@
 		<div id="filters" class="filters">
 			<h2><t>Filter by</t></h2>
 			<vn-filter
-				placeholder="_Family"
-				param="type">
+				lot="hash"
+				name="type"
+				placeholder="_Family">
 				<db-model
-					id="types-model"
+					id="types"
 					property="model"
 					conn="conn"
+					lot="hash"
 					result-index="1"
 					on-status-changed="refreshTitle">
 					CALL item_available ();
@@ -236,19 +224,16 @@
 							JOIN vn2008.Articles a ON a.tipo_id = t.tipo_id
 							LEFT JOIN vn_locale.family_view l ON l.family_id = t.tipo_id
 							JOIN tmp.item_available i ON i.item_id = a.Id_Article
-						WHERE #filter
+						WHERE t.reino_id = #realm
 						ORDER BY name
 				</db-model>
-				<sql-filter property="filter" type="AND">
-					<sql-filter-item type="EQUAL">
-						<sql-field name="reino_id" target="t"/>
-						<sql-value param="realm"/>
-					</sql-filter-item>
-				</sql-filter>
 			</vn-filter>
 			<vn-filter
+				id="test"
+				lot="hash"
+				name="color"
 				placeholder="_Color"
-				param="color">
+				filter="filter">
 				<db-model property="model" auto-load="false" result-index="1">
 					CALL item_available ();
 					SELECT DISTINCT c.Id_Tinta, l.str name
@@ -260,18 +245,12 @@
 						WHERE #filter
 						ORDER BY name
 				</db-model>
-				<sql-filter property="filter" always-ready="true" type="AND">
-					<pointer object="op-realm"/>
-					<pointer object="op-type"/>
-					<pointer object="op-name="/>
-					<pointer object="op-origin"/>
-					<pointer object="op-category"/>
-					<pointer object="op-producer"/>
-				</sql-filter>
 			</vn-filter>
 			<vn-filter
+				lot="hash"
+				name="producer"
 				placeholder="_Producer"
-				param="producer">
+				filter="filter">
 				<db-model property="model" auto-load="false" result-index="1">
 					CALL item_available ();
 					SELECT DISTINCT p.producer_id, p.name
@@ -282,18 +261,12 @@
 						WHERE #filter
 						ORDER BY name
 				</db-model>
-				<sql-filter property="filter" always-ready="true" type="AND">
-					<pointer object="op-realm"/>
-					<pointer object="op-type"/>
-					<pointer object="op-name="/>
-					<pointer object="op-origin"/>
-					<pointer object="op-color"/>
-					<pointer object="op-category"/>
-				</sql-filter>
 			</vn-filter>
 			<vn-filter
+				lot="hash"
+				name="origin"
 				placeholder="_Origin"
-				param="origin">
+				filter="filter">
 				<db-model property="model" auto-load="false" result-index="1">
 					CALL item_available ();
 					SELECT DISTINCT o.id, l.str name, o.Abreviatura
@@ -305,18 +278,12 @@
 						WHERE #filter
 						ORDER BY name
 				</db-model>
-				<sql-filter property="filter" always-ready="true" type="AND">
-					<pointer object="op-realm"/>
-					<pointer object="op-type"/>
-					<pointer object="op-name="/>
-					<pointer object="op-color"/>
-					<pointer object="op-category"/>
-					<pointer object="op-producer"/>
-				</sql-filter>
 			</vn-filter>
 			<vn-filter
+				lot="hash"
+				name="category"
 				placeholder="_Category"
-				param="category">
+				filter="filter">
 				<db-model property="model" auto-load="false" result-index="1">
 					CALL item_available ();
 					SELECT DISTINCT a.Categoria, a.Categoria category
@@ -326,14 +293,6 @@
 						WHERE #filter
 						ORDER BY a.Categoria
 				</db-model>
-				<sql-filter property="filter" always-ready="true" type="AND">
-					<pointer object="op-realm"/>
-					<pointer object="op-type"/>
-					<pointer object="op-name="/>
-					<pointer object="op-color"/>
-					<pointer object="op-origin"/>
-					<pointer object="op-producer"/>
-				</sql-filter>
 			</vn-filter>
 		</div>
 		<div id="order" class="order">
@@ -444,4 +403,22 @@
 		</div>
 	</div>
 </htk-popup>
-</vn>
+<!--
+<htk-combo on-change="onOrderChange">
+	<vn-json-model property="model">
+	<array>
+		<object way="A" field="Article" desc="_Name"/>
+		<object way="A" field="price" desc="_Lower price"/>
+		<object way="D" field="price" desc="_Higher price"/>
+		<object way="A" field="available" desc="_Available"/>
+		<object way="A" field="Medida" desc="_Lower size"/>
+		<object way="D" field="Medida" desc="_Higher size"/>
+		<object way="A" field="color" desc="_Color"/>
+		<object way="A" field="producer" desc="_Producer"/>
+		<object way="A" field="Abreviatura" desc="_Origin"/>
+		<object way="A" field="Categoria" desc="_Category"/>
+	</array>
+	</vn-json-model>
+</htk-combo>
+-->
+</vn>
\ No newline at end of file
diff --git a/forms/ecomerce/checkout/checkout.js b/forms/ecomerce/checkout/checkout.js
index f87cea6b..ecd26f11 100644
--- a/forms/ecomerce/checkout/checkout.js
+++ b/forms/ecomerce/checkout/checkout.js
@@ -3,12 +3,16 @@ Hedera.Checkout = new Class
 ({
 	Extends: Hedera.Form
 
+	,initialize: function (props)
+	{
+		this.today = new Date ();
+		this.today.setHours (0, 0, 0, 0);
+		this.parent (props);
+	}
+
 	,activate: function ()
 	{
 		this.autoStepLocked = true;
-
-		this.today = new Date ();
-		this.today.setHours (0,0,0,0);
 	}
 
 	,onValuesReady: function ()
diff --git a/forms/ecomerce/confirm/ui.xml b/forms/ecomerce/confirm/ui.xml
index f1623a41..8229e343 100755
--- a/forms/ecomerce/confirm/ui.xml
+++ b/forms/ecomerce/confirm/ui.xml
@@ -184,7 +184,7 @@
 									</p>
 									<p>
 										<htk-text lot="iter" name="iban"/> 
-										<htk-text lot="iter" name="entity_id"/> 
+										<htk-text lot="iter" name="entity_id" format="%.4d"/> 
 										<htk-text lot="iter" name="office"/> 
 										<htk-text lot="iter" name="dc"/> 
 										<htk-text lot="iter" name="number"/>
diff --git a/js/db/connection.js b/js/db/connection.js
index 0e61da41..141ed7ff 100644
--- a/js/db/connection.js
+++ b/js/db/connection.js
@@ -75,6 +75,13 @@ Connection.implement
 		this.execSql (this.renderQuery (query, params), callback);
 	}
 
+	/**
+	 * Renders a query.
+	 *
+	 * @param {String} query The SQL statement
+	 * @param {Object} params The statement parameters
+	 * @return {String} The rendered statement
+	 */
 	,renderQuery: function (query, params)
 	{
 		return new Sql.String ({query: query}).render (params);
diff --git a/js/db/form.js b/js/db/form.js
index 6947bf96..b3b0968d 100644
--- a/js/db/form.js
+++ b/js/db/form.js
@@ -106,13 +106,13 @@ module.exports = new Class
 				this.lastRow = this._row;
 	
 			this._ready = ready;
-			this.signalEmit ('status-changed');
+			this.emit ('status-changed');
 			
 			if (this._row == -1)
 				this.row = this.lastRow;
 				
 			if (ready)
-				this.signalEmit ('ready');
+				this.emit ('ready');
 
 			this.changed ();
 		}
diff --git a/js/db/model.js b/js/db/model.js
index 4d4acdcd..779d0391 100644
--- a/js/db/model.js
+++ b/js/db/model.js
@@ -10,8 +10,8 @@ var Connection = require ('./connection');
  * otherwise updates are not allowed on that table/column. If two tables or
  * columns have the same name, an alias should be used to make it updatable.
  */
-var Model = new Class ();
-module.exports = Model;
+var Klass = new Class ();
+module.exports = Klass;
 
 var Status =
 {
@@ -40,7 +40,7 @@ var SortWay =
 	,DESC  : 2 
 };
 
-Model.extend
+Klass.extend
 ({
 	 Status: Status
 	,Mode: Mode
@@ -48,7 +48,7 @@ Model.extend
 	,SortWay: SortWay
 });
 
-Model.implement
+Klass.implement
 ({
 	Extends: Vn.Object
 	,Tag: 'db-model'
@@ -85,22 +85,6 @@ Model.implement
 				return this._resultIndex;
 			}
 		},
-		/**
-		 * The batch used to execute the statement.
-		 */
-		batch:
-		{
-			type: Sql.Batch
-			,set: function (x)
-			{
-				this.link ({_batch: x}, {'changed': this._autoLoad});
-				this._autoLoad ();
-			}
-			,get: function ()
-			{
-				return this._batch;
-			}
-		},
 		/**
 		 * The lot used to execute the statement.
 		 */
@@ -241,10 +225,10 @@ Model.implement
 
 	,_conn: null
 	,_resultIndex: 0
-	,_batch: null
 	,_lot: null
 	,_stmt: null
 	,_status: Status.CLEAN
+	,result: null
 	,data: null
 	,tables: null
 	,columns: null
@@ -289,6 +273,10 @@ Model.implement
 			return null;
 
 		var lotParams = this._lot ? this._lot.params : {};
+		
+		if (lotParams == null)
+			lotParams = {};
+
 		var params = {};
 
 		for (var i = 0; i < ids.length; i++)
@@ -371,6 +359,7 @@ Model.implement
 			throw e;
 		}
 
+		this.result = dataResult;
 		this.data = dataResult.data;
 		this.tables = dataResult.tables;
 		this.columns = dataResult.columns;
@@ -412,6 +401,7 @@ Model.implement
 
 	,_cleanData: function ()
 	{
+		this.result = null;
 		this.data = null;
 		this.tables = null;
 		this.columns = null;
@@ -427,7 +417,7 @@ Model.implement
 		this._updatable = this._mainTable !== null && this._requestedUpdatable;
 		
 		if (oldValue != this._updatable)
-			this.signalEmit ('updatable-changed');
+			this.emit ('updatable-changed');
 	}
 	
 	,_refreshMainTable: function ()
@@ -591,7 +581,7 @@ Model.implement
 	}
 
 	/**
-	 * Gets a value from the model using the column index.
+	 * Gets a value using the column index.
 	 *
 	 * @param {number} rowIndex The row index
 	 * @param {number} column The column index
@@ -605,6 +595,20 @@ Model.implement
 		return undefined;
 	}
 
+	/**
+	 * Gets a row as an object  using the column index.
+	 *
+	 * @param {number} rowIndex The row index
+	 * @return {Object} The row as an object
+	 */
+	,getObject: function (rowIndex)
+	{
+		if (!this.checkRowExists (rowIndex))
+			return null;
+
+		return this.result.getObject (rowIndex);
+	}
+
 	/**
 	 * Updates a value on the model using the column index.
 	 *
@@ -654,9 +658,9 @@ Model.implement
 		&& op.oldValues[col] === undefined)
 			op.oldValues[col] = row[col];
 
-		this.signalEmit ('row-updated-before', rowIndex);
+		this.emit ('row-updated-before', rowIndex);
 		row[col] = value;
-		this.signalEmit ('row-updated', rowIndex, [col]);
+		this.emit ('row-updated', rowIndex, [col]);
 
 		if (this.mode == Mode.ON_CHANGE
 		&& !(op.type & Operation.INSERT))
@@ -679,14 +683,14 @@ Model.implement
 
 		if (!this._requestedMainTable)
 		{
-			this.signalEmit ('row-deleted-before', rowIndex);
+			this.emit ('row-deleted-before', rowIndex);
 			this.data.splice (rowIndex, 1);
-			this.signalEmit ('row-deleted', rowIndex);
+			this.emit ('row-deleted', rowIndex);
 			this._refreshRowIndexes (rowIndex);
 		}
 		else
 		{
-			this.signalEmit ('row-updated-before', rowIndex);
+			this.emit ('row-updated-before', rowIndex);
 
 			if (!op.oldValues)
 				op.oldValues = [];
@@ -703,7 +707,7 @@ Model.implement
 				updatedCols.push (i);
 			}
 
-			this.signalEmit ('row-updated', rowIndex, updatedCols);
+			this.emit ('row-updated', rowIndex, updatedCols);
 		}
 		
 		if (this.mode === Mode.ON_CHANGE)
@@ -735,7 +739,7 @@ Model.implement
 		var op = this._createOperation (rowIndex);
 		op.type |= Operation.INSERT;
 
-		this.signalEmit ('row-inserted', rowIndex);
+		this.emit ('row-inserted', rowIndex);
 	
 		return rowIndex;
 	}
@@ -749,7 +753,7 @@ Model.implement
 	
 		if (ops.length === 0)
 		{
-			this.signalEmit ('operations-done');
+			this.emit ('operations-done');
 			return;
 		}
 		
@@ -912,7 +916,7 @@ Model.implement
 			}
 			else if (op.type & (Operation.INSERT | Operation.UPDATE))
 			{
-				this.signalEmit ('row-updated-before', row.index);
+				this.emit ('row-updated-before', row.index);
 
 				var updatedCols = [];
 				var cols = this.columns;
@@ -946,14 +950,14 @@ Model.implement
 					}
 				}
 
-				this.signalEmit ('row-updated', row.index, updatedCols);
+				this.emit ('row-updated', row.index, updatedCols);
 			}
 		}
 		
 		resultSet.fetchResult ();
 			
 //		if (isOperation)
-			this.signalEmit ('operations-done');
+			this.emit ('operations-done');
 	}
 	
 	/**
@@ -970,11 +974,11 @@ Model.implement
 			&& !(op.type & Operation.INSERT))
 			{
 				this.data.splice (row.index, 0, row);
-				this.signalEmit ('row-inserted', row.index);
+				this.emit ('row-inserted', row.index);
 			}
 			else if (op.type & Operation.UPDATE)
 			{
-				this.signalEmit ('row-updated-before', row.index);
+				this.emit ('row-updated-before', row.index);
 
 				var updatedCols = [];
 				var cols = this.columns;
@@ -986,7 +990,7 @@ Model.implement
 					updatedCols.push (i);
 				}
 
-				this.signalEmit ('row-updated', row.index, updatedCols);
+				this.emit ('row-updated', row.index, updatedCols);
 			}
 		}
 
@@ -1215,8 +1219,8 @@ Model.implement
 	,_setStatus: function (status)
 	{
 		this._status = status;
-		this.signalEmit ('status-changed', status);
-		this.signalEmit ('status-changed-after', status);
+		this.emit ('status-changed', status);
+		this.emit ('status-changed-after', status);
 	}
 	
 	,_createTarget: function (tableIndex)
diff --git a/js/db/query.js b/js/db/query.js
index c8468131..c9c7e7c3 100644
--- a/js/db/query.js
+++ b/js/db/query.js
@@ -95,7 +95,7 @@ module.exports = new Class
 	
 	,onQueryDone: function (resultSet)
 	{
-		this.signalEmit ('ready', resultSet);
+		this.emit ('ready', resultSet);
 	}
 	
 	,onChange: function ()
diff --git a/js/hedera/app.js b/js/hedera/app.js
index 51cea148..92db4264 100644
--- a/js/hedera/app.js
+++ b/js/hedera/app.js
@@ -2,11 +2,17 @@
 var Login = require ('./login');
 var Gui = require ('./gui');
 
+/**
+ * The main application object.
+ */
 module.exports = new Class
 ({
 	Extends: Vn.Object,
 	Properties:
 	{
+		/**
+		 * The main connection object.
+		 */
 		conn:
 		{
 			type: Db.Connection
@@ -15,6 +21,28 @@ module.exports = new Class
 				return this._conn;
 			}
 		}
+		/**
+		 * The main screen component.
+		 */
+		,gui:
+		{
+			type: Gui
+			,get: function ()
+			{
+				return this._gui;
+			}
+		}
+		/**
+		 * The login component.
+		 */
+		,login:
+		{
+			type: Login
+			,get: function ()
+			{
+				return this._login;
+			}
+		}
 	}
 
 	,initialize: function ()
@@ -66,7 +94,7 @@ module.exports = new Class
 		this.unref ();
 	}
 	
-	,_onWindowError: function (message, file, line, col, err)
+	,_onWindowError: function (message, file, line)
 	{
 		var error = new Error (message);
 		error.fileName = file;
diff --git a/js/hedera/form.js b/js/hedera/form.js
index b09a423f..d4098f8b 100644
--- a/js/hedera/form.js
+++ b/js/hedera/form.js
@@ -1,19 +1,33 @@
 
+var Gui = require ('./gui');
+
 module.exports = new Class
 ({
 	Extends: Htk.Component
+	,Properties: {
+		gui:
+		{
+			type: Gui
+			,set: function (x)
+			{
+				this._gui = x;
+				this.conn = x.conn;
+				this.hash = x.hash;
+			}
+			,get: function ()
+			{
+				return this._gui;
+			}
+		},
+		formInfo:
+		{
+			type: Object,
+			value: null
+		}
+	}
 	
 	,isOpen: false
 	,uiLoaded: false
-
-	,initialize: function (gui, formInfo)
-	{
-		this.gui = gui;
-		this.conn = gui.conn;
-		this.hash = gui.hash;
-		this.formInfo = formInfo;
-		this.parent ();
-	}
 	
 	,loadUi: function ()
 	{
diff --git a/js/hedera/gui.js b/js/hedera/gui.js
index b5f7ed3b..2e37b310 100644
--- a/js/hedera/gui.js
+++ b/js/hedera/gui.js
@@ -1,13 +1,22 @@
 
-var Module = require ('./module');
-var Css = require ('./gui.css');
 var Tpl = require ('./gui.xml');
+var Module = require ('./module');
+var Form = require ('./form');
 
+require ('./gui.css');
+
+/**
+ * The main screen component. It has a left menu, a navbar with configurable
+ * action buttons and the body where @Db.Form childs can be displayed.
+ */
 module.exports = new Class
 ({
 	Extends: Htk.Component,
 	Properties:
 	{
+		/**
+		 * The main connection object.
+		 */
 		conn:
 		{
 			type: Db.Connection
@@ -19,15 +28,26 @@ module.exports = new Class
 			{
 				return this._conn;
 			}
+		},
+		/**
+		 * The current open form.
+		 */
+		activeForm:
+		{
+			type: Form
+			,get: function ()
+			{
+				return this._activeForm;
+			}
 		}
 	}
 
-	,forms: {}
-	,activeForm: null
-	,requestedForm: null
-	,menuShown: false
-	,menuOptions: {}
-	,choosedOption: null
+	,_forms: {}
+	,_activeForm: null
+	,_requestedForm: null
+	,_menuShown: false
+	,_menuOptions: {}
+	,_choosedOption: null
 	,_shown: false
 	,_scrollTimeout: null
 	,_navbarVisible: true
@@ -119,7 +139,7 @@ module.exports = new Class
 	
 	,_onConnClose: function ()
 	{
-		this.signalEmit ('logout');
+		this.emit ('logout');
 	}
 
 	,_onConnLoadChange: function (conn, isLoading)
@@ -220,7 +240,7 @@ module.exports = new Class
 			if (row.path)
 			{
 				a.href = this.hash.make ({'form': row.path});
-				this.menuOptions[row.path] = a;
+				this._menuOptions[row.path] = a;
 			}
 
 			a.appendChild (text);
@@ -246,7 +266,7 @@ module.exports = new Class
 
 	,_onLiMouseHover: function (submenu, parent)
 	{
-		if (this.menuShown)
+		if (this._menuShown)
 			return;
 
 		this.hideSubmenu ();
@@ -282,7 +302,7 @@ module.exports = new Class
 	{
 		this.showBackground ();
 		Vn.Node.addClass (this.$('left-panel'), 'show');
-		this.menuShown = true;
+		this._menuShown = true;
 
 		this.hideMenuCallback = this.hideMenu.bind (this);
 		this.doc.addEventListener ('click', this.hideMenuCallback);
@@ -290,12 +310,12 @@ module.exports = new Class
 
 	,hideMenu: function ()
 	{
-		if (!this.menuShown)
+		if (!this._menuShown)
 			return;
 	
 		this.hideBackground ();
 		Vn.Node.removeClass (this.$('left-panel'), 'show');
-		this.menuShown = false;
+		this._menuShown = false;
 		
 		this.doc.removeEventListener ('click', this.hideMenuCallback);
 		this.hideMenuCallback = null;
@@ -387,22 +407,22 @@ module.exports = new Class
 		this.loaderPush ();
 		
 		this.closeForm ();
-		this.requestedForm = formPath;
+		this._requestedForm = formPath;
 
-		var newChoosedOption = this.menuOptions[formPath];
+		var newChoosedOption = this._menuOptions[formPath];
 		
 		if (newChoosedOption)
 		{
 			Vn.Node.addClass (newChoosedOption, 'selected');
-			this.choosedOption = newChoosedOption;
+			this._choosedOption = newChoosedOption;
 		}
 
-		var formInfo = this.forms[formPath];
+		var formInfo = this._forms[formPath];
 
 		if (!formInfo)
 		{
 			formInfo = new Module ('forms', formPath);
-			this.forms[formPath] = formInfo;
+			this._forms[formPath] = formInfo;
 		}
 		
 		formInfo.load (callback);
@@ -418,8 +438,11 @@ module.exports = new Class
 			return;
 		}
 
-		this.activeForm = new formInfo.klass (this, formInfo);
-		this.activeForm.open ();
+		this._activeForm = new formInfo.klass ({
+			gui: this,
+			formInfo: formInfo
+		});
+		this._activeForm.open ();
 	}
 	
 	,setForm: function (form)
@@ -453,18 +476,18 @@ module.exports = new Class
 	
 	,closeForm: function ()
 	{
-		if (this.activeForm)
+		if (this._activeForm)
 		{
-			this.activeForm.formInfo.unload ();
-			this.activeForm.close ();
-			this.activeForm.unref ();
-			this.activeForm = null;
+			this._activeForm.formInfo.unload ();
+			this._activeForm.close ();
+			this._activeForm.unref ();
+			this._activeForm = null;
 		}
 
-		if (this.choosedOption)
+		if (this._choosedOption)
 		{
-			Vn.Node.removeClass (this.choosedOption, 'selected');
-			this.choosedOption = null;
+			Vn.Node.removeClass (this._choosedOption, 'selected');
+			this._choosedOption = null;
 		}
 	}
 
diff --git a/js/hedera/login.js b/js/hedera/login.js
index 78fa4d4d..1a09e699 100644
--- a/js/hedera/login.js
+++ b/js/hedera/login.js
@@ -1,12 +1,19 @@
 
-var Css = require ('./login.css');
 var Tpl = require ('./login.xml');
 
+require ('./login.css');
+
+/**
+ * The login component.
+ */
 module.exports = new Class
 ({
 	Extends: Htk.Component,
 	Properties:
 	{
+		/**
+		 * The main connection object.
+		 */
 		conn:
 		{
 			type: Db.Connection
@@ -35,14 +42,6 @@ module.exports = new Class
 			return false;
 		};
 	}
-	
-	,_onConnLoadChange: function (conn, isLoading)
-	{
-		if (isLoading)
-			this.$('spinner').start ();
-		else
-			this.$('spinner').stop ();
-	}
 
 	,show: function ()
 	{
@@ -56,6 +55,19 @@ module.exports = new Class
 		this._focusUserInput ();
 	}
 	
+	,hide: function ()
+	{
+		Vn.Node.remove (this.node);
+	}
+	
+	,_onConnLoadChange: function (conn, isLoading)
+	{
+		if (isLoading)
+			this.$('spinner').start ();
+		else
+			this.$('spinner').stop ();
+	}
+	
 	,_onSubmit: function ()
 	{
 		this._conn.open (
@@ -79,7 +91,7 @@ module.exports = new Class
 			if (user)
 				localStorage.setItem ('hederaLastUser', user);
 
-			this.signalEmit ('login');
+			this.emit ('login');
 		}
 		else
 		{
@@ -88,11 +100,6 @@ module.exports = new Class
 		}
 	}
 	
-	,hide: function ()
-	{
-		Vn.Node.remove (this.node);
-	}
-	
 	,_focusUserInput: function ()
 	{
 		var userEntry = this.$('user');
@@ -114,7 +121,7 @@ module.exports = new Class
 		if (!user)
 			Htk.Toast.showError (_('Please write your user name'));
 		else
-			this._conn.send ('core/recover-password', {'recoverUser': user},
+			this._conn.send ('core/recover-password', {recoverUser: user},
 				this._onPasswordRecovered.bind (this));
 	}
 	
diff --git a/js/hedera/report.html b/js/hedera/report.html
deleted file mode 100755
index 908c81fd..00000000
--- a/js/hedera/report.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!DOCTYPE html>
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-	<head>
-		<meta charset="UTF-8">
-		<meta name="viewport" content="user-scalable=no"/>
-		<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"/>
-		<meta name="mobile-web-app-capable" content="yes">
-		<link rel="shortcut icon" type="image/x-icon" href="image/favicon.ico"/>
-		<link rel="stylesheet" type="text/css" href="report.css"/>
-		<title>Report</title>
-	</head>
-	<body>
-		<div id="topbar">
-			<button id="print">Print</button>
-		</div>
-	</body>
-</html>
diff --git a/js/htk/assistant.js b/js/htk/assistant.js
index 971f7c7f..08580912 100644
--- a/js/htk/assistant.js
+++ b/js/htk/assistant.js
@@ -93,7 +93,7 @@ module.exports = new Class
 	,_setStepIndex: function (stepIndex)
 	{
 		this._stepIndex = stepIndex;
-		this.signalEmit ('step-change', stepIndex);
+		this.emit ('step-change', stepIndex);
 	}
 	
 	,movePrevious: function ()
diff --git a/js/htk/column.js b/js/htk/column.js
index 62103b26..3c1e4ad5 100644
--- a/js/htk/column.js
+++ b/js/htk/column.js
@@ -106,7 +106,7 @@ module.exports = new Class
 
 	,changed: function (tr, newValue)
 	{
-		this.signalEmit ('changed', tr.rowIndex - 1, newValue);
+		this.emit ('changed', tr.rowIndex - 1, newValue);
 	}
 });
 
diff --git a/js/htk/column/button.js b/js/htk/column/button.js
index daf794fd..95a870ab 100644
--- a/js/htk/column/button.js
+++ b/js/htk/column/button.js
@@ -61,6 +61,6 @@ module.exports = new Class
 	,_onClick: function (value, tr, button, event)
 	{
 		event.preventDefault ();
-		this.signalEmit ('clicked', value, tr.rowIndex - 1, button, event);
+		this.emit ('clicked', value, tr.rowIndex - 1, button, event);
 	}
 });
diff --git a/js/htk/dialog.js b/js/htk/dialog.js
index 38e1b827..4e282b11 100644
--- a/js/htk/dialog.js
+++ b/js/htk/dialog.js
@@ -173,12 +173,12 @@ Dialog.implement
 	,_onButtonClick: function (response)
 	{
 		this.hide ();
-		this.signalEmit ('response', response);
+		this.emit ('response', response);
 	}
 	
 	,_onClosed: function ()
 	{
-		this.signalEmit ('response', null);
+		this.emit ('response', null);
 	}
 });
 
diff --git a/js/htk/field.js b/js/htk/field.js
index 6a4f6bbb..8852f786 100644
--- a/js/htk/field.js
+++ b/js/htk/field.js
@@ -1,44 +1,89 @@
 
 var Widget = require ('./widget');
 
+/**
+ * Base class for graphical fields.
+ */
 module.exports = new Class
 ({
 	Extends: Widget
+	,Implements: Vn.ParamIface
 	,Tag: 'htk-field'
 	,Properties:
 	{
 		value:
 		{
-			type: String
+			type: null
 			,set: function (x)
 			{
-				if (Vn.Value.compare (x, this._value))
-					return;
-
-				if (x instanceof Date)
-					x = x.clone ();
-
-				this.valueChanged (x);
-				this.putValue (x);
+				this._setValue (x);
 			}
 			,get: function ()
 			{
 				return this._value;
 			}
 		},
-		param:
+		type:
 		{
-			type: Vn.Param
+			type: Type
 			,set: function (x)
 			{
-				this.link ({_param: x}, {'changed': this.onParamChange});
-				this.onParamChange ();
+				this._setType (x);
+			}
+			,get: function ()
+			{
+				return this._type;
+			}
+		},
+		param:
+		{
+			type: Vn.ParamIface
+			,set: function (x)
+			{
+				this._setParam (x);
 			}
 			,get: function ()
 			{
 				return this._param;
 			}
 		},
+		lot:
+		{
+			type: Vn.LotIface
+			,set: function (x)
+			{
+				this._setLot (x);
+			}
+			,get: function ()
+			{
+				return this._lot;
+			}
+		},
+		name:
+		{
+			type: String
+			,set: function (x)
+			{
+				this._name = x;
+				this._onLotChange ();
+			}
+			,get: function ()
+			{
+				return this._name;
+			}
+		},
+		oneWay:
+		{
+			type: Boolean
+			,set: function (x)
+			{
+				this._oneWay = x;
+			}
+			,get: function ()
+			{
+				return this._oneWay;
+			}
+		},
 		editable:
 		{
 			type: Boolean
@@ -55,24 +100,6 @@ module.exports = new Class
 				return this._editable;
 			}
 		},
-		lot:
-		{
-			type: Db.Iterator
-			,set: function (x)
-			{
-				this._lot = x;
-				this.bindToLot ();
-			}
-		},
-		name:
-		{
-			type: String
-			,set: function (x)
-			{
-				this._paramName = x;
-				this.bindToLot ();
-			}
-		},
 		conditionalFunc:
 		{
 			type: Function
@@ -80,30 +107,26 @@ module.exports = new Class
 		}
 	}
 
-	,_value: undefined
-	,_param: null
 	,_editable: true
-	,_blockParamChange: false
-	,_blockValueChange: false
 
-	,onParamChange: function ()
+	,_setValue: function (newValue)
 	{
-		if (!this._blockValueChange)
-		{
-			this._blockParamChange = true;
-			this.value = this._param.value;
-			this._blockParamChange = false;
-		}
+		Vn.ParamIface.prototype._setValue.call (this, newValue);
+		this.putValue (newValue);
+
+		if (this.conditionalFunc)
+			this.conditionalFunc (this, newValue);
 	}
-	
-	,bindToLot: function ()
+
+	/**
+	 * Protected method that should be called from class childs when the value
+	 * on the associated entry changes.
+	 *
+	 * @param {*} value The new entry value
+	 */
+	,valueChanged: function (value)
 	{
-		if (this._lot && this._paramName)
-			this.param = new Vn.Param
-			({
-				 lot: this._lot
-				,name: this._paramName
-			});
+		this._setValue (value);
 	}
 
 	/**
@@ -121,28 +144,5 @@ module.exports = new Class
 	 * @param {*} value The new value for the entry
 	 */
 	,putValue: function () {}
-
-	/**
-	 * Protected method that should be called from class childs when the value
-	 * on the associated entry changes.
-	 *
-	 * @param {*} value The new entry value
-	 */
-	,valueChanged: function (value)
-	{
-		this._value = value;
-		
-		if (this.conditionalFunc)
-			this.conditionalFunc (this, value);
-	
-		if (this._param && !this._blockParamChange)
-		{
-			this._blockValueChange = true;
-			this._param.value = value;
-			this._blockValueChange = false;
-		}
-		
-		this.signalEmit ('changed');
-	}
 });
 
diff --git a/js/htk/field/button.js b/js/htk/field/button.js
index 84056f24..033952b3 100644
--- a/js/htk/field/button.js
+++ b/js/htk/field/button.js
@@ -82,6 +82,6 @@ module.exports = new Class
 	,_onClick: function (event)
 	{
 		event.preventDefault ();
-		this.signalEmit ('click', this._form, event);
+		this.emit ('click', this._form, event);
 	}
 });
diff --git a/js/htk/field/combo.js b/js/htk/field/combo.js
index 1f41a2c0..de578a14 100644
--- a/js/htk/field/combo.js
+++ b/js/htk/field/combo.js
@@ -184,7 +184,7 @@ module.exports = new Class
 		popup.on ('closed', this._onPopupClose.bind (this));
 		popup.show (this.node, true, e);
 
-		this.signalEmit ('menu-show');
+		this.emit ('menu-show');
 	}
 	
 	,_onGridClicked: function (grid, e)
@@ -216,7 +216,7 @@ module.exports = new Class
 	,_onPopupClose: function ()
 	{
 		this._popup = null;
-		this.signalEmit ('menu-hide');
+		this.emit ('menu-hide');
 	}
 	
 	,_refreshShowText: function ()
@@ -238,7 +238,7 @@ module.exports = new Class
 	,_onModelChange: function ()
 	{
 		var model = this._model;
-		this.signalEmit ('status-changed');
+		this.emit ('status-changed');
 		
 		if (this._popup)
 			this._popup.reset ();
@@ -246,7 +246,7 @@ module.exports = new Class
 		if (model && model.ready)
 		{
 			this._selectOption ();
-			this.signalEmit ('ready');
+			this.emit ('ready');
 		}
 		else
 			this._setRow (-1);
diff --git a/js/htk/field/radio.js b/js/htk/field/radio.js
index ea839618..3e5e4723 100644
--- a/js/htk/field/radio.js
+++ b/js/htk/field/radio.js
@@ -21,7 +21,7 @@ module.exports = new Class
 			type: RadioGroup
 			,set: function (x)
 			{
-				this.link ({_radioGroup: x}, {'changed': this._onRadioGroupChange});
+				this.link ({_radioGroup: x}, {changed: this._onRadioGroupChange});
 				this.node.name = x.radioName
 				this._onRadioGroupChange ();
 			}
diff --git a/js/htk/field/table.js b/js/htk/field/table.js
index b1b39464..b0137de7 100644
--- a/js/htk/field/table.js
+++ b/js/htk/field/table.js
@@ -32,7 +32,7 @@ module.exports = new Class
 	,changed: function (rbGroup)
 	{
 		this.realValue = this.rbGroup.getValue ();
-		this.signalEmit ('changed');
+		this.emit ('changed');
 	}
 
 	,selectValue: function ()
diff --git a/js/htk/image-editor.js b/js/htk/image-editor.js
index 399d5c54..053258d4 100644
--- a/js/htk/image-editor.js
+++ b/js/htk/image-editor.js
@@ -39,7 +39,7 @@ module.exports = new Class
 		if (!newValue)
 			newValue = null
 	
-		this.signalEmit ('name-changed', newValue);
+		this.emit ('name-changed', newValue);
 	}
 	
 	,_onSubmit: function ()
@@ -60,7 +60,7 @@ module.exports = new Class
 			throw error;
 
 		Toast.showMessage (_('ImageAdded'));
-		this.signalEmit ('file-uploaded', this.$('name').value);
+		this.emit ('file-uploaded', this.$('name').value);
 	}
 	
 	,setData: function (image, directory)
diff --git a/js/htk/popup.js b/js/htk/popup.js
index bf687516..20a33198 100644
--- a/js/htk/popup.js
+++ b/js/htk/popup.js
@@ -261,7 +261,7 @@ module.exports = new Class
 		Vn.Node.remove (node);
 		this._parent = null;
 		this._isOpen = false;
-		this.signalEmit ('closed');
+		this.emit ('closed');
 	}
 	
 	,_bgMouseDown: function (e)
diff --git a/js/htk/repeater.js b/js/htk/repeater.js
index 5c00690b..055b928a 100644
--- a/js/htk/repeater.js
+++ b/js/htk/repeater.js
@@ -169,7 +169,7 @@ module.exports = new Class
 		}
 	
 		this.node.appendChild (this._container);
-		this.signalEmit ('change');
+		this.emit ('change');
 	}
 
 	,_showNoRecordsFound: function (count)
diff --git a/js/sql/field.js b/js/sql/field.js
index b936b125..e6967331 100644
--- a/js/sql/field.js
+++ b/js/sql/field.js
@@ -12,11 +12,17 @@ module.exports = new Class
 	,Tag: 'sql-field'
 	,Properties:
 	{
+		/**
+		 * The column name.
+		 */
 		name:
 		{
 			type: String
 			,value: null
 		},
+		/**
+		 * The source table name or its alias if it has been specified.
+		 */
 		target:
 		{
 			type: String
@@ -24,7 +30,7 @@ module.exports = new Class
 		}
 	}
 
-	,render: function (batch)
+	,render: function ()
 	{
 		var sql = (this.target) ? '`' + this.target + '`.' : '';	
 		return sql + '`' + this.name + '`';
diff --git a/js/sql/filter-item.js b/js/sql/filter-item.js
index 175fb3a6..c730a980 100644
--- a/js/sql/filter-item.js
+++ b/js/sql/filter-item.js
@@ -1,8 +1,12 @@
 
 var Operation = require ('./operation');
+var Value = require ('./value');
+var Field = require ('./field');
 
 /**
- * The equivalent of a SQL operation.
+ * Objects to be used as an operands of @Sql.Filter. It represents a two
+ * expressions basic operation composed by a table column, the operator and the
+ * value extracted from the rendering paramerers.
  */
 module.exports = new Class
 ({
@@ -10,11 +14,66 @@ module.exports = new Class
 	,Tag: 'sql-filter-item'
 	,Properties:
 	{
-		primary:
+		/**
+		 * The column name.
+		 */
+		field:
 		{
-			type: Boolean
+			type: String,
+			value: null
+		},
+		/**
+		 * The source table name or its alias if it has been specified.
+		 */
+		target:
+		{
+			type: String,
+			value: null
+		},
+		/**
+		 * The parameter name.
+		 */
+		param:
+		{
+			type: String,
+			value: null
 		}
 	}
-	
-	,primary: true
+
+	/**
+	 * Checks if parameter name haa been defined and if it has a value.
+	 */
+	,isReady: function (params)
+	{
+		return this.param != null && params != null && params[this.param] != null;
+	}
+
+	,render: function (params)
+	{
+		var op = new Operation ({type: this.type});
+
+		var field = new Field ({
+			name: this.field,
+			target: this.target
+		});
+		op.appendChild (field);
+
+		var value = params[this.param];
+
+		if (this.type === Operation.Type.LIKE && typeof value == 'string')
+		{
+			value = value.replace (/[\%\?]/g, this._escapeChar);
+			value = value.replace (/^|\s+|$/g, '%');
+		}
+
+		var sqlValue = new Value ({value: value});
+		op.appendChild (sqlValue);
+			
+		return op.render (params);
+	}
+
+	,_escapeChar: function (char)
+	{
+		return '\\'+ char;
+	}
 });
diff --git a/js/sql/filter.js b/js/sql/filter.js
index a574ff3b..5b557fcf 100644
--- a/js/sql/filter.js
+++ b/js/sql/filter.js
@@ -1,50 +1,46 @@
 
 var Operation = require ('./operation');
+var Value = require ('./value');
 
 /**
- * The equivalent of a SQL operation.
+ * The equivalent of a SQL filter expression. It allows to automatically build
+ * SQL filters based on lot parameters.
  */
 module.exports = new Class
 ({
 	Extends: Operation
 	,Tag: 'sql-filter'
-	,Properties:
-	{
-		alwaysReady:
-		{
-			type: Boolean
-		}
-	}
 
-	,isReady: function ()
+	/**
+	 * Checks if any of filters childs are ready.
+	 */
+	,isReady: function (params)
 	{
-		if (this.alwaysReady)
+		var exprs = this.exprs.objects;
+		for (var i = exprs.length; i--;)
+		if (exprs[i].isReady (params))
 			return true;
-	
-		var e = this.exprs.getArray ();
-		for (var i = 0; i < e.length; i++)
-		if (e[i].isReady () && e[i].primary)
-			return true;
-				
+
 		return false;
 	}
 
-	,render: function (batch)
+	/**
+	 * Renders the filter as an SQL expression. If any of its childs isn't
+	 * ready is ommitted from the expression. If all of its childs aren't ready
+	 * renders the TRUE expression.
+	 */
+	,render: function (params)
 	{
-		var isReady = false;
-		var newOp = new Operation ({type: this.type});
+		var op = new Operation ({type: this.type});
+		var exprs = this.exprs.objects;
+
+		for (var i = 0; i < exprs.length; i++)
+		if (exprs[i].isReady (params))
+			op.exprs.add (exprs[i]);
 	
-		var e = this.exprs.getArray ();
-		for (var i = 0; i < e.length; i++)
-		if (e[i].isReady ())
-		{
-			newOp.exprs.add (e[i]);
-			isReady = true;
-		}
+		if (op.exprs.objects.length == 0)
+			op = new Value ({value: true});
 			
-		if (!isReady)
-			return 'TRUE';
-			
-		return newOp.render (batch);
+		return op.render (params);
 	}
 });
diff --git a/js/sql/list.js b/js/sql/list.js
index e9ff9275..00af5453 100644
--- a/js/sql/list.js
+++ b/js/sql/list.js
@@ -35,7 +35,7 @@ module.exports = new Class
 
 	,_onObjectChange: function ()
 	{
-		this.signalEmit ('changed');
+		this.emit ('changed');
 	}
 
 	,isReady: function ()
diff --git a/js/sql/object.js b/js/sql/object.js
index 316d111b..74f56b31 100644
--- a/js/sql/object.js
+++ b/js/sql/object.js
@@ -16,6 +16,7 @@ module.exports = new Class
 	/**
 	 * Gets if the object is ready to be rendered.
 	 *
+	 * @param {Object} params The query parameters
 	 * @return {boolean} %true if the object is ready, %false otherwise
 	 */
 	,isReady: function ()
diff --git a/js/sql/operation.js b/js/sql/operation.js
index be8a4145..76445641 100644
--- a/js/sql/operation.js
+++ b/js/sql/operation.js
@@ -7,8 +7,8 @@ var Expr = require ('./expr');
  * @param {Array#Sql.Expr} expr Array with the exprs
  * @param {Sql..Operation.Type} type The type of the operation
  */
-var Operation = new Class ();
-module.exports = Operation;
+var Klass = new Class ();
+module.exports = Klass;
 
 var Type =
 {
@@ -28,13 +28,13 @@ var Operators =
 	,'REGEXP'
 ];
 
-Operation.extend
+Klass.extend
 ({
 	Type: Type
 	,Operators: Operators
 });
 
-Operation.implement
+Klass.implement
 ({
 	Extends: Expr
 	,Tag: 'sql-operation'
@@ -60,7 +60,7 @@ Operation.implement
 
 	,onListChange: function ()
 	{
-		this.signalEmit ('changed');
+		this.emit ('changed');
 	}
 
 	,isReady: function ()
diff --git a/js/sql/search-tags.js b/js/sql/search-tags.js
index 7e0eab98..96f4ac77 100644
--- a/js/sql/search-tags.js
+++ b/js/sql/search-tags.js
@@ -9,7 +9,7 @@ module.exports = new Class
 	Extends: Value
 	,Tag: 'sql-search-tags'
 
-	,render: function (batch)
+	,render: function ()
 	{
 		if (typeof this._value == 'string')
 		{
diff --git a/js/sql/string.js b/js/sql/string.js
index aa9fefb7..54015f29 100644
--- a/js/sql/string.js
+++ b/js/sql/string.js
@@ -19,18 +19,18 @@ module.exports = new Class
 
 	,regexp: /#\w+/g
 	
-	,replaceFunc: function (batch, token)
+	,replaceFunc: function (params, token)
 	{
 		var holder = new Holder ({id: token.substr (1)});
-		return holder.render (batch);
+		return holder.render (params);
 	}
 
-	,render: function (batch)
+	,render: function (params)
 	{
 		if (!this.query)
 			return null;
 
-		return this.query.replace (this.regexp, this.replaceFunc.bind (this, batch));
+		return this.query.replace (this.regexp, this.replaceFunc.bind (this, params));
 	}
 
 	,findHolders: function ()
diff --git a/js/sql/value.js b/js/sql/value.js
index c0a73d5b..21e88e92 100644
--- a/js/sql/value.js
+++ b/js/sql/value.js
@@ -7,72 +7,86 @@ var Expr = require ('./expr');
 module.exports = new Class
 ({
 	Extends: Expr
+	,Implements: Vn.ParamIface
 	,Tag: 'sql-value'
-
 	,Properties:
 	{
-		/**
-		 * The master param.
-		 */
-		param:
+		value:
 		{
-			type: Vn.Param
+			type: null
 			,set: function (x)
 			{
-				this.link ({_param: x}, {'changed': this.onParamChange});
-				this.onParamChange ();
+				this._setValue (x);
+			}
+			,get: function ()
+			{
+				return this._value;
+			}
+		},
+		type:
+		{
+			type: Type
+			,set: function (x)
+			{
+				this._setType (x);
+			}
+			,get: function ()
+			{
+				return this._type;
+			}
+		},
+		param:
+		{
+			type: Vn.ParamIface
+			,set: function (x)
+			{
+				this._setParam (x);
 			}
 			,get: function ()
 			{
 				return this._param;
 			}
 		},
-		/**
-		 * The value.
-		 */
-		value:
+		lot:
+		{
+			type: Vn.LotIface
+			,set: function (x)
+			{
+				this._setLot (x);
+			}
+			,get: function ()
+			{
+				return this._lot;
+			}
+		},
+		name:
 		{
 			type: String
 			,set: function (x)
 			{
-				if (Vn.Value.compare (x, this._value))
-					return;
-
-				if (x instanceof Date)
-					x = x.clone ();
-			
-				this._value = x;
-		
-				if (this._param && !this.paramLock)
-				{
-					this.paramLock = true;
-					this._param.value = x;
-					this.paramLock = false;
-				}
-
-				this.signalEmit ('changed');
+				this._name = x;
+				this._onLotChange ();
 			}
 			,get: function ()
 			{
-				return this._value;
+				return this._name;
+			}
+		},
+		oneWay:
+		{
+			type: Boolean
+			,set: function (x)
+			{
+				this._oneWay = x;
+			}
+			,get: function ()
+			{
+				return this._oneWay;
 			}
 		}
 	}
 
-	,_value: undefined
-	,_param: null
 	,regexp: new RegExp ('(\\\\)|\'', 'g')
-	,paramLock: false
-
-	,onParamChange: function ()
-	{
-		if (this.paramLock || !this._param)
-			return;
-	
-		this.paramLock = true;
-		this.value = this._param.value;
-		this.paramLock = false;
-	}
 	
 	,isReady: function ()
 	{
diff --git a/js/vn/builder.js b/js/vn/builder.js
index 09b07ef7..d3a9dfb4 100644
--- a/js/vn/builder.js
+++ b/js/vn/builder.js
@@ -390,6 +390,9 @@ module.exports = new Class
 
 		switch (propInfo.type)
 		{
+			case null:
+				newValue = value;
+				break;
 			case Boolean:
 				newValue = (/^(true|1)$/i).test (value);
 				break;
@@ -614,7 +617,7 @@ var BuilderResult = new Class
 	{
 		var objects = this.objects;
 	
-		for (var i = 0; i < objects.length; i++)
+		for (var i = objects.length; i--;)
 		if (objects[i] instanceof VnObject)
 			objects[i].unref ();
 
diff --git a/js/vn/hash.js b/js/vn/hash.js
index 48236ce9..81d6537b 100644
--- a/js/vn/hash.js
+++ b/js/vn/hash.js
@@ -5,7 +5,9 @@ var LotIface = require ('./lot-iface');
 var Value = require ('./value');
 
 /**
- * Class to handle the URL.
+ * Class to handle the hash part of the URL as a key-value
+ * javascript object. It also handles dates and objects as
+ * a value.
  */
 module.exports = new Class  
 ({
@@ -60,12 +62,7 @@ module.exports = new Class
 		object[key] = value;
 		this.assign (object);
 	}
-	
-	/**
-	 * Sets the hash part of the URL, respecting the current hash variables.
-	 *
-	 * @param {Object} object A key-value object
-	 */
+
 	,assign: function (object)
 	{
 		var newObject = {};
diff --git a/js/vn/json-connection.js b/js/vn/json-connection.js
index 2c893c81..ba334a10 100644
--- a/js/vn/json-connection.js
+++ b/js/vn/json-connection.js
@@ -79,7 +79,7 @@ module.exports = new Class
 			var storage = remember ? localStorage : sessionStorage;
 			storage.setItem ('vnToken', this.token);
 
-			this.signalEmit ('openned');
+			this.emit ('openned');
 		}
 		else	
 			this._closeClient ();
@@ -105,7 +105,7 @@ module.exports = new Class
 	 */
 	,_onClose: function (callback, json, error)
 	{
-		this.signalEmit ('closed');
+		this.emit ('closed');
 
 		if (callback)
 			callback (this, json === true, error);
@@ -217,7 +217,7 @@ module.exports = new Class
 		this._requestsCount++;
 		
 		if (this._requestsCount === 1)
-			this.signalEmit ('loading-changed', true);
+			this.emit ('loading-changed', true);
 	}
 	
 	,_onStateChange: function (request, callback)
@@ -228,7 +228,7 @@ module.exports = new Class
 		this._requestsCount--;
 		
 		if (this._requestsCount === 0)
-			this.signalEmit ('loading-changed', false);
+			this.emit ('loading-changed', false);
 
 		var data = null;
 		var error = null;
@@ -310,7 +310,7 @@ module.exports = new Class
 			if (error.exception == 'SessionExpired')
 				this.clearToken ();
 		
-			this.signalEmit ('error', error);
+			this.emit ('error', error);
 		}
 	}
 });
diff --git a/js/vn/json-model.js b/js/vn/json-model.js
new file mode 100644
index 00000000..ec6ae32f
--- /dev/null
+++ b/js/vn/json-model.js
@@ -0,0 +1,90 @@
+
+var VnObject = require ('./object');
+var ModelIface = require ('./model-iface');
+var ModelProxy = require ('./model-proxy');
+
+var Mode = ModelProxy.Mode;
+
+/**
+ * Model that holds an array of Javascript objects with
+ * the same structure.
+ */
+module.exports = new Class
+({
+	Extends: VnObject
+	,Implements: ModelIface
+	,Tag: 'vn-json-model'
+	,Properties:
+	{
+		numRows:
+		{
+			type: Number
+		},
+		status:
+		{
+			type: Number
+		},
+		mode:
+		{
+			enumType: Mode
+			,value: Mode.ON_CHANGE
+		},
+		data:
+		{
+			type: Array
+			,set: function (x)
+			{
+				this.data = x;
+			}
+			,get: function ()
+			{
+				return this.data;
+			}
+		}
+	}
+
+	,checkColExists: function () {}
+
+	,checkRowExists: function () {}
+	
+	,getColumnIndex: function () {}
+	
+	,get: function (rowIndex, columnName)
+	{
+		return this.data[rowIndex][columnName];
+	}
+
+	,getByIndex: function (rowIndex, column)
+	{
+		var columnName = this.columns[column];
+		return this.data[rowIndex][columnName];
+	}
+
+	,getObject: function (rowIndex)
+	{
+		return this.data[rowIndex];
+	}
+
+	,sortByName: function () {}
+
+	,sort: function () {}
+
+	,search: function () {}
+
+	,searchByIndex: function () {}
+
+	,set: function () {}
+
+	,setByIndex: function () {}
+
+	,deleteRow: function () {}
+
+	,insertRow: function () {}
+
+	,clean: function () {}
+
+	,performOperations: function () {}
+
+	,indexColumn: function () {}
+});
+
diff --git a/js/vn/lot-iface.js b/js/vn/lot-iface.js
index dfc12168..4f621ebf 100644
--- a/js/vn/lot-iface.js
+++ b/js/vn/lot-iface.js
@@ -1,4 +1,8 @@
 
+/**
+ * Holds a plain key-value javascript object and monitorizes
+ * changes over it.
+ */
 module.exports = new Class
 ({
 	Properties:
@@ -40,7 +44,7 @@ module.exports = new Class
 	 */
 	,changed: function ()
 	{
-		this.signalEmit ('change');
+		this.emit ('change');
 	}
 
 	/**
@@ -56,6 +60,11 @@ module.exports = new Class
 		this.changed ();
 	}
 
+	/**
+	 * Copies all values from another lot.
+	 *
+	 * @param {LotIface} lot The source lot
+	 */
 	,assignLot: function (lot)
 	{
 		this.assign (lot.params);
diff --git a/js/vn/lot-query.js b/js/vn/lot-query.js
new file mode 100644
index 00000000..e77a45af
--- /dev/null
+++ b/js/vn/lot-query.js
@@ -0,0 +1,125 @@
+
+var Lot = require ('./lot');
+var LotIface = require ('./lot-iface');
+
+var Klass = new Class ();
+module.exports = Klass;
+
+var Type =
+{
+	INCLUDE: 1,
+	EXCLUDE: 2
+};
+
+Klass.extend
+({
+	Type: Type
+});
+
+Klass.implement
+({
+	Extends: Lot
+	,Tag: 'vn-lot-query'
+	,Properties:
+	{
+		fields:
+		{ 
+			type: Array
+			,set: function (x)
+			{
+				this._fields = x;
+				this._onSourceChange ();
+			}
+			,get: function ()
+			{
+				return this._fields;
+			}
+		},
+		source:
+		{ 
+			type: LotIface
+			,set: function (x)
+			{
+				this.link ({_source: x}, {change: this._onSourceChange});
+				this._onSourceChange ();
+			}
+			,get: function ()
+			{
+				return this._source;
+			}
+		},
+		type:
+		{
+			enumType: Type
+			,set: function (x)
+			{
+				this._type = x;
+				this._onSourceChange ();
+			}
+			,get: function ()
+			{
+				return this._type;
+			}
+		}
+	}
+
+	,_fields: null
+	,_source: null
+	,_type: Type.INCLUDE
+
+	,_onSourceChange: function ()
+	{
+		var changed = false;
+		var params = this._source ? this._source.params : {};
+
+		if (this._fields)
+		switch (this._type)
+		{
+			case Type.EXCLUDE:
+				changed = this.typeExclude (params);
+				break;
+			default:
+				changed = this.typeInclude (params);
+		}
+		else
+			changed = true;
+
+		if (changed)
+			this.changed ();
+	}
+
+	,typeInclude: function (params)
+	{
+		var changed = false;
+		var fields = this._fields;
+
+		for (var i = fields.length; i--;)
+		{
+			var field = fields[i];
+
+			if (this._params[field] !== params[field])
+			{
+				this._params[field] = params[field];
+				changed = true;
+			}
+		}
+
+		return changed;
+	}
+
+	,typeExclude: function (params)
+	{
+		var changed = false;
+
+		for (var field in params)
+		if (this._fields.indexOf (field) === -1
+		&& this._params[field] !== params[field])
+		{
+			this._params[field] = params[field];
+			changed = true;
+		}
+
+		return changed;
+	}
+});
+
diff --git a/js/vn/lot.js b/js/vn/lot.js
index 65594326..2f6fd981 100644
--- a/js/vn/lot.js
+++ b/js/vn/lot.js
@@ -1,10 +1,10 @@
 
-var Object = require ('./object');
+var VnObject = require ('./object');
 var LotIface = require ('./lot-iface');
 
 module.exports = new Class
 ({
-	Extends: Object
+	Extends: VnObject
 	,Implements: LotIface
 	,Tag: 'vn-lot'
 	,Properties:
@@ -24,6 +24,8 @@ module.exports = new Class
 		}
 	}
 
+	,_attachments: null
+
 	,initialize: function (props)
 	{
 		this._params = {};
diff --git a/js/vn/model-iface.js b/js/vn/model-iface.js
new file mode 100644
index 00000000..59cc75f0
--- /dev/null
+++ b/js/vn/model-iface.js
@@ -0,0 +1,157 @@
+
+var VnObject = require ('./object');
+
+/**
+ * Readable data model.
+ */
+var Klass = new Class ();
+module.exports = Klass;
+
+var Status =
+{
+	 CLEAN    : 1
+	,LOADING  : 2
+	,READY    : 3
+	,ERROR    : 4
+};
+
+var SortWay =
+{
+	 ASC   : 1
+	,DESC  : 2 
+};
+
+Klass.extend
+({
+	 Status: Status
+	,SortWay: SortWay
+});
+
+Klass.implement
+({
+	Extends: VnObject
+	,Properties:
+	{
+		/**
+		 * The number of rows in the model.
+		 */
+		numRows:
+		{
+			type: Number
+		},
+		/**
+		 * The current status of the model.
+		 */
+		status:
+		{
+			type: Number
+		},
+		/**
+		 * Checks if the model data is ready.
+		 */
+		ready:
+		{
+			type: Boolean
+		}
+	}
+
+	/**
+	 * Checks if the column exists.
+	 *
+	 * @param {integer} column The column index
+	 * @return {Boolean} %true if column exists, %false otherwise
+	 */
+	,checkColExists: function () {}
+
+	/**
+	 * Checks if the row exists.
+	 *
+	 * @param {integer} rowIndex The row index
+	 * @return {Boolean} %true if row exists, %false otherwise
+	 */
+	,checkRowExists: function () {}
+	
+	/**
+	 * Get the index of the column from its name.
+	 *
+	 * @param {string} columnName The column name
+	 * @return {number} The column index or -1 if column not exists
+	 */
+	,getColumnIndex: function () {}
+	
+	/**
+	 * Gets a value from the model.
+	 *
+	 * @param {number} rowIndex The row index
+	 * @param {String} columnName The column name
+	 * @return {*} The value, or %undefined
+	 */
+	,get: function () {}
+
+	/**
+	 * Gets a value using the column index.
+	 *
+	 * @param {number} rowIndex The row index
+	 * @param {number} column The column index
+	 * @return {*} The value
+	 */
+	,getByIndex: function () {}
+
+	/**
+	 * Gets a row as an object  using the column index.
+	 *
+	 * @param {number} rowIndex The row index
+	 * @return {Object} The row as an object
+	 */
+	,getObject: function () {}
+
+	/**
+	 * Orders the model by the specified column name.
+	 *
+	 * @param {Number} column The column name
+	 * @param {SortWay} way The sort way
+	 */
+	,sortByName: function () {}
+
+	/**
+	 * Orders the model by the specified column.
+	 *
+	 * @param {Number} column The column index
+	 * @param {SortWay} way The sort way
+	 */
+	,sort: function () {}
+
+	/**
+	 * Searchs a value on the model and returns the row index of the first
+	 * ocurrence.
+	 * If an index have been built on that column, it will be used, for more
+	 * information see the indexColumn() method.
+	 *
+	 * @param {String} column The column name
+	 * @param {Object} value The value to search
+	 * @return {Number} The column index
+	 */
+	,search: function () {}
+
+	/**
+	 * Searchs a value on the model and returns the row index of the first
+	 * ocurrence.
+	 *
+	 * @param {Number} col The column index
+	 * @param {Object} value The value to search
+	 * @return {Number} The column index
+	 */
+	,searchByIndex: function () {}
+
+	/**
+	 * Builds an internal hash index for the specified column, this speeds
+	 * significantly searches on that column, specially when model has a lot of
+	 * rows.
+	 *
+	 * FIXME: Not fully implemented.
+	 *
+	 * @param {String} column The column name
+	 */
+	,indexColumn: function () {}
+});
+
diff --git a/js/vn/model-proxy.js b/js/vn/model-proxy.js
new file mode 100644
index 00000000..fd9bdeff
--- /dev/null
+++ b/js/vn/model-proxy.js
@@ -0,0 +1,86 @@
+
+var ModelIface = require ('./object');
+
+/**
+ * Monitorizes all changes made to a model.
+ */
+var Klass = new Class ();
+module.exports = Klass;
+
+var Mode =
+{
+	 ON_CHANGE : 1
+	,ON_DEMAND : 2
+};
+
+var Operation =
+{
+	 INSERT : 1 << 1
+	,UPDATE : 1 << 2
+	,DELETE : 1 << 3
+};
+
+Klass.extend
+({
+	 Mode: Mode
+	,Operation: Operation
+});
+
+Klass.implement
+({
+	Implements: ModelIface
+	,Properties:
+	{
+		/**
+		 * Update mode.
+		 */
+		mode:
+		{
+			enumType: Mode
+			,value: Mode.ON_CHANGE
+		}
+	}
+
+	/**
+	 * Updates a value on the model.
+	 *
+	 * @param {number} rowIndex The row index
+	 * @param {String} columnName The column name
+	 * @param {*} value The new value
+	 */
+	,set: function () {}
+
+	/**
+	 * Updates a value on the model using the column index.
+	 *
+	 * @param {Number} rowIndex The row index
+	 * @param {Number} col The column index
+	 * @param {*} value The new value
+	 */
+	,setByIndex: function () {}
+
+	/**
+	 * Deletes a row from the model. 
+	 *
+	 * @param {number} rowIndex The row index
+	 */
+	,deleteRow: function () {}
+
+	/**
+	 * Inserts a new row on the model.
+	 *
+	 * @return The index of the inserted row
+	 */
+	,insertRow: function () {}
+
+	/**
+	 * Cleans the model data.
+	 */
+	,clean: function () {}
+	
+	/**
+	 * Performs all model changes on the database.
+	 */
+	,performOperations: function () {}
+});
+
diff --git a/js/vn/object.js b/js/vn/object.js
index 6a308c13..f0e644c4 100644
--- a/js/vn/object.js
+++ b/js/vn/object.js
@@ -12,6 +12,12 @@ module.exports = new Class
 	,_refCount: 1
 	,_signalData: null
 
+	/**
+	 * Initializes the object and sets all properties passed to the class
+	 * constructor.
+	 * 
+	 * @param {Object} props The properties passed to the contructor
+	 */
 	,initialize: function (props)
 	{
 		this.setProperties (props);
@@ -47,18 +53,23 @@ module.exports = new Class
 		if (this._refCount === 0)
 			this._destroy ();
 	}
-	
-	,loadXml: function (builder, node) {}
-	,appendChild: function (child) {}
-	
-	,_signalInit: function ()
-	{
-		if (!this._signalData)
-			this._signalData = {
-				signals: {},
-				links: {}
-			};
-	}
+
+	/**
+	 * Called from @Vn.Builder when it finds a custom tag as a child of the
+	 * element.
+	 * 
+	 * @param {Vn.Builder} builder The builder instance
+	 * @param {Node} node The custom tag child nodes
+	 */
+	,loadXml: function () {}
+
+	/**
+	 * Called from @Vn.Builder when it finds a a child tag that isn't
+	 * associated to any object property.
+	 * 
+	 * @param {Object} child The child object instance
+	 */
+	,appendChild: function () {}
 
 	/**
 	 * Conects a signal with a function.
@@ -81,8 +92,7 @@ module.exports = new Class
 		if (!callbacks)
 			callbacks = this._signalData.signals[id] = [];
 			
-		callbacks.push
-		({
+		callbacks.push ({
 			 blocked: false
 			,callback: callback
 			,instance: instance
@@ -117,7 +127,7 @@ module.exports = new Class
 	 *
 	 * @param {String} id The signal identifier
 	 */
-	,signalEmit: function (id)
+	,emit: function (id)
 	{
 		if (!this._signalData)
 			return;
@@ -152,13 +162,11 @@ module.exports = new Class
 
 		var callbacks = this._signalData.signals[id];
 		
-		if (!callbacks)
-			return;
-
-		for (var i = 0; i < callbacks.length; i++)
+		if (callbacks)
+		for (var i = callbacks.length; i--;)
 		if (callbacks[i].callback === callback
 		&& callbacks[i].instance === instance)
-			callbacks.splice (i--, 1);
+			callbacks.splice (i, 1);
 	}
 	
 	/**
@@ -177,9 +185,10 @@ module.exports = new Class
 		{
 			var callbacks = signals[signalId];
 
-			for (var i = 0; i < callbacks.length; i++)
+			if (callbacks)
+			for (var i = callbacks.length; i--;)
 			if (callbacks[i].instance === instance)
-				callbacks.splice (i--, 1);
+				callbacks.splice (i, 1);
 		}
 	}
 
@@ -199,12 +208,6 @@ module.exports = new Class
 		
 		this._signalData = null;
 	}
-
-	,_unlink: function (object)
-	{
-		object.disconnectByInstance (this);
-		object.unref ();
-	}
 	
 	/**
 	 * Links the object with another object.
@@ -238,5 +241,20 @@ module.exports = new Class
 				delete links[key];
 		}
 	}
+
+	,_unlink: function (object)
+	{
+		object.disconnectByInstance (this);
+		object.unref ();
+	}
+	
+	,_signalInit: function ()
+	{
+		if (!this._signalData)
+			this._signalData = {
+				signals: {},
+				links: {}
+			};
+	}
 });
 
diff --git a/js/vn/param-iface.js b/js/vn/param-iface.js
new file mode 100644
index 00000000..6f62e741
--- /dev/null
+++ b/js/vn/param-iface.js
@@ -0,0 +1,140 @@
+
+var LotIface = require ('./lot-iface');
+var Type = require ('./type');
+
+/**
+ * A value holder, it emits the changed signal when value is changed.
+ * Also it can be linked with a lot value or another parameter.
+ */
+module.exports = new Class
+({
+	Properties:
+	{
+		/**
+		 * The parameter value.
+		 */
+		value:
+		{
+			type: null
+		},
+		/**
+		 * The parameter type.
+		 */
+		type:
+		{
+			type: Type
+		},
+		/**
+		 * Another parameter to bind with.
+		 */
+		param:
+		{
+			type: Object
+		},
+		/**
+		 * A lot to bind with.
+		 */
+		lot:
+		{
+			type: LotIface
+		},
+		/**
+		 * The field name in the lot.
+		 */
+		name:
+		{
+			type: String
+		},
+		/**
+		 * Determines whether the link to the lot is unidirectional, ie, a
+		 * change in the lot updates the parameter but not viceversa.
+		 */
+		oneWay:
+		{
+			type: Boolean
+		}
+	}
+	
+	,_value: undefined
+	,_type: null
+	,_param: null
+	,_paramLock: false
+	,_lot: null
+	,_name: null
+	,_lotLock: false
+	,_oneWay: false
+	
+	,_setValue: function (newValue)
+	{
+		if (newValue == this._value)
+			return;
+	
+		if (newValue instanceof Date)
+			newValue = newValue.clone ();
+
+		this._value = newValue;
+		this._refreshLot ();
+		this._refreshParam ();
+		this.emit ('changed', newValue);
+	}
+
+	,_setType: function (type)
+	{
+		this._type = type;
+		this._onLotChange ();
+	}
+	
+	,_setParam: function (param)
+	{
+		this.link ({_param: param}, {changed: this._onParamChange});
+		this._refreshParam ();
+	}
+
+	,_onParamChange: function ()
+	{
+		if (this._paramLock || !this._param)
+			return;
+
+		this._paramLock = true;
+		this._setValue (this._param.value);
+		this._paramLock = false;
+	}
+
+	,_refreshParam: function ()
+	{
+		if (this._paramLock || !this._param)
+			return;
+
+		this._paramLock = true;
+		this._param.value = this._value;
+		this._paramLock = false;
+	}
+
+	,_setLot: function (lot)
+	{
+		this.link ({_lot: lot}, {change: this._onLotChange});
+		this._onLotChange ();
+	}
+
+	,_onLotChange: function ()
+	{
+		if (this._lotLock || !this._name || !this._lot)
+			return;
+
+		var newValue = this._lot.get (this._name, this._type);
+
+		this._lotLock = true;
+		this._setValue (newValue);
+		this._lotLock = false;
+	}
+
+	,_refreshLot: function ()
+	{
+		if (this._lotLock || !this._name || !this._lot || this._oneWay)
+			return;
+
+		this._lotLock = true;
+		this._lot.set (this._name, this._value);
+		this._lotLock = false;
+	}
+});
diff --git a/js/vn/param.js b/js/vn/param.js
index 6f23e799..620ef8a7 100644
--- a/js/vn/param.js
+++ b/js/vn/param.js
@@ -1,28 +1,67 @@
 
 var VnObject = require ('./object');
-var Lot = require ('./lot');
 var Type = require ('./type');
+var ParamIface = require ('./param-iface');
+var LotIface = require ('./lot-iface');
 
+/**
+ * A simple implementation of @ParamIface.
+ */
 module.exports = new Class
 ({
 	Extends: VnObject
+	,Implements: ParamIface
 	,Tag: 'vn-param'
-	,Child: 'param'
 	,Properties:
 	{
-		param:
+		value:
 		{
-			type: Object
+			type: null
 			,set: function (x)
 			{
-				this.link ({_param: x}, {'changed': this._onParamChange});
-				this._refreshParam ();
+				this._setValue (x);
+			}
+			,get: function ()
+			{
+				return this._value;
+			}
+		},
+		type:
+		{
+			type: Type
+			,set: function (x)
+			{
+				this._setType (x);
+			}
+			,get: function ()
+			{
+				return this._type;
+			}
+		},
+		param:
+		{
+			type: ParamIface
+			,set: function (x)
+			{
+				this._setParam (x);
 			}
 			,get: function ()
 			{
 				return this._param;
 			}
 		},
+		lot:
+		{
+			type: LotIface
+			,set: function (x)
+			{
+				this._setLot (x);
+			}
+			,get: function ()
+			{
+				return this._lot;
+			}
+		},
 		name:
 		{
 			type: String
@@ -36,48 +75,6 @@ module.exports = new Class
 				return this._name;
 			}
 		},
-		value:
-		{
-			type: Object
-			,set: function (x)
-			{
-				this._setValue (x, true);
-			}
-			,get: function ()
-			{
-				return this._value;
-			}
-		},
-		type:
-		{
-			type: Type
-			,set: function (x)
-			{
-				this._type = x;
-				this._onLotChange ();
-			}
-			,get: function ()
-			{
-				return this._type;
-			}
-		},
-		lot:
-		{
-			type: Lot
-			,set: function (x)
-			{
-				this.link ({_lot: x}, {'change': this._onLotChange});
-				this._onLotChange ();
-			}
-			,get: function ()
-			{
-				return this._lot;
-			}
-		},
-		/**
-		 * Determines whether the link to the form is unidirectional, ie, a
-		 * change in the lot updates the parameter but not vice versa.
-		 */
 		oneWay:
 		{
 			type: Boolean
@@ -91,67 +88,4 @@ module.exports = new Class
 			}
 		}
 	}
-	
-	,_lotLock: false
-	,_paramLock: false
-	,_value: undefined
-	,_lot: null
-	,_name: null
-	,_type: null
-	,_oneWay: false
-
-	,_onLotChange: function ()
-	{
-		if (this._lotLock || !this._name || !this._lot)
-			return;
-
-		var newValue = this._lot.get (this._name, this._type);
-
-		this._lotLock = true;
-		this._setValue (newValue, true);
-		this._lotLock = false;
-	}
-	
-	,_setValue: function (newValue, signal)
-	{
-		if (newValue == this._value)
-			return;
-	
-		if (newValue instanceof Date)
-			newValue = newValue.clone ();
-
-		this._value = newValue;
-
-		if (this._lot && this._name && !this._lotLock && !this._oneWay)
-		{
-			this._lotLock = true;
-			this._lot.set (this._name, newValue);
-			this._lotLock = false;
-		}
-
-		this._refreshParam ();
-
-		if (signal)
-			this.signalEmit ('changed', newValue);
-	}
-
-	,_refreshParam: function ()
-	{
-		if (this._param && !this._paramLock)
-		{
-			this._paramLock = true;
-			this._param.value = this._value;
-			this._paramLock = false;
-		}
-	}
-	
-	,_onParamChange: function ()
-	{
-		if (this._paramLock)
-			return;
-
-		this._paramLock = true;
-		this._setValue (this._param.value);
-		this._paramLock = false;
-	}
 });
diff --git a/js/vn/value.js b/js/vn/value.js
index c21603ed..ecc0cc53 100644
--- a/js/vn/value.js
+++ b/js/vn/value.js
@@ -31,12 +31,65 @@ function equals (a, b)
 	return false;
 }
 
+/**
+ * Calculates differences of to key-value objects.
+ *
+ * @param {Object} orgObject Value to compare to
+ * @param {Object} newObject Value to compare with
+ * @return {Object} The differences or %null if there are no differences
+ */
+function diff (orgObject, newObject)
+{
+	var key;
+	var diff = {};
+
+	var keys = Object.keys (orgObject);
+
+	for (var i = keys.length; --i; key = keys[i])
+	if (orgObject[key] !== newObject[key])
+		diff[key] = newObject[key];
+
+	var keys = Object.keys (newObject);
+
+	for (var i = keys.length; --i; key = keys[i])
+	if (orgObject[key] === undefined)
+		diff[key] = newObject[key];
+
+	if (Object.keys (diff).length > 0)
+		return diff;
+
+	return null;
+}
+
+/**
+ * Returns a formated string.
+ *
+ * @param {Object} formatString The base string template
+ * @param {...} arguments Format parameters
+ * @return {String} The formated string
+ */
+function sprintf (formatString)
+{
+	var args = arguments;
+
+	if (args.length <= 1)
+		return formatString;
+	
+	var i = 1;
+	return formatString.replace (/%[s|d]/g, function ()
+	{
+		return args[i++];
+	});
+}
+
 module.exports =
 {
 	 regexpNumber: /%\.([0-9]+)d/g
 	,regexpString: /%s/g
 
 	,equals: equals
+	,diff: diff
+	,sprintf: sprintf
 
 	,compare: function (a, b)
 	{
@@ -80,18 +133,3 @@ module.exports =
 		return value;
 	}
 };
-
-window.sprintf = function (formatString)
-{
-	var args = arguments;
-
-	if (args.length <= 1)
-		return formatString;
-	
-	var i = 1;
-	return formatString.replace (/%[s|d]/g, function ()
-	{
-		return args[i++];
-	});
-}
-
diff --git a/js/vn/vn.js b/js/vn/vn.js
index 1fd84735..275d2504 100644
--- a/js/vn/vn.js
+++ b/js/vn/vn.js
@@ -14,8 +14,13 @@ Vn = module.exports = {
 	,Url            : require ('./url')
 	,LotIface       : require ('./lot-iface')
 	,Lot            : require ('./lot')
+	,LotQuery       : require ('./lot-query')
 	,Hash           : require ('./hash')
+	,ParamIface     : require ('./param-iface')
 	,Param          : require ('./param')
+	,ModelIface     : require ('./model-iface')
+	,ModelProxy     : require ('./model-proxy')
+	,JsonModel      : require ('./json-model')
 	,Node           : require ('./node')
 	,Builder        : require ('./builder')
 	,JsonException  : require ('./json-exception')
diff --git a/rest/edi/sql/supplier.sql b/rest/edi/sql/supplier.sql
index 93c82929..6dd419be 100644
--- a/rest/edi/sql/supplier.sql
+++ b/rest/edi/sql/supplier.sql
@@ -3,6 +3,7 @@ LOAD DATA LOCAL INFILE #file
 	FIELDS TERMINATED BY ';'
 	LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7, @col8, @col9, @col10, @col11, @col12, @col13, @col14, @col15, @col16, @col17, @col18, @col19, @col20)
 	SET
+		glnAddressCode = @col2,
 		supplier_id = @col4,
 		company_name = @col3,
 		entry_date = STR_TO_DATE(@col9, '%Y%m%d'),