[REF] point_of_sale: update to the new module system

This commit is contained in:
Géry Debongnie
2015-03-18 09:01:52 +01:00
parent 1ca7f9f6aa
commit 1d23c24d79
12 changed files with 6123 additions and 6058 deletions
File diff suppressed because it is too large Load Diff
+468 -462
View File
@@ -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
+342 -337
View File
@@ -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,
};
});
+190 -184
View File
@@ -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);
});
},
});
}
});
+6 -23
View File
@@ -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
+217 -217
View File
@@ -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
+87 -86
View File
@@ -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;
});
+12 -7
View File
@@ -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>