[REF] point_of_sale: update to the new module system
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,479 +1,485 @@
|
||||
openerp.point_of_sale.load_db = function load_db(instance, module){
|
||||
"use strict";
|
||||
odoo.define('point_of_sale.DB', ['web.core'], function (require) {
|
||||
"use strict";
|
||||
|
||||
/* The PosDB holds reference to data that is either
|
||||
* - static: does not change between pos reloads
|
||||
* - persistent : must stay between reloads ( orders )
|
||||
var core = require('web.core');
|
||||
/* The PosDB holds reference to data that is either
|
||||
* - static: does not change between pos reloads
|
||||
* - persistent : must stay between reloads ( orders )
|
||||
*/
|
||||
|
||||
var PosDB = core.Class.extend({
|
||||
name: 'openerp_pos_db', //the prefix of the localstorage data
|
||||
limit: 100, // the maximum number of results returned by a search
|
||||
init: function(options){
|
||||
options = options || {};
|
||||
this.name = options.name || this.name;
|
||||
this.limit = options.limit || this.limit;
|
||||
|
||||
if (options.uuid) {
|
||||
this.name = this.name + '_' + options.uuid;
|
||||
}
|
||||
|
||||
//cache the data in memory to avoid roundtrips to the localstorage
|
||||
this.cache = {};
|
||||
|
||||
this.product_by_id = {};
|
||||
this.product_by_barcode = {};
|
||||
this.product_by_category_id = {};
|
||||
|
||||
this.partner_sorted = [];
|
||||
this.partner_by_id = {};
|
||||
this.partner_by_barcode = {};
|
||||
this.partner_search_string = "";
|
||||
this.partner_write_date = null;
|
||||
|
||||
this.category_by_id = {};
|
||||
this.root_category_id = 0;
|
||||
this.category_products = {};
|
||||
this.category_ancestors = {};
|
||||
this.category_childs = {};
|
||||
this.category_parent = {};
|
||||
this.category_search_string = {};
|
||||
this.packagings_by_id = {};
|
||||
this.packagings_by_product_tmpl_id = {};
|
||||
this.packagings_by_barcode = {};
|
||||
},
|
||||
|
||||
/*
|
||||
* sets an uuid to prevent conflict in locally stored data between multiple databases running
|
||||
* in the same browser at the same origin (Doing this is not advised !)
|
||||
*/
|
||||
set_uuid: function(uuid){
|
||||
this.name = this.name + '_' + uuid;
|
||||
},
|
||||
|
||||
module.PosDB = instance.web.Class.extend({
|
||||
name: 'openerp_pos_db', //the prefix of the localstorage data
|
||||
limit: 100, // the maximum number of results returned by a search
|
||||
init: function(options){
|
||||
options = options || {};
|
||||
this.name = options.name || this.name;
|
||||
this.limit = options.limit || this.limit;
|
||||
|
||||
if (options.uuid) {
|
||||
this.name = this.name + '_' + options.uuid;
|
||||
}
|
||||
|
||||
//cache the data in memory to avoid roundtrips to the localstorage
|
||||
this.cache = {};
|
||||
|
||||
this.product_by_id = {};
|
||||
this.product_by_barcode = {};
|
||||
this.product_by_category_id = {};
|
||||
|
||||
this.partner_sorted = [];
|
||||
this.partner_by_id = {};
|
||||
this.partner_by_barcode = {};
|
||||
this.partner_search_string = "";
|
||||
this.partner_write_date = null;
|
||||
|
||||
this.category_by_id = {};
|
||||
this.root_category_id = 0;
|
||||
this.category_products = {};
|
||||
this.category_ancestors = {};
|
||||
this.category_childs = {};
|
||||
this.category_parent = {};
|
||||
this.category_search_string = {};
|
||||
this.packagings_by_id = {};
|
||||
this.packagings_by_product_tmpl_id = {};
|
||||
this.packagings_by_barcode = {};
|
||||
},
|
||||
|
||||
/*
|
||||
* sets an uuid to prevent conflict in locally stored data between multiple databases running
|
||||
* in the same browser at the same origin (Doing this is not advised !)
|
||||
*/
|
||||
set_uuid: function(uuid){
|
||||
this.name = this.name + '_' + uuid;
|
||||
},
|
||||
|
||||
/* returns the category object from its id. If you pass a list of id as parameters, you get
|
||||
* a list of category objects.
|
||||
*/
|
||||
get_category_by_id: function(categ_id){
|
||||
if(categ_id instanceof Array){
|
||||
var list = [];
|
||||
for(var i = 0, len = categ_id.length; i < len; i++){
|
||||
var cat = this.category_by_id[categ_id[i]];
|
||||
if(cat){
|
||||
list.push(cat);
|
||||
}else{
|
||||
console.error("get_category_by_id: no category has id:",categ_id[i]);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}else{
|
||||
return this.category_by_id[categ_id];
|
||||
}
|
||||
},
|
||||
/* returns a list of the category's child categories ids, or an empty list
|
||||
* if a category has no childs */
|
||||
get_category_childs_ids: function(categ_id){
|
||||
return this.category_childs[categ_id] || [];
|
||||
},
|
||||
/* returns a list of all ancestors (parent, grand-parent, etc) categories ids
|
||||
* starting from the root category to the direct parent */
|
||||
get_category_ancestors_ids: function(categ_id){
|
||||
return this.category_ancestors[categ_id] || [];
|
||||
},
|
||||
/* returns the parent category's id of a category, or the root_category_id if no parent.
|
||||
* the root category is parent of itself. */
|
||||
get_category_parent_id: function(categ_id){
|
||||
return this.category_parent[categ_id] || this.root_category_id;
|
||||
},
|
||||
/* adds categories definitions to the database. categories is a list of categories objects as
|
||||
* returned by the openerp server. Categories must be inserted before the products or the
|
||||
* product/ categories association may (will) not work properly */
|
||||
add_categories: function(categories){
|
||||
var self = this;
|
||||
if(!this.category_by_id[this.root_category_id]){
|
||||
this.category_by_id[this.root_category_id] = {
|
||||
id : this.root_category_id,
|
||||
name : 'Root',
|
||||
};
|
||||
}
|
||||
for(var i=0, len = categories.length; i < len; i++){
|
||||
this.category_by_id[categories[i].id] = categories[i];
|
||||
}
|
||||
for(var i=0, len = categories.length; i < len; i++){
|
||||
var cat = categories[i];
|
||||
var parent_id = cat.parent_id[0] || this.root_category_id;
|
||||
this.category_parent[cat.id] = cat.parent_id[0];
|
||||
if(!this.category_childs[parent_id]){
|
||||
this.category_childs[parent_id] = [];
|
||||
}
|
||||
this.category_childs[parent_id].push(cat.id);
|
||||
}
|
||||
function make_ancestors(cat_id, ancestors){
|
||||
self.category_ancestors[cat_id] = ancestors;
|
||||
|
||||
ancestors = ancestors.slice(0);
|
||||
ancestors.push(cat_id);
|
||||
|
||||
var childs = self.category_childs[cat_id] || [];
|
||||
for(var i=0, len = childs.length; i < len; i++){
|
||||
make_ancestors(childs[i], ancestors);
|
||||
}
|
||||
}
|
||||
make_ancestors(this.root_category_id, []);
|
||||
},
|
||||
category_contains: function(categ_id, product_id) {
|
||||
var product = this.product_by_id[product_id];
|
||||
if (product) {
|
||||
var cid = product.pos_categ_id[0];
|
||||
while (cid && cid !== categ_id){
|
||||
cid = this.category_parent[cid];
|
||||
}
|
||||
return !!cid;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
/* loads a record store from the database. returns default if nothing is found */
|
||||
load: function(store,deft){
|
||||
if(this.cache[store] !== undefined){
|
||||
return this.cache[store];
|
||||
}
|
||||
var data = localStorage[this.name + '_' + store];
|
||||
if(data !== undefined && data !== ""){
|
||||
data = JSON.parse(data);
|
||||
this.cache[store] = data;
|
||||
return data;
|
||||
}else{
|
||||
return deft;
|
||||
}
|
||||
},
|
||||
/* saves a record store to the database */
|
||||
save: function(store,data){
|
||||
var str_data = JSON.stringify(data);
|
||||
localStorage[this.name + '_' + store] = JSON.stringify(data);
|
||||
this.cache[store] = data;
|
||||
},
|
||||
_product_search_string: function(product){
|
||||
var str = product.display_name;
|
||||
if (product.barcode) {
|
||||
str += '|' + product.barcode;
|
||||
}
|
||||
if (product.default_code) {
|
||||
str += '|' + product.default_code;
|
||||
}
|
||||
if (product.description) {
|
||||
str += '|' + product.description;
|
||||
}
|
||||
if (product.description_sale) {
|
||||
str += '|' + product.description_sale;
|
||||
}
|
||||
var packagings = this.packagings_by_product_tmpl_id[product.product_tmpl_id] || [];
|
||||
for (var i = 0; i < packagings.length; i++) {
|
||||
str += '|' + packagings[i].barcode;
|
||||
}
|
||||
str = product.id + ':' + str.replace(':','') + '\n';
|
||||
return str;
|
||||
},
|
||||
add_products: function(products){
|
||||
var stored_categories = this.product_by_category_id;
|
||||
|
||||
if(!products instanceof Array){
|
||||
products = [products];
|
||||
}
|
||||
for(var i = 0, len = products.length; i < len; i++){
|
||||
var product = products[i];
|
||||
var search_string = this._product_search_string(product);
|
||||
var categ_id = product.pos_categ_id ? product.pos_categ_id[0] : this.root_category_id;
|
||||
product.product_tmpl_id = product.product_tmpl_id[0];
|
||||
if(!stored_categories[categ_id]){
|
||||
stored_categories[categ_id] = [];
|
||||
}
|
||||
stored_categories[categ_id].push(product.id);
|
||||
|
||||
if(this.category_search_string[categ_id] === undefined){
|
||||
this.category_search_string[categ_id] = '';
|
||||
}
|
||||
this.category_search_string[categ_id] += search_string;
|
||||
|
||||
var ancestors = this.get_category_ancestors_ids(categ_id) || [];
|
||||
|
||||
for(var j = 0, jlen = ancestors.length; j < jlen; j++){
|
||||
var ancestor = ancestors[j];
|
||||
if(! stored_categories[ancestor]){
|
||||
stored_categories[ancestor] = [];
|
||||
}
|
||||
stored_categories[ancestor].push(product.id);
|
||||
|
||||
if( this.category_search_string[ancestor] === undefined){
|
||||
this.category_search_string[ancestor] = '';
|
||||
}
|
||||
this.category_search_string[ancestor] += search_string;
|
||||
}
|
||||
this.product_by_id[product.id] = product;
|
||||
if(product.barcode){
|
||||
this.product_by_barcode[product.barcode] = product;
|
||||
}
|
||||
}
|
||||
},
|
||||
add_packagings: function(packagings){
|
||||
for(var i = 0, len = packagings.length; i < len; i++){
|
||||
var pack = packagings[i];
|
||||
this.packagings_by_id[pack.id] = pack;
|
||||
if(!this.packagings_by_product_tmpl_id[pack.product_tmpl_id[0]]){
|
||||
this.packagings_by_product_tmpl_id[pack.product_tmpl_id[0]] = [];
|
||||
}
|
||||
this.packagings_by_product_tmpl_id[pack.product_tmpl_id[0]].push(pack);
|
||||
if(pack.barcode){
|
||||
this.packagings_by_barcode[pack.barcode] = pack;
|
||||
}
|
||||
}
|
||||
},
|
||||
_partner_search_string: function(partner){
|
||||
var str = partner.name;
|
||||
if(partner.barcode){
|
||||
str += '|' + partner.barcode;
|
||||
}
|
||||
if(partner.address){
|
||||
str += '|' + partner.address;
|
||||
}
|
||||
if(partner.phone){
|
||||
str += '|' + partner.phone.split(' ').join('');
|
||||
}
|
||||
if(partner.mobile){
|
||||
str += '|' + partner.mobile.split(' ').join('');
|
||||
}
|
||||
if(partner.email){
|
||||
str += '|' + partner.email;
|
||||
}
|
||||
str = '' + partner.id + ':' + str.replace(':','') + '\n';
|
||||
return str;
|
||||
},
|
||||
add_partners: function(partners){
|
||||
var updated_count = 0;
|
||||
var new_write_date = '';
|
||||
for(var i = 0, len = partners.length; i < len; i++){
|
||||
var partner = partners[i];
|
||||
|
||||
if ( this.partner_write_date &&
|
||||
this.partner_by_id[partner.id] &&
|
||||
new Date(this.partner_write_date).getTime() + 1000 >=
|
||||
new Date(partner.write_date).getTime() ) {
|
||||
// FIXME: The write_date is stored with milisec precision in the database
|
||||
// but the dates we get back are only precise to the second. This means when
|
||||
// you read partners modified strictly after time X, you get back partners that were
|
||||
// modified X - 1 sec ago.
|
||||
continue;
|
||||
} else if ( new_write_date < partner.write_date ) {
|
||||
new_write_date = partner.write_date;
|
||||
}
|
||||
if (!this.partner_by_id[partner.id]) {
|
||||
this.partner_sorted.push(partner.id);
|
||||
}
|
||||
this.partner_by_id[partner.id] = partner;
|
||||
|
||||
updated_count += 1;
|
||||
}
|
||||
|
||||
this.partner_write_date = new_write_date || this.partner_write_date;
|
||||
|
||||
if (updated_count) {
|
||||
// If there were updates, we need to completely
|
||||
// rebuild the search string and the barcode indexing
|
||||
|
||||
this.partner_search_string = "";
|
||||
this.partner_by_barcode = {};
|
||||
|
||||
for (var id in this.partner_by_id) {
|
||||
var partner = this.partner_by_id[id];
|
||||
|
||||
if(partner.barcode){
|
||||
this.partner_by_barcode[partner.barcode] = partner;
|
||||
}
|
||||
partner.address = (partner.street || '') +', '+
|
||||
(partner.zip || '') +' '+
|
||||
(partner.city || '') +', '+
|
||||
(partner.country_id[1] || '');
|
||||
this.partner_search_string += this._partner_search_string(partner);
|
||||
}
|
||||
}
|
||||
return updated_count;
|
||||
},
|
||||
get_partner_write_date: function(){
|
||||
return this.partner_write_date || "1970-01-01 00:00:00";
|
||||
},
|
||||
get_partner_by_id: function(id){
|
||||
return this.partner_by_id[id];
|
||||
},
|
||||
get_partner_by_barcode: function(barcode){
|
||||
return this.partner_by_barcode[barcode];
|
||||
},
|
||||
get_partners_sorted: function(max_count){
|
||||
max_count = max_count ? Math.min(this.partner_sorted.length, max_count) : this.partner_sorted.length;
|
||||
var partners = [];
|
||||
for (var i = 0; i < max_count; i++) {
|
||||
partners.push(this.partner_by_id[this.partner_sorted[i]]);
|
||||
}
|
||||
return partners;
|
||||
},
|
||||
search_partner: function(query){
|
||||
try {
|
||||
query = query.replace(/[\[\]\(\)\+\*\?\.\-\!\&\^\$\|\~\_\{\}\:\,\\\/]/g,'.');
|
||||
query = query.replace(' ','.+');
|
||||
var re = RegExp("([0-9]+):.*?"+query,"gi");
|
||||
}catch(e){
|
||||
return [];
|
||||
}
|
||||
var results = [];
|
||||
for(var i = 0; i < this.limit; i++){
|
||||
var r = re.exec(this.partner_search_string);
|
||||
if(r){
|
||||
var id = Number(r[1]);
|
||||
results.push(this.get_partner_by_id(id));
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
},
|
||||
/* removes all the data from the database. TODO : being able to selectively remove data */
|
||||
clear: function(stores){
|
||||
for(var i = 0, len = arguments.length; i < len; i++){
|
||||
localStorage.removeItem(this.name + '_' + arguments[i]);
|
||||
}
|
||||
},
|
||||
/* this internal methods returns the count of properties in an object. */
|
||||
_count_props : function(obj){
|
||||
var count = 0;
|
||||
for(var prop in obj){
|
||||
if(obj.hasOwnProperty(prop)){
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
},
|
||||
get_product_by_id: function(id){
|
||||
return this.product_by_id[id];
|
||||
},
|
||||
get_product_by_barcode: function(barcode){
|
||||
if(this.product_by_barcode[barcode]){
|
||||
return this.product_by_barcode[barcode];
|
||||
}
|
||||
var pack = this.packagings_by_barcode[barcode];
|
||||
if(pack){
|
||||
return this.product_by_id[pack.product_tmpl_id[0]];
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
get_product_by_category: function(category_id){
|
||||
var product_ids = this.product_by_category_id[category_id];
|
||||
/* returns the category object from its id. If you pass a list of id as parameters, you get
|
||||
* a list of category objects.
|
||||
*/
|
||||
get_category_by_id: function(categ_id){
|
||||
if(categ_id instanceof Array){
|
||||
var list = [];
|
||||
if (product_ids) {
|
||||
for (var i = 0, len = Math.min(product_ids.length, this.limit); i < len; i++) {
|
||||
list.push(this.product_by_id[product_ids[i]]);
|
||||
for(var i = 0, len = categ_id.length; i < len; i++){
|
||||
var cat = this.category_by_id[categ_id[i]];
|
||||
if(cat){
|
||||
list.push(cat);
|
||||
}else{
|
||||
console.error("get_category_by_id: no category has id:",categ_id[i]);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
},
|
||||
/* returns a list of products with :
|
||||
* - a category that is or is a child of category_id,
|
||||
* - a name, package or barcode containing the query (case insensitive)
|
||||
*/
|
||||
search_product_in_category: function(category_id, query){
|
||||
try {
|
||||
query = query.replace(/[\[\]\(\)\+\*\?\.\-\!\&\^\$\|\~\_\{\}\:\,\\\/]/g,'.');
|
||||
query = query.replace(' ','.+');
|
||||
var re = RegExp("([0-9]+):.*?"+query,"gi");
|
||||
}catch(e){
|
||||
return [];
|
||||
}else{
|
||||
return this.category_by_id[categ_id];
|
||||
}
|
||||
},
|
||||
/* returns a list of the category's child categories ids, or an empty list
|
||||
* if a category has no childs */
|
||||
get_category_childs_ids: function(categ_id){
|
||||
return this.category_childs[categ_id] || [];
|
||||
},
|
||||
/* returns a list of all ancestors (parent, grand-parent, etc) categories ids
|
||||
* starting from the root category to the direct parent */
|
||||
get_category_ancestors_ids: function(categ_id){
|
||||
return this.category_ancestors[categ_id] || [];
|
||||
},
|
||||
/* returns the parent category's id of a category, or the root_category_id if no parent.
|
||||
* the root category is parent of itself. */
|
||||
get_category_parent_id: function(categ_id){
|
||||
return this.category_parent[categ_id] || this.root_category_id;
|
||||
},
|
||||
/* adds categories definitions to the database. categories is a list of categories objects as
|
||||
* returned by the openerp server. Categories must be inserted before the products or the
|
||||
* product/ categories association may (will) not work properly */
|
||||
add_categories: function(categories){
|
||||
var self = this;
|
||||
if(!this.category_by_id[this.root_category_id]){
|
||||
this.category_by_id[this.root_category_id] = {
|
||||
id : this.root_category_id,
|
||||
name : 'Root',
|
||||
};
|
||||
}
|
||||
for(var i=0, len = categories.length; i < len; i++){
|
||||
this.category_by_id[categories[i].id] = categories[i];
|
||||
}
|
||||
len = categories.length;
|
||||
for(i=0; i < len; i++){
|
||||
var cat = categories[i];
|
||||
var parent_id = cat.parent_id[0] || this.root_category_id;
|
||||
this.category_parent[cat.id] = cat.parent_id[0];
|
||||
if(!this.category_childs[parent_id]){
|
||||
this.category_childs[parent_id] = [];
|
||||
}
|
||||
var results = [];
|
||||
for(var i = 0; i < this.limit; i++){
|
||||
var r = re.exec(this.category_search_string[category_id]);
|
||||
if(r){
|
||||
var id = Number(r[1]);
|
||||
results.push(this.get_product_by_id(id));
|
||||
}else{
|
||||
break;
|
||||
this.category_childs[parent_id].push(cat.id);
|
||||
}
|
||||
function make_ancestors(cat_id, ancestors){
|
||||
self.category_ancestors[cat_id] = ancestors;
|
||||
|
||||
ancestors = ancestors.slice(0);
|
||||
ancestors.push(cat_id);
|
||||
|
||||
var childs = self.category_childs[cat_id] || [];
|
||||
for(var i=0, len = childs.length; i < len; i++){
|
||||
make_ancestors(childs[i], ancestors);
|
||||
}
|
||||
}
|
||||
make_ancestors(this.root_category_id, []);
|
||||
},
|
||||
category_contains: function(categ_id, product_id) {
|
||||
var product = this.product_by_id[product_id];
|
||||
if (product) {
|
||||
var cid = product.pos_categ_id[0];
|
||||
while (cid && cid !== categ_id){
|
||||
cid = this.category_parent[cid];
|
||||
}
|
||||
return !!cid;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
/* loads a record store from the database. returns default if nothing is found */
|
||||
load: function(store,deft){
|
||||
if(this.cache[store] !== undefined){
|
||||
return this.cache[store];
|
||||
}
|
||||
var data = localStorage[this.name + '_' + store];
|
||||
if(data !== undefined && data !== ""){
|
||||
data = JSON.parse(data);
|
||||
this.cache[store] = data;
|
||||
return data;
|
||||
}else{
|
||||
return deft;
|
||||
}
|
||||
},
|
||||
/* saves a record store to the database */
|
||||
save: function(store,data){
|
||||
localStorage[this.name + '_' + store] = JSON.stringify(data);
|
||||
this.cache[store] = data;
|
||||
},
|
||||
_product_search_string: function(product){
|
||||
var str = product.display_name;
|
||||
if (product.barcode) {
|
||||
str += '|' + product.barcode;
|
||||
}
|
||||
if (product.default_code) {
|
||||
str += '|' + product.default_code;
|
||||
}
|
||||
if (product.description) {
|
||||
str += '|' + product.description;
|
||||
}
|
||||
if (product.description_sale) {
|
||||
str += '|' + product.description_sale;
|
||||
}
|
||||
var packagings = this.packagings_by_product_tmpl_id[product.product_tmpl_id] || [];
|
||||
for (var i = 0; i < packagings.length; i++) {
|
||||
str += '|' + packagings[i].barcode;
|
||||
}
|
||||
str = product.id + ':' + str.replace(':','') + '\n';
|
||||
return str;
|
||||
},
|
||||
add_products: function(products){
|
||||
var stored_categories = this.product_by_category_id;
|
||||
|
||||
if(!products instanceof Array){
|
||||
products = [products];
|
||||
}
|
||||
for(var i = 0, len = products.length; i < len; i++){
|
||||
var product = products[i];
|
||||
var search_string = this._product_search_string(product);
|
||||
var categ_id = product.pos_categ_id ? product.pos_categ_id[0] : this.root_category_id;
|
||||
product.product_tmpl_id = product.product_tmpl_id[0];
|
||||
if(!stored_categories[categ_id]){
|
||||
stored_categories[categ_id] = [];
|
||||
}
|
||||
stored_categories[categ_id].push(product.id);
|
||||
|
||||
if(this.category_search_string[categ_id] === undefined){
|
||||
this.category_search_string[categ_id] = '';
|
||||
}
|
||||
this.category_search_string[categ_id] += search_string;
|
||||
|
||||
var ancestors = this.get_category_ancestors_ids(categ_id) || [];
|
||||
|
||||
for(var j = 0, jlen = ancestors.length; j < jlen; j++){
|
||||
var ancestor = ancestors[j];
|
||||
if(! stored_categories[ancestor]){
|
||||
stored_categories[ancestor] = [];
|
||||
}
|
||||
}
|
||||
return results;
|
||||
},
|
||||
stored_categories[ancestor].push(product.id);
|
||||
|
||||
/* paid orders */
|
||||
add_order: function(order){
|
||||
var order_id = order.uid;
|
||||
var orders = this.load('orders',[]);
|
||||
|
||||
// if the order was already stored, we overwrite its data
|
||||
for(var i = 0, len = orders.length; i < len; i++){
|
||||
if(orders[i].id === order_id){
|
||||
orders[i].data = order;
|
||||
this.save('orders',orders);
|
||||
return order_id;
|
||||
if( this.category_search_string[ancestor] === undefined){
|
||||
this.category_search_string[ancestor] = '';
|
||||
}
|
||||
this.category_search_string[ancestor] += search_string;
|
||||
}
|
||||
this.product_by_id[product.id] = product;
|
||||
if(product.barcode){
|
||||
this.product_by_barcode[product.barcode] = product;
|
||||
}
|
||||
}
|
||||
},
|
||||
add_packagings: function(packagings){
|
||||
for(var i = 0, len = packagings.length; i < len; i++){
|
||||
var pack = packagings[i];
|
||||
this.packagings_by_id[pack.id] = pack;
|
||||
if(!this.packagings_by_product_tmpl_id[pack.product_tmpl_id[0]]){
|
||||
this.packagings_by_product_tmpl_id[pack.product_tmpl_id[0]] = [];
|
||||
}
|
||||
this.packagings_by_product_tmpl_id[pack.product_tmpl_id[0]].push(pack);
|
||||
if(pack.barcode){
|
||||
this.packagings_by_barcode[pack.barcode] = pack;
|
||||
}
|
||||
}
|
||||
},
|
||||
_partner_search_string: function(partner){
|
||||
var str = partner.name;
|
||||
if(partner.barcode){
|
||||
str += '|' + partner.barcode;
|
||||
}
|
||||
if(partner.address){
|
||||
str += '|' + partner.address;
|
||||
}
|
||||
if(partner.phone){
|
||||
str += '|' + partner.phone.split(' ').join('');
|
||||
}
|
||||
if(partner.mobile){
|
||||
str += '|' + partner.mobile.split(' ').join('');
|
||||
}
|
||||
if(partner.email){
|
||||
str += '|' + partner.email;
|
||||
}
|
||||
str = '' + partner.id + ':' + str.replace(':','') + '\n';
|
||||
return str;
|
||||
},
|
||||
add_partners: function(partners){
|
||||
var updated_count = 0;
|
||||
var new_write_date = '';
|
||||
var partner;
|
||||
for(var i = 0, len = partners.length; i < len; i++){
|
||||
partner = partners[i];
|
||||
|
||||
orders.push({id: order_id, data: order});
|
||||
this.save('orders',orders);
|
||||
return order_id;
|
||||
},
|
||||
remove_order: function(order_id){
|
||||
var orders = this.load('orders',[]);
|
||||
orders = _.filter(orders, function(order){
|
||||
return order.id !== order_id;
|
||||
});
|
||||
this.save('orders',orders);
|
||||
},
|
||||
remove_all_orders: function(){
|
||||
this.save('orders',[]);
|
||||
},
|
||||
get_orders: function(){
|
||||
return this.load('orders',[]);
|
||||
},
|
||||
get_order: function(order_id){
|
||||
var orders = this.get_orders();
|
||||
for(var i = 0, len = orders.length; i < len; i++){
|
||||
if(orders[i].id === order_id){
|
||||
return orders[i];
|
||||
if ( this.partner_write_date &&
|
||||
this.partner_by_id[partner.id] &&
|
||||
new Date(this.partner_write_date).getTime() + 1000 >=
|
||||
new Date(partner.write_date).getTime() ) {
|
||||
// FIXME: The write_date is stored with milisec precision in the database
|
||||
// but the dates we get back are only precise to the second. This means when
|
||||
// you read partners modified strictly after time X, you get back partners that were
|
||||
// modified X - 1 sec ago.
|
||||
continue;
|
||||
} else if ( new_write_date < partner.write_date ) {
|
||||
new_write_date = partner.write_date;
|
||||
}
|
||||
if (!this.partner_by_id[partner.id]) {
|
||||
this.partner_sorted.push(partner.id);
|
||||
}
|
||||
this.partner_by_id[partner.id] = partner;
|
||||
|
||||
updated_count += 1;
|
||||
}
|
||||
|
||||
this.partner_write_date = new_write_date || this.partner_write_date;
|
||||
|
||||
if (updated_count) {
|
||||
// If there were updates, we need to completely
|
||||
// rebuild the search string and the barcode indexing
|
||||
|
||||
this.partner_search_string = "";
|
||||
this.partner_by_barcode = {};
|
||||
|
||||
for (var id in this.partner_by_id) {
|
||||
partner = this.partner_by_id[id];
|
||||
|
||||
if(partner.barcode){
|
||||
this.partner_by_barcode[partner.barcode] = partner;
|
||||
}
|
||||
partner.address = (partner.street || '') +', '+
|
||||
(partner.zip || '') +' '+
|
||||
(partner.city || '') +', '+
|
||||
(partner.country_id[1] || '');
|
||||
this.partner_search_string += this._partner_search_string(partner);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/* working orders */
|
||||
save_unpaid_order: function(order){
|
||||
var order_id = order.uid;
|
||||
var orders = this.load('unpaid_orders',[]);
|
||||
var serialized = order.export_as_JSON();
|
||||
|
||||
for (var i = 0; i < orders.length; i++) {
|
||||
if (orders[i].id === order_id){
|
||||
orders[i].data = serialized;
|
||||
this.save('unpaid_orders',orders);
|
||||
return order_id;
|
||||
}
|
||||
}
|
||||
return updated_count;
|
||||
},
|
||||
get_partner_write_date: function(){
|
||||
return this.partner_write_date || "1970-01-01 00:00:00";
|
||||
},
|
||||
get_partner_by_id: function(id){
|
||||
return this.partner_by_id[id];
|
||||
},
|
||||
get_partner_by_barcode: function(barcode){
|
||||
return this.partner_by_barcode[barcode];
|
||||
},
|
||||
get_partners_sorted: function(max_count){
|
||||
max_count = max_count ? Math.min(this.partner_sorted.length, max_count) : this.partner_sorted.length;
|
||||
var partners = [];
|
||||
for (var i = 0; i < max_count; i++) {
|
||||
partners.push(this.partner_by_id[this.partner_sorted[i]]);
|
||||
}
|
||||
return partners;
|
||||
},
|
||||
search_partner: function(query){
|
||||
try {
|
||||
query = query.replace(/[\[\]\(\)\+\*\?\.\-\!\&\^\$\|\~\_\{\}\:\,\\\/]/g,'.');
|
||||
query = query.replace(' ','.+');
|
||||
var re = RegExp("([0-9]+):.*?"+query,"gi");
|
||||
}catch(e){
|
||||
return [];
|
||||
}
|
||||
var results = [];
|
||||
for(var i = 0; i < this.limit; i++){
|
||||
var r = re.exec(this.partner_search_string);
|
||||
if(r){
|
||||
var id = Number(r[1]);
|
||||
results.push(this.get_partner_by_id(id));
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
|
||||
orders.push({id: order_id, data: serialized});
|
||||
this.save('unpaid_orders',orders);
|
||||
return order_id;
|
||||
},
|
||||
remove_unpaid_order: function(order){
|
||||
var orders = this.load('unpaid_orders',[]);
|
||||
orders = _.filter(orders, function(o){
|
||||
return o.id !== order.uid;
|
||||
});
|
||||
this.save('unpaid_orders',orders);
|
||||
},
|
||||
remove_all_unpaid_orders: function(){
|
||||
this.save('unpaid_orders',[]);
|
||||
},
|
||||
get_unpaid_orders: function(){
|
||||
var saved = this.load('unpaid_orders',[]);
|
||||
var orders = [];
|
||||
for (var i = 0; i < saved.length; i++) {
|
||||
orders.push(saved[i].data);
|
||||
}
|
||||
return results;
|
||||
},
|
||||
/* removes all the data from the database. TODO : being able to selectively remove data */
|
||||
clear: function(){
|
||||
for(var i = 0, len = arguments.length; i < len; i++){
|
||||
localStorage.removeItem(this.name + '_' + arguments[i]);
|
||||
}
|
||||
},
|
||||
/* this internal methods returns the count of properties in an object. */
|
||||
_count_props : function(obj){
|
||||
var count = 0;
|
||||
for(var prop in obj){
|
||||
if(obj.hasOwnProperty(prop)){
|
||||
count++;
|
||||
}
|
||||
return orders;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
return count;
|
||||
},
|
||||
get_product_by_id: function(id){
|
||||
return this.product_by_id[id];
|
||||
},
|
||||
get_product_by_barcode: function(barcode){
|
||||
if(this.product_by_barcode[barcode]){
|
||||
return this.product_by_barcode[barcode];
|
||||
}
|
||||
var pack = this.packagings_by_barcode[barcode];
|
||||
if(pack){
|
||||
return this.product_by_id[pack.product_tmpl_id[0]];
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
get_product_by_category: function(category_id){
|
||||
var product_ids = this.product_by_category_id[category_id];
|
||||
var list = [];
|
||||
if (product_ids) {
|
||||
for (var i = 0, len = Math.min(product_ids.length, this.limit); i < len; i++) {
|
||||
list.push(this.product_by_id[product_ids[i]]);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
},
|
||||
/* returns a list of products with :
|
||||
* - a category that is or is a child of category_id,
|
||||
* - a name, package or barcode containing the query (case insensitive)
|
||||
*/
|
||||
search_product_in_category: function(category_id, query){
|
||||
try {
|
||||
query = query.replace(/[\[\]\(\)\+\*\?\.\-\!\&\^\$\|\~\_\{\}\:\,\\\/]/g,'.');
|
||||
query = query.replace(' ','.+');
|
||||
var re = RegExp("([0-9]+):.*?"+query,"gi");
|
||||
}catch(e){
|
||||
return [];
|
||||
}
|
||||
var results = [];
|
||||
for(var i = 0; i < this.limit; i++){
|
||||
var r = re.exec(this.category_search_string[category_id]);
|
||||
if(r){
|
||||
var id = Number(r[1]);
|
||||
results.push(this.get_product_by_id(id));
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
},
|
||||
|
||||
/* paid orders */
|
||||
add_order: function(order){
|
||||
var order_id = order.uid;
|
||||
var orders = this.load('orders',[]);
|
||||
|
||||
// if the order was already stored, we overwrite its data
|
||||
for(var i = 0, len = orders.length; i < len; i++){
|
||||
if(orders[i].id === order_id){
|
||||
orders[i].data = order;
|
||||
this.save('orders',orders);
|
||||
return order_id;
|
||||
}
|
||||
}
|
||||
|
||||
orders.push({id: order_id, data: order});
|
||||
this.save('orders',orders);
|
||||
return order_id;
|
||||
},
|
||||
remove_order: function(order_id){
|
||||
var orders = this.load('orders',[]);
|
||||
orders = _.filter(orders, function(order){
|
||||
return order.id !== order_id;
|
||||
});
|
||||
this.save('orders',orders);
|
||||
},
|
||||
remove_all_orders: function(){
|
||||
this.save('orders',[]);
|
||||
},
|
||||
get_orders: function(){
|
||||
return this.load('orders',[]);
|
||||
},
|
||||
get_order: function(order_id){
|
||||
var orders = this.get_orders();
|
||||
for(var i = 0, len = orders.length; i < len; i++){
|
||||
if(orders[i].id === order_id){
|
||||
return orders[i];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/* working orders */
|
||||
save_unpaid_order: function(order){
|
||||
var order_id = order.uid;
|
||||
var orders = this.load('unpaid_orders',[]);
|
||||
var serialized = order.export_as_JSON();
|
||||
|
||||
for (var i = 0; i < orders.length; i++) {
|
||||
if (orders[i].id === order_id){
|
||||
orders[i].data = serialized;
|
||||
this.save('unpaid_orders',orders);
|
||||
return order_id;
|
||||
}
|
||||
}
|
||||
|
||||
orders.push({id: order_id, data: serialized});
|
||||
this.save('unpaid_orders',orders);
|
||||
return order_id;
|
||||
},
|
||||
remove_unpaid_order: function(order){
|
||||
var orders = this.load('unpaid_orders',[]);
|
||||
orders = _.filter(orders, function(o){
|
||||
return o.id !== order.uid;
|
||||
});
|
||||
this.save('unpaid_orders',orders);
|
||||
},
|
||||
remove_all_unpaid_orders: function(){
|
||||
this.save('unpaid_orders',[]);
|
||||
},
|
||||
get_unpaid_orders: function(){
|
||||
var saved = this.load('unpaid_orders',[]);
|
||||
var orders = [];
|
||||
for (var i = 0; i < saved.length; i++) {
|
||||
orders.push(saved[i].data);
|
||||
}
|
||||
return orders;
|
||||
},
|
||||
});
|
||||
|
||||
return PosDB;
|
||||
|
||||
});
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,366 +1,371 @@
|
||||
|
||||
odoo.define('point_of_sale.gui', ['web.core', 'web.Model'], function (require) {
|
||||
"use strict";
|
||||
// this file contains the Gui, which is the pos 'controller'.
|
||||
// It contains high level methods to manipulate the interface
|
||||
// such as changing between screens, creating popups, etc.
|
||||
//
|
||||
// it is available to all pos objects trough the '.gui' field.
|
||||
|
||||
openerp.point_of_sale.load_gui = function load_gui(instance, module) {
|
||||
"use strict";
|
||||
var core = require('web.core');
|
||||
var Model = require('web.Model');
|
||||
|
||||
var QWeb = instance.web.qweb;
|
||||
var _t = instance.web._t;
|
||||
var _t = core._t;
|
||||
|
||||
module.Gui = instance.web.Class.extend({
|
||||
screen_classes: [],
|
||||
popup_classes: [],
|
||||
init: function(options){
|
||||
var self = this;
|
||||
this.pos = options.pos;
|
||||
this.chrome = options.chrome;
|
||||
this.screen_instances = {};
|
||||
this.popup_instances = {};
|
||||
this.default_screen = null;
|
||||
this.startup_screen = null;
|
||||
this.current_popup = null;
|
||||
this.current_screen = null;
|
||||
var Gui = core.Class.extend({
|
||||
screen_classes: [],
|
||||
popup_classes: [],
|
||||
init: function(options){
|
||||
var self = this;
|
||||
this.pos = options.pos;
|
||||
this.chrome = options.chrome;
|
||||
this.screen_instances = {};
|
||||
this.popup_instances = {};
|
||||
this.default_screen = null;
|
||||
this.startup_screen = null;
|
||||
this.current_popup = null;
|
||||
this.current_screen = null;
|
||||
|
||||
this.chrome.ready.then(function(){
|
||||
var order = self.pos.get_order();
|
||||
if (order) {
|
||||
self.show_saved_screen(order);
|
||||
} else {
|
||||
self.show_screen(self.startup_screen);
|
||||
}
|
||||
self.pos.bind('change:selectedOrder', function(){
|
||||
self.show_saved_screen(self.pos.get_order());
|
||||
this.chrome.ready.then(function(){
|
||||
var order = self.pos.get_order();
|
||||
if (order) {
|
||||
self.show_saved_screen(order);
|
||||
} else {
|
||||
self.show_screen(self.startup_screen);
|
||||
}
|
||||
self.pos.bind('change:selectedOrder', function(){
|
||||
self.show_saved_screen(self.pos.get_order());
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/* ---- Gui: SCREEN MANIPULATION ---- */
|
||||
|
||||
// register a screen widget to the gui,
|
||||
// it must have been inserted into the dom.
|
||||
add_screen: function(name, screen){
|
||||
screen.hide();
|
||||
this.screen_instances[name] = screen;
|
||||
},
|
||||
|
||||
// sets the screen that will be displayed
|
||||
// for new orders
|
||||
set_default_screen: function(name){
|
||||
this.default_screen = name;
|
||||
},
|
||||
|
||||
// sets the screen that will be displayed
|
||||
// when no orders are present
|
||||
set_startup_screen: function(name) {
|
||||
this.startup_screen = name;
|
||||
},
|
||||
|
||||
// display the screen saved in an order,
|
||||
// called when the user changes the current order
|
||||
// no screen saved ? -> display default_screen
|
||||
// no order ? -> display startup_screen
|
||||
show_saved_screen: function(order,options) {
|
||||
options = options || {};
|
||||
this.close_popup();
|
||||
if (order) {
|
||||
this.show_screen(order.get_screen_data('screen') ||
|
||||
options.default_screen ||
|
||||
this.default_screen,
|
||||
null,'refresh');
|
||||
} else {
|
||||
this.show_screen(this.startup_screen);
|
||||
}
|
||||
},
|
||||
|
||||
// display a screen.
|
||||
// If there is an order, the screen will be saved in the order
|
||||
// - params: used to load a screen with parameters, for
|
||||
// example loading a 'product_details' screen for a specific product.
|
||||
// - refresh: if you want the screen to cycle trough show / hide even
|
||||
// if you are already on the same screen.
|
||||
show_screen: function(screen_name,params,refresh) {
|
||||
var screen = this.screen_instances[screen_name];
|
||||
if (!screen) {
|
||||
console.error("ERROR: show_screen("+screen_name+") : screen not found");
|
||||
}
|
||||
|
||||
this.close_popup();
|
||||
|
||||
var order = this.pos.get_order();
|
||||
if (order) {
|
||||
var old_screen_name = order.get_screen_data('screen');
|
||||
|
||||
order.set_screen_data('screen',screen_name);
|
||||
|
||||
if(params){
|
||||
order.set_screen_data('params',params);
|
||||
}
|
||||
|
||||
if( screen_name !== old_screen_name ){
|
||||
order.set_screen_data('previous-screen',old_screen_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (refresh || screen !== this.current_screen) {
|
||||
if (this.current_screen) {
|
||||
this.current_screen.close();
|
||||
this.current_screen.hide();
|
||||
}
|
||||
this.current_screen = screen;
|
||||
this.current_screen.show();
|
||||
}
|
||||
},
|
||||
|
||||
// returns the current screen.
|
||||
get_current_screen: function() {
|
||||
return this.pos.get_order() ? ( this.pos.get_order().get_screen_data('screen') || this.default_screen ) : this.startup_screen;
|
||||
},
|
||||
|
||||
// goes to the previous screen (as specified in the order). The history only
|
||||
// goes 1 deep ...
|
||||
back: function() {
|
||||
var previous = this.pos.get_order().get_screen_data('previous-screen');
|
||||
if (previous) {
|
||||
this.show_screen(previous);
|
||||
}
|
||||
},
|
||||
|
||||
// returns the parameter specified when this screen was displayed
|
||||
get_current_screen_param: function(param) {
|
||||
if (this.pos.get_order()) {
|
||||
var params = this.pos.get_order().get_screen_data('params');
|
||||
return params ? params[param] : undefined;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
|
||||
/* ---- Gui: POPUP MANIPULATION ---- */
|
||||
|
||||
// registers a new popup in the GUI.
|
||||
// the popup must have been previously inserted
|
||||
// into the dom.
|
||||
add_popup: function(name, popup) {
|
||||
popup.hide();
|
||||
this.popup_instances[name] = popup;
|
||||
},
|
||||
|
||||
// displays a popup. Popup do not stack,
|
||||
// are not remembered by the order, and are
|
||||
// closed by screen changes or new popups.
|
||||
show_popup: function(name,options) {
|
||||
if (this.current_popup) {
|
||||
this.close_popup();
|
||||
}
|
||||
this.current_popup = this.popup_instances[name];
|
||||
this.current_popup.show(options);
|
||||
},
|
||||
|
||||
// close the current popup.
|
||||
close_popup: function() {
|
||||
if (this.current_popup) {
|
||||
this.current_popup.close();
|
||||
this.current_popup.hide();
|
||||
this.current_popup = null;
|
||||
}
|
||||
},
|
||||
|
||||
// is there an active popup ?
|
||||
has_popup: function() {
|
||||
return !!this.current_popup;
|
||||
},
|
||||
|
||||
/* ---- Gui: ACCESS CONTROL ---- */
|
||||
|
||||
// A Generic UI that allow to select a user from a list.
|
||||
// It returns a deferred that resolves with the selected user
|
||||
// upon success. Several options are available :
|
||||
// - security: passwords will be asked
|
||||
// - only_managers: restricts the list to managers
|
||||
// - current_user: password will not be asked if this
|
||||
// user is selected.
|
||||
// - title: The title of the user selection list.
|
||||
select_user: function(options){
|
||||
options = options || {};
|
||||
var self = this;
|
||||
var def = new $.Deferred();
|
||||
|
||||
var list = [];
|
||||
for (var i = 0; i < this.pos.users.length; i++) {
|
||||
var user = this.pos.users[i];
|
||||
if (!options.only_managers || user.role === 'manager') {
|
||||
list.push({
|
||||
'label': user.name,
|
||||
'item': user,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/* ---- Gui: SCREEN MANIPULATION ---- */
|
||||
|
||||
// register a screen widget to the gui,
|
||||
// it must have been inserted into the dom.
|
||||
add_screen: function(name, screen){
|
||||
screen.hide();
|
||||
this.screen_instances[name] = screen;
|
||||
},
|
||||
|
||||
// sets the screen that will be displayed
|
||||
// for new orders
|
||||
set_default_screen: function(name){
|
||||
this.default_screen = name;
|
||||
},
|
||||
|
||||
// sets the screen that will be displayed
|
||||
// when no orders are present
|
||||
set_startup_screen: function(name) {
|
||||
this.startup_screen = name;
|
||||
},
|
||||
|
||||
// display the screen saved in an order,
|
||||
// called when the user changes the current order
|
||||
// no screen saved ? -> display default_screen
|
||||
// no order ? -> display startup_screen
|
||||
show_saved_screen: function(order,options) {
|
||||
options = options || {};
|
||||
this.close_popup();
|
||||
if (order) {
|
||||
this.show_screen(order.get_screen_data('screen') ||
|
||||
options.default_screen ||
|
||||
this.default_screen,
|
||||
null,'refresh');
|
||||
} else {
|
||||
this.show_screen(this.startup_screen);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// display a screen.
|
||||
// If there is an order, the screen will be saved in the order
|
||||
// - params: used to load a screen with parameters, for
|
||||
// example loading a 'product_details' screen for a specific product.
|
||||
// - refresh: if you want the screen to cycle trough show / hide even
|
||||
// if you are already on the same screen.
|
||||
show_screen: function(screen_name,params,refresh) {
|
||||
var screen = this.screen_instances[screen_name];
|
||||
if (!screen) {
|
||||
console.error("ERROR: show_screen("+screen_name+") : screen not found");
|
||||
}
|
||||
this.show_popup('selection',{
|
||||
'title': options.title || _t('Select User'),
|
||||
list: list,
|
||||
confirm: function(user){ def.resolve(user); },
|
||||
cancel: function(){ def.reject(); },
|
||||
});
|
||||
|
||||
this.close_popup();
|
||||
|
||||
var order = this.pos.get_order();
|
||||
if (order) {
|
||||
var old_screen_name = order.get_screen_data('screen');
|
||||
|
||||
order.set_screen_data('screen',screen_name);
|
||||
|
||||
if(params){
|
||||
order.set_screen_data('params',params);
|
||||
}
|
||||
|
||||
if( screen_name !== old_screen_name ){
|
||||
order.set_screen_data('previous-screen',old_screen_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (refresh || screen !== this.current_screen) {
|
||||
if (this.current_screen) {
|
||||
this.current_screen.close();
|
||||
this.current_screen.hide();
|
||||
}
|
||||
this.current_screen = screen;
|
||||
this.current_screen.show();
|
||||
}
|
||||
},
|
||||
|
||||
// returns the current screen.
|
||||
get_current_screen: function() {
|
||||
return this.pos.get_order() ? ( this.pos.get_order().get_screen_data('screen') || this.default_screen ) : this.startup_screen;
|
||||
},
|
||||
|
||||
// goes to the previous screen (as specified in the order). The history only
|
||||
// goes 1 deep ...
|
||||
back: function() {
|
||||
var previous = this.pos.get_order().get_screen_data('previous-screen');
|
||||
if (previous) {
|
||||
this.show_screen(previous);
|
||||
}
|
||||
},
|
||||
|
||||
// returns the parameter specified when this screen was displayed
|
||||
get_current_screen_param: function(param) {
|
||||
if (this.pos.get_order()) {
|
||||
var params = this.pos.get_order().get_screen_data('params');
|
||||
return params ? params[param] : undefined;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
|
||||
/* ---- Gui: POPUP MANIPULATION ---- */
|
||||
|
||||
// registers a new popup in the GUI.
|
||||
// the popup must have been previously inserted
|
||||
// into the dom.
|
||||
add_popup: function(name, popup) {
|
||||
popup.hide();
|
||||
this.popup_instances[name] = popup;
|
||||
},
|
||||
|
||||
// displays a popup. Popup do not stack,
|
||||
// are not remembered by the order, and are
|
||||
// closed by screen changes or new popups.
|
||||
show_popup: function(name,options) {
|
||||
if (this.current_popup) {
|
||||
this.close_popup();
|
||||
}
|
||||
this.current_popup = this.popup_instances[name];
|
||||
this.current_popup.show(options);
|
||||
},
|
||||
|
||||
// close the current popup.
|
||||
close_popup: function() {
|
||||
if (this.current_popup) {
|
||||
this.current_popup.close();
|
||||
this.current_popup.hide();
|
||||
this.current_popup = null;
|
||||
}
|
||||
},
|
||||
|
||||
// is there an active popup ?
|
||||
has_popup: function() {
|
||||
return !!this.current_popup;
|
||||
},
|
||||
|
||||
/* ---- Gui: ACCESS CONTROL ---- */
|
||||
|
||||
// A Generic UI that allow to select a user from a list.
|
||||
// It returns a deferred that resolves with the selected user
|
||||
// upon success. Several options are available :
|
||||
// - security: passwords will be asked
|
||||
// - only_managers: restricts the list to managers
|
||||
// - current_user: password will not be asked if this
|
||||
// user is selected.
|
||||
// - title: The title of the user selection list.
|
||||
select_user: function(options){
|
||||
options = options || {};
|
||||
var self = this;
|
||||
var def = new $.Deferred();
|
||||
|
||||
var list = [];
|
||||
for (var i = 0; i < this.pos.users.length; i++) {
|
||||
var user = this.pos.users[i];
|
||||
if (!options.only_managers || user.role === 'manager') {
|
||||
list.push({
|
||||
'label': user.name,
|
||||
'item': user,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.show_popup('selection',{
|
||||
'title': options.title || _t('Select User'),
|
||||
list: list,
|
||||
confirm: function(user){ def.resolve(user); },
|
||||
cancel: function(){ def.reject(); },
|
||||
});
|
||||
|
||||
return def.then(function(user){
|
||||
if (options.security && user !== options.current_user && user.pos_security_pin) {
|
||||
return self.ask_password(user.pos_security_pin).then(function(){
|
||||
return user;
|
||||
});
|
||||
} else {
|
||||
return def.then(function(user){
|
||||
if (options.security && user !== options.current_user && user.pos_security_pin) {
|
||||
return self.ask_password(user.pos_security_pin).then(function(){
|
||||
return user;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Ask for a password, and checks if it this
|
||||
// the same as specified by the function call.
|
||||
// returns a deferred that resolves on success,
|
||||
// fails on failure.
|
||||
ask_password: function(password) {
|
||||
var self = this;
|
||||
var ret = new $.Deferred();
|
||||
if (password) {
|
||||
this.gui.show_popup('password',{
|
||||
'title': _t('Password ?'),
|
||||
confirm: function(pw) {
|
||||
if (pw !== password) {
|
||||
self.show_popup('error',_t('Incorrect Password'));
|
||||
ret.reject();
|
||||
} else {
|
||||
ret.resolve();
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
ret.resolve();
|
||||
return user;
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// checks if the current user (or the user provided) has manager
|
||||
// access rights. If not, a popup is shown allowing the user to
|
||||
// temporarily login as an administrator.
|
||||
// This method returns a deferred, that succeeds with the
|
||||
// manager user when the login is successfull.
|
||||
sudo: function(user){
|
||||
var def = new $.Deferred();
|
||||
user = user || this.pos.get_cashier();
|
||||
|
||||
if (user.role === 'manager') {
|
||||
return new $.Deferred().resolve(user);
|
||||
} else {
|
||||
return this.select_user({
|
||||
security: true,
|
||||
only_managers: true,
|
||||
title: _t('Login as a Manager'),
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/* ---- Gui: CLOSING THE POINT OF SALE ---- */
|
||||
|
||||
close: function() {
|
||||
var self = this;
|
||||
this.chrome.loading_show();
|
||||
this.chrome.loading_message(_t('Closing ...'));
|
||||
|
||||
this.pos.push_order().then(function(){
|
||||
return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_client_pos_menu']], ['res_id'])
|
||||
.pipe(function(res) {
|
||||
window.location = '/web#action=' + res[0]['res_id'];
|
||||
},function(err,event) {
|
||||
event.preventDefault();
|
||||
self.show_popup('error',{
|
||||
'title': _t('Could not close the point of sale.'),
|
||||
'body': _t('Your internet connection is probably down.'),
|
||||
});
|
||||
self.chrome.widget.close_button.renderElement();
|
||||
});
|
||||
// Ask for a password, and checks if it this
|
||||
// the same as specified by the function call.
|
||||
// returns a deferred that resolves on success,
|
||||
// fails on failure.
|
||||
ask_password: function(password) {
|
||||
var self = this;
|
||||
var ret = new $.Deferred();
|
||||
if (password) {
|
||||
this.gui.show_popup('password',{
|
||||
'title': _t('Password ?'),
|
||||
confirm: function(pw) {
|
||||
if (pw !== password) {
|
||||
self.show_popup('error',_t('Incorrect Password'));
|
||||
ret.reject();
|
||||
} else {
|
||||
ret.resolve();
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
} else {
|
||||
ret.resolve();
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
/* ---- Gui: SOUND ---- */
|
||||
// checks if the current user (or the user provided) has manager
|
||||
// access rights. If not, a popup is shown allowing the user to
|
||||
// temporarily login as an administrator.
|
||||
// This method returns a deferred, that succeeds with the
|
||||
// manager user when the login is successfull.
|
||||
sudo: function(user){
|
||||
user = user || this.pos.get_cashier();
|
||||
|
||||
play_sound: function(sound) {
|
||||
var src = '';
|
||||
if (sound === 'error') {
|
||||
src = "/point_of_sale/static/src/sounds/error.wav";
|
||||
} else if (sound === 'bell') {
|
||||
src = "/point_of_sale/static/src/sounds/bell.wav";
|
||||
if (user.role === 'manager') {
|
||||
return new $.Deferred().resolve(user);
|
||||
} else {
|
||||
return this.select_user({
|
||||
security: true,
|
||||
only_managers: true,
|
||||
title: _t('Login as a Manager'),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/* ---- Gui: CLOSING THE POINT OF SALE ---- */
|
||||
|
||||
close: function() {
|
||||
var self = this;
|
||||
this.chrome.loading_show();
|
||||
this.chrome.loading_message(_t('Closing ...'));
|
||||
|
||||
this.pos.push_order().then(function(){
|
||||
return new Model("ir.model.data").get_func("search_read")([['name', '=', 'action_client_pos_menu']], ['res_id'])
|
||||
.pipe(function(res) {
|
||||
window.location = '/web#action=' + res[0]['res_id'];
|
||||
},function(err,event) {
|
||||
event.preventDefault();
|
||||
self.show_popup('error',{
|
||||
'title': _t('Could not close the point of sale.'),
|
||||
'body': _t('Your internet connection is probably down.'),
|
||||
});
|
||||
self.chrome.widget.close_button.renderElement();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/* ---- Gui: SOUND ---- */
|
||||
|
||||
play_sound: function(sound) {
|
||||
var src = '';
|
||||
if (sound === 'error') {
|
||||
src = "/point_of_sale/static/src/sounds/error.wav";
|
||||
} else if (sound === 'bell') {
|
||||
src = "/point_of_sale/static/src/sounds/bell.wav";
|
||||
} else {
|
||||
console.error('Unknown sound: ',sound);
|
||||
return;
|
||||
}
|
||||
$('body').append('<audio src="'+src+'" autoplay="true"></audio>');
|
||||
},
|
||||
|
||||
/* ---- Gui: KEYBOARD INPUT ---- */
|
||||
|
||||
// This is a helper to handle numpad keyboard input.
|
||||
// - buffer: an empty or number string
|
||||
// - input: '[0-9],'+','-','.','CLEAR','BACKSPACE'
|
||||
// - options: 'firstinput' -> will clear buffer if
|
||||
// input is '[0-9]' or '.'
|
||||
// returns the new buffer containing the modifications
|
||||
// (the original is not touched)
|
||||
numpad_input: function(buffer, input, options) {
|
||||
var newbuf = buffer.slice(0);
|
||||
options = options || {};
|
||||
|
||||
if (input === '.') {
|
||||
if (options.firstinput) {
|
||||
newbuf = "0.";
|
||||
}else if (!newbuf.length || newbuf === '-') {
|
||||
newbuf += "0.";
|
||||
} else if (newbuf.indexOf('.') < 0){
|
||||
newbuf = newbuf + '.';
|
||||
}
|
||||
} else if (input === 'CLEAR') {
|
||||
newbuf = "";
|
||||
} else if (input === 'BACKSPACE') {
|
||||
newbuf = newbuf.substring(0,newbuf.length - 1);
|
||||
} else if (input === '+') {
|
||||
if ( newbuf[0] === '-' ) {
|
||||
newbuf = newbuf.substring(1,newbuf.length);
|
||||
}
|
||||
} else if (input === '-') {
|
||||
if ( newbuf[0] === '-' ) {
|
||||
newbuf = newbuf.substring(1,newbuf.length);
|
||||
} else {
|
||||
console.error('Unknown sound: ',sound);
|
||||
return;
|
||||
newbuf = '-' + newbuf;
|
||||
}
|
||||
$('body').append('<audio src="'+src+'" autoplay="true"></audio>');
|
||||
},
|
||||
|
||||
/* ---- Gui: KEYBOARD INPUT ---- */
|
||||
|
||||
// This is a helper to handle numpad keyboard input.
|
||||
// - buffer: an empty or number string
|
||||
// - input: '[0-9],'+','-','.','CLEAR','BACKSPACE'
|
||||
// - options: 'firstinput' -> will clear buffer if
|
||||
// input is '[0-9]' or '.'
|
||||
// returns the new buffer containing the modifications
|
||||
// (the original is not touched)
|
||||
numpad_input: function(buffer, input, options) {
|
||||
var options = options || {};
|
||||
var newbuf = buffer.slice(0);
|
||||
|
||||
if (input === '.') {
|
||||
if (options.firstinput) {
|
||||
newbuf = "0.";
|
||||
}else if (!newbuf.length || newbuf === '-') {
|
||||
newbuf += "0.";
|
||||
} else if (newbuf.indexOf('.') < 0){
|
||||
newbuf = newbuf + '.';
|
||||
}
|
||||
} else if (input === 'CLEAR') {
|
||||
newbuf = "";
|
||||
} else if (input === 'BACKSPACE') {
|
||||
newbuf = newbuf.substring(0,newbuf.length - 1);
|
||||
} else if (input === '+') {
|
||||
if ( newbuf[0] === '-' ) {
|
||||
newbuf = newbuf.substring(1,newbuf.length);
|
||||
}
|
||||
} else if (input === '-') {
|
||||
if ( newbuf[0] === '-' ) {
|
||||
newbuf = newbuf.substring(1,newbuf.length);
|
||||
} else {
|
||||
newbuf = '-' + newbuf;
|
||||
}
|
||||
} else if (input[0] === '+' && !isNaN(parseFloat(input))) {
|
||||
newbuf = '' + ((parseFloat(newbuf) || 0) + parseFloat(input));
|
||||
} else if (!isNaN(parseInt(input))) {
|
||||
if (options.firstinput) {
|
||||
newbuf = '' + input;
|
||||
} else {
|
||||
newbuf += input;
|
||||
}
|
||||
} else if (input[0] === '+' && !isNaN(parseFloat(input))) {
|
||||
newbuf = '' + ((parseFloat(newbuf) || 0) + parseFloat(input));
|
||||
} else if (!isNaN(parseInt(input))) {
|
||||
if (options.firstinput) {
|
||||
newbuf = '' + input;
|
||||
} else {
|
||||
newbuf += input;
|
||||
}
|
||||
}
|
||||
|
||||
// End of input buffer at 12 characters.
|
||||
if (newbuf.length > buffer.length && newbuf.length > 12) {
|
||||
this.play_sound('bell');
|
||||
return buffer.slice(0);
|
||||
}
|
||||
// End of input buffer at 12 characters.
|
||||
if (newbuf.length > buffer.length && newbuf.length > 12) {
|
||||
this.play_sound('bell');
|
||||
return buffer.slice(0);
|
||||
}
|
||||
|
||||
return newbuf;
|
||||
},
|
||||
});
|
||||
return newbuf;
|
||||
},
|
||||
});
|
||||
|
||||
module.Gui.define_screen = function (classe) {
|
||||
module.Gui.prototype.screen_classes.push(classe);
|
||||
};
|
||||
var define_screen = function (classe) {
|
||||
Gui.prototype.screen_classes.push(classe);
|
||||
};
|
||||
|
||||
module.Gui.define_popup = function (classe) {
|
||||
module.Gui.prototype.popup_classes.push(classe);
|
||||
};
|
||||
};
|
||||
var define_popup = function (classe) {
|
||||
Gui.prototype.popup_classes.push(classe);
|
||||
};
|
||||
|
||||
return {
|
||||
Gui: Gui,
|
||||
define_screen: define_screen,
|
||||
define_popup: define_popup,
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@@ -1,201 +1,207 @@
|
||||
openerp.point_of_sale.load_keyboard = function load_keyboard(instance, module){ //module is instance.point_of_sale
|
||||
"use strict";
|
||||
odoo.define('point_of_sale.keyboard', ['web.Widget'], function (require) {
|
||||
"use strict";
|
||||
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
// ---------- OnScreen Keyboard Widget ----------
|
||||
// A Widget that displays an onscreen keyboard.
|
||||
// There are two options when creating the widget :
|
||||
//
|
||||
// * 'keyboard_model' : 'simple' (default) | 'full'
|
||||
// The 'full' emulates a PC keyboard, while 'simple' emulates an 'android' one.
|
||||
//
|
||||
// * 'input_selector : (default: '.searchbox input')
|
||||
// defines the dom element that the keyboard will write to.
|
||||
//
|
||||
// The widget is initially hidden. It can be shown with this.show(), and is
|
||||
// automatically shown when the input_selector gets focused.
|
||||
|
||||
var OnscreenKeyboardWidget = Widget.extend({
|
||||
template: 'OnscreenKeyboardSimple',
|
||||
init: function(parent, options){
|
||||
this._super(parent,options);
|
||||
options = options || {};
|
||||
|
||||
this.keyboard_model = options.keyboard_model || 'simple';
|
||||
if(this.keyboard_model === 'full'){
|
||||
this.template = 'OnscreenKeyboardFull';
|
||||
}
|
||||
|
||||
this.input_selector = options.input_selector || '.searchbox input';
|
||||
this.$target = null;
|
||||
|
||||
//Keyboard state
|
||||
this.capslock = false;
|
||||
this.shift = false;
|
||||
this.numlock = false;
|
||||
},
|
||||
|
||||
// ---------- OnScreen Keyboard Widget ----------
|
||||
// A Widget that displays an onscreen keyboard.
|
||||
// There are two options when creating the widget :
|
||||
//
|
||||
// * 'keyboard_model' : 'simple' (default) | 'full'
|
||||
// The 'full' emulates a PC keyboard, while 'simple' emulates an 'android' one.
|
||||
//
|
||||
// * 'input_selector : (default: '.searchbox input')
|
||||
// defines the dom element that the keyboard will write to.
|
||||
//
|
||||
// The widget is initially hidden. It can be shown with this.show(), and is
|
||||
// automatically shown when the input_selector gets focused.
|
||||
connect : function(target){
|
||||
var self = this;
|
||||
this.$target = $(target);
|
||||
this.$target.focus(function(){self.show();});
|
||||
},
|
||||
generateEvent: function(type,key){
|
||||
var event = document.createEvent("KeyboardEvent");
|
||||
var initMethod = event.initKeyboardEvent ? 'initKeyboardEvent' : 'initKeyEvent';
|
||||
event[initMethod]( type,
|
||||
true, //bubbles
|
||||
true, //cancelable
|
||||
window, //viewArg
|
||||
false, //ctrl
|
||||
false, //alt
|
||||
false, //shift
|
||||
false, //meta
|
||||
((typeof key.code === 'undefined') ? key.char.charCodeAt(0) : key.code),
|
||||
((typeof key.char === 'undefined') ? String.fromCharCode(key.code) : key.char)
|
||||
);
|
||||
return event;
|
||||
|
||||
module.OnscreenKeyboardWidget = instance.web.Widget.extend({
|
||||
template: 'OnscreenKeyboardSimple',
|
||||
init: function(parent, options){
|
||||
var self = this;
|
||||
this._super(parent,options);
|
||||
options = options || {};
|
||||
},
|
||||
|
||||
this.keyboard_model = options.keyboard_model || 'simple';
|
||||
if(this.keyboard_model === 'full'){
|
||||
this.template = 'OnscreenKeyboardFull';
|
||||
}
|
||||
|
||||
this.input_selector = options.input_selector || '.searchbox input';
|
||||
this.$target = null;
|
||||
|
||||
//Keyboard state
|
||||
this.capslock = false;
|
||||
this.shift = false;
|
||||
this.numlock = false;
|
||||
},
|
||||
|
||||
connect : function(target){
|
||||
var self = this;
|
||||
this.$target = $(target);
|
||||
this.$target.focus(function(){self.show();});
|
||||
},
|
||||
generateEvent: function(type,key){
|
||||
var event = document.createEvent("KeyboardEvent");
|
||||
var initMethod = event.initKeyboardEvent ? 'initKeyboardEvent' : 'initKeyEvent';
|
||||
event[initMethod]( type,
|
||||
true, //bubbles
|
||||
true, //cancelable
|
||||
window, //viewArg
|
||||
false, //ctrl
|
||||
false, //alt
|
||||
false, //shift
|
||||
false, //meta
|
||||
((typeof key.code === 'undefined') ? key.char.charCodeAt(0) : key.code),
|
||||
((typeof key.char === 'undefined') ? String.fromCharCode(key.code) : key.char)
|
||||
);
|
||||
return event;
|
||||
|
||||
},
|
||||
|
||||
// Write a character to the input zone
|
||||
writeCharacter: function(character){
|
||||
var input = this.$target[0];
|
||||
input.dispatchEvent(this.generateEvent('keydown',{char: character}));
|
||||
if(character !== '\n'){
|
||||
input.value += character;
|
||||
}
|
||||
input.dispatchEvent(this.generateEvent('keyup',{char: character}));
|
||||
},
|
||||
|
||||
// Removes the last character from the input zone.
|
||||
deleteCharacter: function(){
|
||||
var input = this.$target[0];
|
||||
// Write a character to the input zone
|
||||
writeCharacter: function(character){
|
||||
var input = this.$target[0];
|
||||
input.dispatchEvent(this.generateEvent('keydown',{char: character}));
|
||||
if(character !== '\n'){
|
||||
input.value += character;
|
||||
}
|
||||
input.dispatchEvent(this.generateEvent('keyup',{char: character}));
|
||||
},
|
||||
|
||||
// Removes the last character from the input zone.
|
||||
deleteCharacter: function(){
|
||||
var input = this.$target[0];
|
||||
input.dispatchEvent(this.generateEvent('keydown',{code: 8}));
|
||||
input.value = input.value.substr(0, input.value.length -1);
|
||||
input.dispatchEvent(this.generateEvent('keyup',{code: 8}));
|
||||
},
|
||||
|
||||
// Clears the content of the input zone.
|
||||
deleteAllCharacters: function(){
|
||||
var input = this.$target[0];
|
||||
if(input.value){
|
||||
input.dispatchEvent(this.generateEvent('keydown',{code: 8}));
|
||||
input.value = input.value.substr(0, input.value.length -1);
|
||||
input.value = "";
|
||||
input.dispatchEvent(this.generateEvent('keyup',{code: 8}));
|
||||
},
|
||||
|
||||
// Clears the content of the input zone.
|
||||
deleteAllCharacters: function(){
|
||||
var input = this.$target[0];
|
||||
if(input.value){
|
||||
input.dispatchEvent(this.generateEvent('keydown',{code: 8}));
|
||||
input.value = "";
|
||||
input.dispatchEvent(this.generateEvent('keyup',{code: 8}));
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
// Makes the keyboard show and slide from the bottom of the screen.
|
||||
show: function(){
|
||||
$('.keyboard_frame').show().css({'height':'235px'});
|
||||
},
|
||||
// Makes the keyboard show and slide from the bottom of the screen.
|
||||
show: function(){
|
||||
$('.keyboard_frame').show().css({'height':'235px'});
|
||||
},
|
||||
|
||||
// Makes the keyboard hide by sliding to the bottom of the screen.
|
||||
hide: function(){
|
||||
$('.keyboard_frame')
|
||||
.css({'height':'0'})
|
||||
.hide();
|
||||
this.reset();
|
||||
},
|
||||
|
||||
//What happens when the shift key is pressed : toggle case, remove capslock
|
||||
toggleShift: function(){
|
||||
$('.letter').toggleClass('uppercase');
|
||||
$('.symbol span').toggle();
|
||||
|
||||
// Makes the keyboard hide by sliding to the bottom of the screen.
|
||||
hide: function(){
|
||||
$('.keyboard_frame')
|
||||
.css({'height':'0'})
|
||||
.hide();
|
||||
this.reset();
|
||||
},
|
||||
|
||||
//What happens when the shift key is pressed : toggle case, remove capslock
|
||||
toggleShift: function(){
|
||||
$('.letter').toggleClass('uppercase');
|
||||
this.shift = (this.shift === true) ? false : true;
|
||||
this.capslock = false;
|
||||
},
|
||||
|
||||
//what happens when capslock is pressed : toggle case, set capslock
|
||||
toggleCapsLock: function(){
|
||||
$('.letter').toggleClass('uppercase');
|
||||
this.capslock = true;
|
||||
},
|
||||
|
||||
//What happens when numlock is pressed : toggle symbols and numlock label
|
||||
toggleNumLock: function(){
|
||||
$('.symbol span').toggle();
|
||||
$('.numlock span').toggle();
|
||||
this.numlock = (this.numlock === true ) ? false : true;
|
||||
},
|
||||
|
||||
//After a key is pressed, shift is disabled.
|
||||
removeShift: function(){
|
||||
if (this.shift === true) {
|
||||
$('.symbol span').toggle();
|
||||
if (this.capslock === false) $('.letter').toggleClass('uppercase');
|
||||
|
||||
self.shift = (self.shift === true) ? false : true;
|
||||
self.capslock = false;
|
||||
},
|
||||
|
||||
//what happens when capslock is pressed : toggle case, set capslock
|
||||
toggleCapsLock: function(){
|
||||
$('.letter').toggleClass('uppercase');
|
||||
self.capslock = true;
|
||||
},
|
||||
|
||||
//What happens when numlock is pressed : toggle symbols and numlock label
|
||||
toggleNumLock: function(){
|
||||
$('.symbol span').toggle();
|
||||
$('.numlock span').toggle();
|
||||
self.numlock = (self.numlock === true ) ? false : true;
|
||||
},
|
||||
this.shift = false;
|
||||
}
|
||||
},
|
||||
|
||||
//After a key is pressed, shift is disabled.
|
||||
removeShift: function(){
|
||||
if (self.shift === true) {
|
||||
$('.symbol span').toggle();
|
||||
if (this.capslock === false) $('.letter').toggleClass('uppercase');
|
||||
|
||||
self.shift = false;
|
||||
// Resets the keyboard to its original state; capslock: false, shift: false, numlock: false
|
||||
reset: function(){
|
||||
if(this.shift){
|
||||
this.toggleShift();
|
||||
}
|
||||
if(this.capslock){
|
||||
this.toggleCapsLock();
|
||||
}
|
||||
if(this.numlock){
|
||||
this.toggleNumLock();
|
||||
}
|
||||
},
|
||||
|
||||
//called after the keyboard is in the DOM, sets up the key bindings.
|
||||
start: function(){
|
||||
var self = this;
|
||||
|
||||
//this.show();
|
||||
|
||||
|
||||
$('.close_button').click(function(){
|
||||
self.deleteAllCharacters();
|
||||
self.hide();
|
||||
});
|
||||
|
||||
// Keyboard key click handling
|
||||
$('.keyboard li').click(function(){
|
||||
|
||||
var $this = $(this),
|
||||
character = $this.html(); // If it's a lowercase letter, nothing happens to this variable
|
||||
|
||||
if ($this.hasClass('left-shift') || $this.hasClass('right-shift')) {
|
||||
self.toggleShift();
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
// Resets the keyboard to its original state; capslock: false, shift: false, numlock: false
|
||||
reset: function(){
|
||||
if(this.shift){
|
||||
this.toggleShift();
|
||||
|
||||
if ($this.hasClass('capslock')) {
|
||||
self.toggleCapsLock();
|
||||
return false;
|
||||
}
|
||||
if(this.capslock){
|
||||
this.toggleCapsLock();
|
||||
|
||||
if ($this.hasClass('delete')) {
|
||||
self.deleteCharacter();
|
||||
return false;
|
||||
}
|
||||
if(this.numlock){
|
||||
this.toggleNumLock();
|
||||
|
||||
if ($this.hasClass('numlock')){
|
||||
self.toggleNumLock();
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
// Special characters
|
||||
if ($this.hasClass('symbol')) character = $('span:visible', $this).html();
|
||||
if ($this.hasClass('space')) character = ' ';
|
||||
if ($this.hasClass('tab')) character = "\t";
|
||||
if ($this.hasClass('return')) character = "\n";
|
||||
|
||||
// Uppercase letter
|
||||
if ($this.hasClass('uppercase')) character = character.toUpperCase();
|
||||
|
||||
// Remove shift once a key is clicked.
|
||||
self.removeShift();
|
||||
|
||||
//called after the keyboard is in the DOM, sets up the key bindings.
|
||||
start: function(){
|
||||
var self = this;
|
||||
self.writeCharacter(character);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
//this.show();
|
||||
return {
|
||||
OnscreenKeyboardWidget: OnscreenKeyboardWidget,
|
||||
};
|
||||
|
||||
|
||||
$('.close_button').click(function(){
|
||||
self.deleteAllCharacters();
|
||||
self.hide();
|
||||
});
|
||||
|
||||
// Keyboard key click handling
|
||||
$('.keyboard li').click(function(){
|
||||
|
||||
var $this = $(this),
|
||||
character = $this.html(); // If it's a lowercase letter, nothing happens to this variable
|
||||
|
||||
if ($this.hasClass('left-shift') || $this.hasClass('right-shift')) {
|
||||
self.toggleShift();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this.hasClass('capslock')) {
|
||||
self.toggleCapsLock();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this.hasClass('delete')) {
|
||||
self.deleteCharacter();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this.hasClass('numlock')){
|
||||
self.toggleNumLock();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Special characters
|
||||
if ($this.hasClass('symbol')) character = $('span:visible', $this).html();
|
||||
if ($this.hasClass('space')) character = ' ';
|
||||
if ($this.hasClass('tab')) character = "\t";
|
||||
if ($this.hasClass('return')) character = "\n";
|
||||
|
||||
// Uppercase letter
|
||||
if ($this.hasClass('uppercase')) character = character.toUpperCase();
|
||||
|
||||
// Remove shift once a key is clicked.
|
||||
self.removeShift();
|
||||
|
||||
self.writeCharacter(character);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,27 +1,10 @@
|
||||
|
||||
openerp.point_of_sale = function(openerp) {
|
||||
"use strict";
|
||||
odoo.define('point_of_sale.main', ['point_of_sale.chrome', 'web.core'], function (require) {
|
||||
"use strict";
|
||||
|
||||
var module = openerp.point_of_sale;
|
||||
var chrome = require('point_of_sale.chrome');
|
||||
var core = require('web.core');
|
||||
|
||||
openerp.point_of_sale.load_db(openerp,module);
|
||||
|
||||
openerp.point_of_sale.load_models(openerp,module);
|
||||
|
||||
openerp.point_of_sale.load_basewidget(openerp,module);
|
||||
|
||||
openerp.point_of_sale.load_keyboard(openerp,module);
|
||||
|
||||
openerp.point_of_sale.load_gui(openerp,module);
|
||||
|
||||
openerp.point_of_sale.load_popups(openerp,module);
|
||||
|
||||
openerp.point_of_sale.load_screens(openerp,module);
|
||||
|
||||
openerp.point_of_sale.load_devices(openerp,module);
|
||||
|
||||
openerp.point_of_sale.load_chrome(openerp,module);
|
||||
|
||||
openerp.web.client_actions.add('pos.ui', 'openerp.point_of_sale.Chrome');
|
||||
};
|
||||
core.action_registry.add('pos.ui', chrome.Chrome);
|
||||
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,241 +1,241 @@
|
||||
odoo.define('point_of_sale.popups', ['point_of_sale.BaseWidget', 'point_of_sale.gui'], function (require) {
|
||||
"use strict";
|
||||
|
||||
// This file contains the Popups.
|
||||
// Popups must be loaded and named in chrome.js.
|
||||
// They are instanciated / destroyed with the .gui.show_popup()
|
||||
// and .gui.close_popup() methods.
|
||||
|
||||
openerp.point_of_sale.load_popups = function load_popups(instance, module) {
|
||||
"use strict";
|
||||
|
||||
module.PopupWidget = module.PosBaseWidget.extend({
|
||||
init: function(parent, args) {
|
||||
this._super(parent, args);
|
||||
this.options = {};
|
||||
},
|
||||
events: {
|
||||
'click .button.cancel': 'click_cancel',
|
||||
'click .button.confirm': 'click_confirm',
|
||||
'click .selection-item': 'click_item',
|
||||
'click .input-button': 'click_numpad',
|
||||
'click .mode-button': 'click_numpad',
|
||||
},
|
||||
|
||||
// show the popup !
|
||||
show: function(options){
|
||||
if(this.$el){
|
||||
this.$el.removeClass('oe_hidden');
|
||||
}
|
||||
|
||||
if (typeof options === 'string') {
|
||||
this.options = {title: options};
|
||||
} else {
|
||||
this.options = options || {};
|
||||
}
|
||||
|
||||
this.renderElement();
|
||||
|
||||
// popups block the barcode reader ...
|
||||
if (this.pos.barcode_reader) {
|
||||
this.pos.barcode_reader.save_callbacks();
|
||||
this.pos.barcode_reader.reset_action_callbacks();
|
||||
}
|
||||
},
|
||||
|
||||
// called before hide, when a popup is closed.
|
||||
// extend this if you want a custom action when the
|
||||
// popup is closed.
|
||||
close: function(){
|
||||
if (this.pos.barcode_reader) {
|
||||
this.pos.barcode_reader.restore_callbacks();
|
||||
}
|
||||
},
|
||||
|
||||
// hides the popup. keep in mind that this is called in
|
||||
// the initialization pass of the pos instantiation,
|
||||
// so you don't want to do anything fancy in here
|
||||
hide: function(){
|
||||
if (this.$el) {
|
||||
this.$el.addClass('oe_hidden');
|
||||
}
|
||||
},
|
||||
|
||||
// what happens when we click cancel
|
||||
// ( it should close the popup and do nothing )
|
||||
click_cancel: function(){
|
||||
this.gui.close_popup();
|
||||
if (this.options.cancel) {
|
||||
this.options.cancel.call(this);
|
||||
}
|
||||
},
|
||||
|
||||
// what happens when we confirm the action
|
||||
click_confirm: function(){
|
||||
this.gui.close_popup();
|
||||
if (this.options.confirm) {
|
||||
this.options.confirm.call(this);
|
||||
}
|
||||
},
|
||||
|
||||
// Since Widget does not support extending the events declaration
|
||||
// we declared them all in the top class.
|
||||
click_item: function(){},
|
||||
click_numad: function(){},
|
||||
});
|
||||
var PosBaseWidget = require('point_of_sale.BaseWidget');
|
||||
var gui = require('point_of_sale.gui');
|
||||
|
||||
|
||||
module.ErrorPopupWidget = module.PopupWidget.extend({
|
||||
template:'ErrorPopupWidget',
|
||||
show: function(options){
|
||||
this._super(options);
|
||||
this.gui.play_sound('error');
|
||||
},
|
||||
});
|
||||
module.Gui.define_popup({name:'error', widget:module.ErrorPopupWidget});
|
||||
var PopupWidget = PosBaseWidget.extend({
|
||||
init: function(parent, args) {
|
||||
this._super(parent, args);
|
||||
this.options = {};
|
||||
},
|
||||
events: {
|
||||
'click .button.cancel': 'click_cancel',
|
||||
'click .button.confirm': 'click_confirm',
|
||||
'click .selection-item': 'click_item',
|
||||
'click .input-button': 'click_numpad',
|
||||
'click .mode-button': 'click_numpad',
|
||||
},
|
||||
|
||||
|
||||
module.ErrorTracebackPopupWidget = module.ErrorPopupWidget.extend({
|
||||
template:'ErrorTracebackPopupWidget',
|
||||
});
|
||||
module.Gui.define_popup({name:'error-traceback', widget:module.ErrorTracebackPopupWidget});
|
||||
|
||||
|
||||
module.ErrorBarcodePopupWidget = module.ErrorPopupWidget.extend({
|
||||
template:'ErrorBarcodePopupWidget',
|
||||
show: function(barcode){
|
||||
this._super({barcode: barcode});
|
||||
},
|
||||
});
|
||||
module.Gui.define_popup({name:'error-barcode', widget:module.ErrorBarcodePopupWidget});
|
||||
|
||||
|
||||
module.ConfirmPopupWidget = module.PopupWidget.extend({
|
||||
template: 'ConfirmPopupWidget',
|
||||
});
|
||||
module.Gui.define_popup({name:'confirm', widget:module.ConfirmPopupWidget});
|
||||
|
||||
/**
|
||||
* A popup that allows the user to select one item from a list.
|
||||
*
|
||||
* show_popup('selection',{
|
||||
* title: "Popup Title",
|
||||
* list: [
|
||||
* { label: 'foobar', item: 45 },
|
||||
* { label: 'bar foo', item: 'stuff' },
|
||||
* ],
|
||||
* confirm: function(item) {
|
||||
* // get the item selected by the user.
|
||||
* },
|
||||
* cancel: function(){
|
||||
* // user chose nothing
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
|
||||
module.SelectionPopupWidget = module.PopupWidget.extend({
|
||||
template: 'SelectionPopupWidget',
|
||||
show: function(options){
|
||||
options = options || {};
|
||||
var self = this;
|
||||
this._super(options);
|
||||
|
||||
this.list = options.list || [];
|
||||
this.renderElement();
|
||||
|
||||
this.$('.selection-item').click(function(){
|
||||
if (options.confirm) {
|
||||
var item = self.list[parseInt($(this).data('item-index'))];
|
||||
item = item ? item.item : item;
|
||||
options.confirm.call(self,item);
|
||||
}
|
||||
});
|
||||
},
|
||||
click_item : function(event) {
|
||||
this.gui.close_popup();
|
||||
if (this.options.confirm) {
|
||||
var item = this.list[parseInt($(event.target).data('item-index'))];
|
||||
item = item ? item.item : item;
|
||||
}
|
||||
// show the popup !
|
||||
show: function(options){
|
||||
if(this.$el){
|
||||
this.$el.removeClass('oe_hidden');
|
||||
}
|
||||
});
|
||||
module.Gui.define_popup({name:'selection', widget:module.SelectionPopupWidget});
|
||||
|
||||
if (typeof options === 'string') {
|
||||
this.options = {title: options};
|
||||
} else {
|
||||
this.options = options || {};
|
||||
}
|
||||
|
||||
this.renderElement();
|
||||
|
||||
// popups block the barcode reader ...
|
||||
if (this.pos.barcode_reader) {
|
||||
this.pos.barcode_reader.save_callbacks();
|
||||
this.pos.barcode_reader.reset_action_callbacks();
|
||||
}
|
||||
},
|
||||
|
||||
// called before hide, when a popup is closed.
|
||||
// extend this if you want a custom action when the
|
||||
// popup is closed.
|
||||
close: function(){
|
||||
if (this.pos.barcode_reader) {
|
||||
this.pos.barcode_reader.restore_callbacks();
|
||||
}
|
||||
},
|
||||
|
||||
// hides the popup. keep in mind that this is called in
|
||||
// the initialization pass of the pos instantiation,
|
||||
// so you don't want to do anything fancy in here
|
||||
hide: function(){
|
||||
if (this.$el) {
|
||||
this.$el.addClass('oe_hidden');
|
||||
}
|
||||
},
|
||||
|
||||
// what happens when we click cancel
|
||||
// ( it should close the popup and do nothing )
|
||||
click_cancel: function(){
|
||||
this.gui.close_popup();
|
||||
if (this.options.cancel) {
|
||||
this.options.cancel.call(this);
|
||||
}
|
||||
},
|
||||
|
||||
// what happens when we confirm the action
|
||||
click_confirm: function(){
|
||||
this.gui.close_popup();
|
||||
if (this.options.confirm) {
|
||||
this.options.confirm.call(this);
|
||||
}
|
||||
},
|
||||
|
||||
// Since Widget does not support extending the events declaration
|
||||
// we declared them all in the top class.
|
||||
click_item: function(){},
|
||||
click_numad: function(){},
|
||||
});
|
||||
|
||||
var ErrorPopupWidget = PopupWidget.extend({
|
||||
template:'ErrorPopupWidget',
|
||||
show: function(options){
|
||||
this._super(options);
|
||||
this.gui.play_sound('error');
|
||||
},
|
||||
});
|
||||
gui.define_popup({name:'error', widget: ErrorPopupWidget});
|
||||
|
||||
|
||||
module.TextInputPopupWidget = module.PopupWidget.extend({
|
||||
template: 'TextInputPopupWidget',
|
||||
show: function(options){
|
||||
options = options || {};
|
||||
var self = this;
|
||||
this._super(options);
|
||||
var ErrorTracebackPopupWidget = ErrorPopupWidget.extend({
|
||||
template:'ErrorTracebackPopupWidget',
|
||||
});
|
||||
gui.define_popup({name:'error-traceback', widget: ErrorTracebackPopupWidget});
|
||||
|
||||
this.renderElement();
|
||||
this.$('input,textarea').focus();
|
||||
},
|
||||
click_confirm: function(){
|
||||
var value = this.$('input,textarea').val();
|
||||
this.gui.close_popup();
|
||||
if( this.options.confirm ){
|
||||
this.options.confirm.call(this,value);
|
||||
|
||||
var ErrorBarcodePopupWidget = ErrorPopupWidget.extend({
|
||||
template:'ErrorBarcodePopupWidget',
|
||||
show: function(barcode){
|
||||
this._super({barcode: barcode});
|
||||
},
|
||||
});
|
||||
gui.define_popup({name:'error-barcode', widget: ErrorBarcodePopupWidget});
|
||||
|
||||
|
||||
var ConfirmPopupWidget = PopupWidget.extend({
|
||||
template: 'ConfirmPopupWidget',
|
||||
});
|
||||
gui.define_popup({name:'confirm', widget: ConfirmPopupWidget});
|
||||
|
||||
/**
|
||||
* A popup that allows the user to select one item from a list.
|
||||
*
|
||||
* show_popup('selection',{
|
||||
* title: "Popup Title",
|
||||
* list: [
|
||||
* { label: 'foobar', item: 45 },
|
||||
* { label: 'bar foo', item: 'stuff' },
|
||||
* ],
|
||||
* confirm: function(item) {
|
||||
* // get the item selected by the user.
|
||||
* },
|
||||
* cancel: function(){
|
||||
* // user chose nothing
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
|
||||
var SelectionPopupWidget = PopupWidget.extend({
|
||||
template: 'SelectionPopupWidget',
|
||||
show: function(options){
|
||||
options = options || {};
|
||||
var self = this;
|
||||
this._super(options);
|
||||
|
||||
this.list = options.list || [];
|
||||
this.renderElement();
|
||||
|
||||
this.$('.selection-item').click(function(){
|
||||
if (options.confirm) {
|
||||
var item = self.list[parseInt($(this).data('item-index'))];
|
||||
item = item ? item.item : item;
|
||||
options.confirm.call(self,item);
|
||||
}
|
||||
},
|
||||
});
|
||||
module.Gui.define_popup({name:'textinput', widget:module.TextInputPopupWidget});
|
||||
});
|
||||
},
|
||||
click_item : function(event) {
|
||||
this.gui.close_popup();
|
||||
if (this.options.confirm) {
|
||||
var item = this.list[parseInt($(event.target).data('item-index'))];
|
||||
item = item ? item.item : item;
|
||||
}
|
||||
}
|
||||
});
|
||||
gui.define_popup({name:'selection', widget: SelectionPopupWidget});
|
||||
|
||||
|
||||
module.TextAreaPopupWidget = module.TextInputPopupWidget.extend({
|
||||
template: 'TextAreaPopupWidget',
|
||||
});
|
||||
module.Gui.define_popup({name:'textarea', widget:module.TextAreaPopupWidget});
|
||||
var TextInputPopupWidget = PopupWidget.extend({
|
||||
template: 'TextInputPopupWidget',
|
||||
show: function(options){
|
||||
options = options || {};
|
||||
this._super(options);
|
||||
|
||||
this.renderElement();
|
||||
this.$('input,textarea').focus();
|
||||
},
|
||||
click_confirm: function(){
|
||||
var value = this.$('input,textarea').val();
|
||||
this.gui.close_popup();
|
||||
if( this.options.confirm ){
|
||||
this.options.confirm.call(this,value);
|
||||
}
|
||||
},
|
||||
});
|
||||
gui.define_popup({name:'textinput', widget: TextInputPopupWidget});
|
||||
|
||||
|
||||
module.NumberPopupWidget = module.PopupWidget.extend({
|
||||
template: 'NumberPopupWidget',
|
||||
show: function(options){
|
||||
options = options || {};
|
||||
var self = this;
|
||||
this._super(options);
|
||||
var TextAreaPopupWidget = TextInputPopupWidget.extend({
|
||||
template: 'TextAreaPopupWidget',
|
||||
});
|
||||
gui.define_popup({name:'textarea', widget: TextAreaPopupWidget});
|
||||
|
||||
this.inputbuffer = '' + options.value || '';
|
||||
this.renderElement();
|
||||
this.firstinput = true;
|
||||
},
|
||||
click_numpad: function(event){
|
||||
var newbuf = this.gui.numpad_input(
|
||||
this.inputbuffer,
|
||||
$(event.target).data('action'),
|
||||
{'firstinput': this.firstinput});
|
||||
|
||||
this.firstinput = (newbuf.length === 0);
|
||||
|
||||
if (newbuf !== this.inputbuffer) {
|
||||
this.inputbuffer = newbuf;
|
||||
this.$('.value').text(this.inputbuffer);
|
||||
}
|
||||
},
|
||||
click_confirm: function(event){
|
||||
this.gui.close_popup();
|
||||
if( this.options.confirm ){
|
||||
this.options.confirm.call(this,this.inputbuffer);
|
||||
}
|
||||
},
|
||||
});
|
||||
module.Gui.define_popup({name:'number', widget:module.NumberPopupWidget});
|
||||
var NumberPopupWidget = PopupWidget.extend({
|
||||
template: 'NumberPopupWidget',
|
||||
show: function(options){
|
||||
options = options || {};
|
||||
this._super(options);
|
||||
|
||||
module.PasswordPopupWidget = module.NumberPopupWidget.extend({
|
||||
renderElement: function(){
|
||||
this._super();
|
||||
this.$('.popup').addClass('popup-password');
|
||||
},
|
||||
});
|
||||
module.Gui.define_popup({name:'password', widget:module.PasswordPopupWidget});
|
||||
this.inputbuffer = '' + options.value || '';
|
||||
this.renderElement();
|
||||
this.firstinput = true;
|
||||
},
|
||||
click_numpad: function(event){
|
||||
var newbuf = this.gui.numpad_input(
|
||||
this.inputbuffer,
|
||||
$(event.target).data('action'),
|
||||
{'firstinput': this.firstinput});
|
||||
|
||||
module.UnsentOrdersPopupWidget = module.ConfirmPopupWidget.extend({
|
||||
template: 'UnsentOrdersPopupWidget',
|
||||
});
|
||||
module.Gui.define_popup({name:'unsent-orders', widget:module.UnsentOrdersPopupWidget});
|
||||
this.firstinput = (newbuf.length === 0);
|
||||
|
||||
if (newbuf !== this.inputbuffer) {
|
||||
this.inputbuffer = newbuf;
|
||||
this.$('.value').text(this.inputbuffer);
|
||||
}
|
||||
},
|
||||
click_confirm: function(){
|
||||
this.gui.close_popup();
|
||||
if( this.options.confirm ){
|
||||
this.options.confirm.call(this,this.inputbuffer);
|
||||
}
|
||||
},
|
||||
});
|
||||
gui.define_popup({name:'number', widget: NumberPopupWidget});
|
||||
|
||||
module.UnpaidOrdersPopupWidget = module.ConfirmPopupWidget.extend({
|
||||
template: 'UnpaidOrdersPopupWidget',
|
||||
});
|
||||
module.Gui.define_popup({name:'unpaid-orders', widget:module.UnpaidOrdersPopupWidget});
|
||||
};
|
||||
var PasswordPopupWidget = NumberPopupWidget.extend({
|
||||
renderElement: function(){
|
||||
this._super();
|
||||
this.$('.popup').addClass('popup-password');
|
||||
},
|
||||
});
|
||||
gui.define_popup({name:'password', widget: PasswordPopupWidget});
|
||||
|
||||
var UnsentOrdersPopupWidget = ConfirmPopupWidget.extend({
|
||||
template: 'UnsentOrdersPopupWidget',
|
||||
});
|
||||
gui.define_popup({name:'unsent-orders', widget: UnsentOrdersPopupWidget});
|
||||
|
||||
var UnpaidOrdersPopupWidget = ConfirmPopupWidget.extend({
|
||||
template: 'UnpaidOrdersPopupWidget',
|
||||
});
|
||||
gui.define_popup({name:'unpaid-orders', widget: UnpaidOrdersPopupWidget});
|
||||
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,89 +1,90 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
odoo.define('point_of_sale.Tour', ['web.Tour'], function (require) {
|
||||
"use strict";
|
||||
|
||||
openerp.Tour.register({
|
||||
id: 'pos_basic_order',
|
||||
name: 'Complete a basic order trough the Front-End',
|
||||
path: '/web#model=pos.session.opening&action=point_of_sale.action_pos_session_opening',
|
||||
mode: 'test',
|
||||
steps: [
|
||||
{
|
||||
title: 'Wait fot the bloody screen to be ready',
|
||||
wait: 200,
|
||||
},
|
||||
{
|
||||
title: 'Load the Session',
|
||||
waitNot: '.oe_loading:visible',
|
||||
element: 'span:contains("Resume Session"),span:contains("Start Session")',
|
||||
},
|
||||
{
|
||||
title: 'Loading the Loading Screen',
|
||||
waitFor: '.loader'
|
||||
},
|
||||
{
|
||||
title: 'Waiting for the end of loading...',
|
||||
waitFor: '.loader:hidden',
|
||||
},
|
||||
{
|
||||
title: 'Loading The Point of Sale',
|
||||
waitFor: '.pos',
|
||||
},
|
||||
{
|
||||
title: 'On va manger des CHIPS!',
|
||||
element: '.product-list .product-name:contains("250g Lays Pickels")',
|
||||
},
|
||||
{
|
||||
title: 'The chips have been added to the Order',
|
||||
waitFor: '.order .product-name:contains("250g Lays Pickels")',
|
||||
},
|
||||
{
|
||||
title: 'The order total has been updated to the correct value',
|
||||
wait: 2000,
|
||||
waitFor: '.order .total .value:contains("1.48 €")',
|
||||
},
|
||||
{
|
||||
title: "Let's buy more chips",
|
||||
element: '.product-list .product-name:contains("250g Lays Pickels")',
|
||||
},
|
||||
{
|
||||
title: "Let's veryify we pay the correct price for two bags of chips",
|
||||
waitFor: '.order .total .value:contains("2.96 €")',
|
||||
},
|
||||
{
|
||||
title: "Let's pay with a debit card",
|
||||
element: ".paypad-button:contains('Bank')",
|
||||
},
|
||||
{
|
||||
title: "Let's accept the payment",
|
||||
onload: function(){
|
||||
// The test cannot validate or cancel the print() ... so we replace it by a noop !.
|
||||
window._print = window.print;
|
||||
window.print = function(){ console.log('Print!') };
|
||||
},
|
||||
element: ".button .iconlabel:contains('Validate'):visible",
|
||||
},
|
||||
{
|
||||
title: "Let's finish the order",
|
||||
element: ".button:not(.disabled) .iconlabel:contains('Next'):visible",
|
||||
},
|
||||
{
|
||||
onload: function(){
|
||||
window.print = window._print;
|
||||
window._print = undefined;
|
||||
},
|
||||
title: "Let's wait for the order posting",
|
||||
waitFor: ".oe_status.js_synch .js_connected:visible",
|
||||
},
|
||||
{
|
||||
title: "Let's close the Point of Sale",
|
||||
element: ".header-button:contains('Close')",
|
||||
},
|
||||
{
|
||||
title: "Wait for the backend to ready itself",
|
||||
element: 'span:contains("Resume Session"),span:contains("Start Session")',
|
||||
},
|
||||
],
|
||||
});
|
||||
var Tour = require('web.Tour');
|
||||
|
||||
})();
|
||||
Tour.register({
|
||||
id: 'pos_basic_order',
|
||||
name: 'Complete a basic order trough the Front-End',
|
||||
path: '/web#model=pos.session.opening&action=point_of_sale.action_pos_session_opening',
|
||||
mode: 'test',
|
||||
steps: [
|
||||
{
|
||||
title: 'Wait fot the bloody screen to be ready',
|
||||
wait: 200,
|
||||
},
|
||||
{
|
||||
title: 'Load the Session',
|
||||
waitNot: '.oe_loading:visible',
|
||||
element: 'span:contains("Resume Session"),span:contains("Start Session")',
|
||||
},
|
||||
{
|
||||
title: 'Loading the Loading Screen',
|
||||
waitFor: '.loader'
|
||||
},
|
||||
{
|
||||
title: 'Waiting for the end of loading...',
|
||||
waitFor: '.loader:hidden',
|
||||
},
|
||||
{
|
||||
title: 'Loading The Point of Sale',
|
||||
waitFor: '.pos',
|
||||
},
|
||||
{
|
||||
title: 'On va manger des CHIPS!',
|
||||
element: '.product-list .product-name:contains("250g Lays Pickels")',
|
||||
},
|
||||
{
|
||||
title: 'The chips have been added to the Order',
|
||||
waitFor: '.order .product-name:contains("250g Lays Pickels")',
|
||||
},
|
||||
{
|
||||
title: 'The order total has been updated to the correct value',
|
||||
wait: 2000,
|
||||
waitFor: '.order .total .value:contains("1.48 €")',
|
||||
},
|
||||
{
|
||||
title: "Let's buy more chips",
|
||||
element: '.product-list .product-name:contains("250g Lays Pickels")',
|
||||
},
|
||||
{
|
||||
title: "Let's veryify we pay the correct price for two bags of chips",
|
||||
waitFor: '.order .total .value:contains("2.96 €")',
|
||||
},
|
||||
{
|
||||
title: "Let's pay with a debit card",
|
||||
element: ".paypad-button:contains('Bank')",
|
||||
},
|
||||
{
|
||||
title: "Let's accept the payment",
|
||||
onload: function(){
|
||||
// The test cannot validate or cancel the print() ... so we replace it by a noop !.
|
||||
window._print = window.print;
|
||||
window.print = function(){ console.log('Print!') };
|
||||
},
|
||||
element: ".button .iconlabel:contains('Validate'):visible",
|
||||
},
|
||||
{
|
||||
title: "Let's finish the order",
|
||||
element: ".button:not(.disabled) .iconlabel:contains('Next'):visible",
|
||||
},
|
||||
{
|
||||
onload: function(){
|
||||
window.print = window._print;
|
||||
window._print = undefined;
|
||||
},
|
||||
title: "Let's wait for the order posting",
|
||||
waitFor: ".oe_status.js_synch .js_connected:visible",
|
||||
},
|
||||
{
|
||||
title: "Let's close the Point of Sale",
|
||||
element: ".header-button:contains('Close')",
|
||||
},
|
||||
{
|
||||
title: "Wait for the backend to ready itself",
|
||||
element: 'span:contains("Resume Session"),span:contains("Start Session")',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,80 +1,84 @@
|
||||
openerp.point_of_sale.load_basewidget = function load_basewidget(instance, module){ //module is instance.point_of_sale
|
||||
"use strict";
|
||||
odoo.define('point_of_sale.BaseWidget', ['web.utils', 'web.Widget'], function (require) {
|
||||
"use strict";
|
||||
|
||||
var round_di = instance.web.round_decimals;
|
||||
var round_pr = instance.web.round_precision;
|
||||
var utils = require('web.utils');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
// This is a base class for all Widgets in the POS. It exposes relevant data to the
|
||||
// templates :
|
||||
// - widget.currency : { symbol: '$' | '€' | ..., position: 'before' | 'after }
|
||||
// - widget.format_currency(amount) : this method returns a formatted string based on the
|
||||
// symbol, the position, and the amount of money.
|
||||
// if the PoS is not fully loaded when you instanciate the widget, the currency might not
|
||||
// yet have been initialized. Use __build_currency_template() to recompute with correct values
|
||||
// before rendering.
|
||||
var round_di = utils.round_decimals;
|
||||
var round_pr = utils.round_precision;
|
||||
|
||||
module.PosBaseWidget = instance.web.Widget.extend({
|
||||
init:function(parent,options){
|
||||
this._super(parent);
|
||||
options = options || {};
|
||||
this.pos = options.pos || (parent ? parent.pos : undefined);
|
||||
this.chrome = options.chrome || (parent ? parent.chrome : undefined);
|
||||
this.gui = options.gui || (parent ? parent.gui : undefined);
|
||||
},
|
||||
format_currency: function(amount,precision){
|
||||
var currency = (this.pos && this.pos.currency) ? this.pos.currency : {symbol:'$', position: 'after', rounding: 0.01, decimals: 2};
|
||||
// This is a base class for all Widgets in the POS. It exposes relevant data to the
|
||||
// templates :
|
||||
// - widget.currency : { symbol: '$' | '€' | ..., position: 'before' | 'after }
|
||||
// - widget.format_currency(amount) : this method returns a formatted string based on the
|
||||
// symbol, the position, and the amount of money.
|
||||
// if the PoS is not fully loaded when you instanciate the widget, the currency might not
|
||||
// yet have been initialized. Use __build_currency_template() to recompute with correct values
|
||||
// before rendering.
|
||||
var PosBaseWidget = Widget.extend({
|
||||
init:function(parent,options){
|
||||
this._super(parent);
|
||||
options = options || {};
|
||||
this.pos = options.pos || (parent ? parent.pos : undefined);
|
||||
this.chrome = options.chrome || (parent ? parent.chrome : undefined);
|
||||
this.gui = options.gui || (parent ? parent.gui : undefined);
|
||||
},
|
||||
format_currency: function(amount,precision){
|
||||
var currency = (this.pos && this.pos.currency) ? this.pos.currency : {symbol:'$', position: 'after', rounding: 0.01, decimals: 2};
|
||||
|
||||
amount = this.format_currency_no_symbol(amount,precision);
|
||||
amount = this.format_currency_no_symbol(amount,precision);
|
||||
|
||||
if (currency.position === 'after') {
|
||||
return amount + ' ' + (currency.symbol || '');
|
||||
} else {
|
||||
return (currency.symbol || '') + ' ' + amount;
|
||||
}
|
||||
},
|
||||
format_currency_no_symbol: function(amount, precision) {
|
||||
var currency = (this.pos && this.pos.currency) ? this.pos.currency : {symbol:'$', position: 'after', rounding: 0.01, decimals: 2};
|
||||
var decimals = currency.decimals;
|
||||
if (currency.position === 'after') {
|
||||
return amount + ' ' + (currency.symbol || '');
|
||||
} else {
|
||||
return (currency.symbol || '') + ' ' + amount;
|
||||
}
|
||||
},
|
||||
format_currency_no_symbol: function(amount, precision) {
|
||||
var currency = (this.pos && this.pos.currency) ? this.pos.currency : {symbol:'$', position: 'after', rounding: 0.01, decimals: 2};
|
||||
var decimals = currency.decimals;
|
||||
|
||||
if (precision && (typeof this.pos.dp[precision]) !== undefined) {
|
||||
decimals = this.pos.dp[precision];
|
||||
}
|
||||
|
||||
this.format_currency_no_symbol = function(amount){
|
||||
amount = round_pr(amount,currency.rounding);
|
||||
amount = amount.toFixed(decimals);
|
||||
return amount;
|
||||
};
|
||||
|
||||
if (typeof amount === 'number') {
|
||||
amount = round_di(amount,decimals).toFixed(decimals);
|
||||
}
|
||||
if (precision && (typeof this.pos.dp[precision]) !== undefined) {
|
||||
decimals = this.pos.dp[precision];
|
||||
}
|
||||
|
||||
this.format_currency_no_symbol = function(amount){
|
||||
amount = round_pr(amount,currency.rounding);
|
||||
amount = amount.toFixed(decimals);
|
||||
return amount;
|
||||
},
|
||||
show: function(){
|
||||
this.$el.removeClass('oe_hidden');
|
||||
},
|
||||
hide: function(){
|
||||
this.$el.addClass('oe_hidden');
|
||||
},
|
||||
format_pr: function(value,precision){
|
||||
var decimals = precision > 0 ? Math.max(0,Math.ceil(Math.log(1.0/precision) / Math.log(10))) : 0;
|
||||
return value.toFixed(decimals);
|
||||
},
|
||||
format_fixed: function(value,integer_width,decimal_width){
|
||||
value = value.toFixed(decimal_width || 0);
|
||||
var width = value.indexOf('.');
|
||||
if (width < 0 ) {
|
||||
width = value.length;
|
||||
}
|
||||
var missing = integer_width - width;
|
||||
while (missing > 0) {
|
||||
value = '0' + value;
|
||||
missing--;
|
||||
}
|
||||
return value;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
}
|
||||
if (typeof amount === 'number') {
|
||||
amount = round_di(amount,decimals).toFixed(decimals);
|
||||
}
|
||||
|
||||
return amount;
|
||||
},
|
||||
show: function(){
|
||||
this.$el.removeClass('oe_hidden');
|
||||
},
|
||||
hide: function(){
|
||||
this.$el.addClass('oe_hidden');
|
||||
},
|
||||
format_pr: function(value,precision){
|
||||
var decimals = precision > 0 ? Math.max(0,Math.ceil(Math.log(1.0/precision) / Math.log(10))) : 0;
|
||||
return value.toFixed(decimals);
|
||||
},
|
||||
format_fixed: function(value,integer_width,decimal_width){
|
||||
value = value.toFixed(decimal_width || 0);
|
||||
var width = value.indexOf('.');
|
||||
if (width < 0 ) {
|
||||
width = value.length;
|
||||
}
|
||||
var missing = integer_width - width;
|
||||
while (missing > 0) {
|
||||
value = '0' + value;
|
||||
missing--;
|
||||
}
|
||||
return value;
|
||||
},
|
||||
});
|
||||
|
||||
return PosBaseWidget;
|
||||
|
||||
});
|
||||
|
||||
@@ -31,17 +31,22 @@
|
||||
<t t-call-assets="point_of_sale.assets"/>
|
||||
|
||||
<script type="text/javascript" id="loading-script" t-raw="init">
|
||||
$(function() {
|
||||
var s = new openerp.init();
|
||||
var wc = new s.web.WebClient();
|
||||
odoo.define('web.web_client', ['point_of_sale.main', 'web.WebClient'], function (require) {
|
||||
var WebClient = require('web.WebClient');
|
||||
var web_client = new WebClient();
|
||||
|
||||
wc.show_application = function() {
|
||||
wc.action_manager.do_action("pos.ui");
|
||||
web_client.show_application = function() {
|
||||
web_client.action_manager.do_action("pos.ui");
|
||||
};
|
||||
|
||||
wc.setElement($(document.body));
|
||||
wc.start();
|
||||
$(function () {
|
||||
web_client.setElement($(document.body));
|
||||
web_client.start();
|
||||
});
|
||||
return web_client;
|
||||
});
|
||||
odoo.init();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
Reference in New Issue
Block a user