From 2092500fea8f582911dbda5def6444424239d3b3 Mon Sep 17 00:00:00 2001 From: Adil Houmadi Date: Sun, 21 Dec 2014 00:19:48 +0100 Subject: [PATCH] [ADD] Some functional testing --- pos_pricelist/__openerp__.py | 5 +- pos_pricelist/demo/pos_pricelist_demo.yml | 95 ++++++++++ pos_pricelist/static/src/css/style.css | 46 +++++ pos_pricelist/static/src/js/db.js | 2 - pos_pricelist/static/src/js/models.js | 67 ++++--- pos_pricelist/static/src/js/tests.js | 164 ++++++++++++++++++ pos_pricelist/test/test.py | 27 +++ .../views/pos_pricelist_template.xml | 6 +- 8 files changed, 382 insertions(+), 30 deletions(-) create mode 100644 pos_pricelist/demo/pos_pricelist_demo.yml create mode 100644 pos_pricelist/static/src/css/style.css create mode 100644 pos_pricelist/static/src/js/tests.js create mode 100644 pos_pricelist/test/test.py diff --git a/pos_pricelist/__openerp__.py b/pos_pricelist/__openerp__.py index d254085b..01d14e64 100644 --- a/pos_pricelist/__openerp__.py +++ b/pos_pricelist/__openerp__.py @@ -24,7 +24,7 @@ 'author': 'Adil Houmadi @Taktik', 'summary': 'Pricelist for Point of sale', 'description': """ -New features for the Point Of Sale: +New feature for the Point Of Sale: ============================================= Add support for pricelist on the point of sale """, @@ -34,6 +34,9 @@ New features for the Point Of Sale: 'data': [ "views/pos_pricelist_template.xml", ], + 'demo': [ + 'demo/pos_pricelist_demo.yml', + ], 'installable': True, 'application': False, 'auto_install': False, diff --git a/pos_pricelist/demo/pos_pricelist_demo.yml b/pos_pricelist/demo/pos_pricelist_demo.yml new file mode 100644 index 00000000..4c2e9888 --- /dev/null +++ b/pos_pricelist/demo/pos_pricelist_demo.yml @@ -0,0 +1,95 @@ +- + This product will have two rule (min_qty:3 => 10%, min_qty:5 => 30%) +- + !record {model: product.product, id: pos_product_product_1}: + default_code: ABC123 + name: POS Product 1 + type: product + categ_id: product.product_category_1 + list_price: 100.0 + standard_price: 50.0 + uom_id: product.product_uom_unit + uom_po_id: product.product_uom_unit + available_in_pos: True + +- + This product will have one rule (min_qty:2 => 10%) +- + !record {model: product.product, id: pos_product_product_2}: + default_code: ABC124 + name: POS Product 2 + type: product + categ_id: product.product_category_1 + list_price: 100.0 + standard_price: 100.0 + uom_id: product.product_uom_unit + uom_po_id: product.product_uom_unit + available_in_pos: True + +- + This product will have a rule that (based on supplier discount) +- + !record {model: product.product, id: pos_product_product_3}: + default_code: ABC125 + name: POS Product 3 + type: product + categ_id: product.product_category_1 + list_price: 100.0 + standard_price: 50.0 + uom_id: product.product_uom_unit + uom_po_id: product.product_uom_unit + available_in_pos: True + seller_ids: + - delay: 1 + name: base.res_partner_3 + pricelist_ids: + - min_quantity : 2.0 + price : 80 + +- + This product belgon to computer category (5% dicount) +- + !record {model: product.product, id: pos_product_product_4}: + default_code: ABC125 + name: POS Product 4 + type: product + categ_id: product.product_category_4 + list_price: 100.0 + standard_price: 50.0 + uom_id: product.product_uom_unit + uom_po_id: product.product_uom_unit + available_in_pos: True +- + Prepare pricelist items +- + !record {model: product.pricelist.version, id: product.ver0}: + items_id: + - name: 10% Discount on POS Product 1 (Qty 3) + sequence: 2 + product_id: pos_product_product_1 + base: 1 + price_discount: -0.10 + min_quantity: 3 + - name: 30% Discount on POS Product 1 (Qty 5) + sequence: 1 + product_id: pos_product_product_1 + min_quantity: 5 + base: 1 + price_discount: -0.30 + - name: 10% Discount (POS Product 2) + sequence: 1 + product_id: pos_product_product_2 + base: 2 + price_discount: -0.10 + min_quantity: 2 + - name: 20% Discount given by supplier + sequence: 1 + min_quantity: 2 + product_id: pos_product_product_3 + base: -2 + - name: 5% Discount on all Computer related products (Qty 2) + sequence: 1 + min_quantity: 2 + base: 1 + categ_id: product.product_category_4 + price_discount: -0.05 \ No newline at end of file diff --git a/pos_pricelist/static/src/css/style.css b/pos_pricelist/static/src/css/style.css new file mode 100644 index 00000000..5a652bb9 --- /dev/null +++ b/pos_pricelist/static/src/css/style.css @@ -0,0 +1,46 @@ +/****************************************************************************** +* Point Of Sale - Pricelist for POS Odoo +* Copyright (C) 2014 Taktik (http://www.taktik.be) +* @author Adil Houmadi +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +* +******************************************************************************/ +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + text-align: left; + white-space: normal; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + box-shadow: 0 5px 10px rgba(0, 0, 0, .2); +} +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} \ No newline at end of file diff --git a/pos_pricelist/static/src/js/db.js b/pos_pricelist/static/src/js/db.js index 9e5e2715..d00f6ef9 100644 --- a/pos_pricelist/static/src/js/db.js +++ b/pos_pricelist/static/src/js/db.js @@ -17,8 +17,6 @@ ******************************************************************************/ function pos_pricelist_db(instance, module) { - console.log('Loading ...'); - module.PosDB = module.PosDB.extend({ init: function (options) { options = options || {}; diff --git a/pos_pricelist/static/src/js/models.js b/pos_pricelist/static/src/js/models.js index e87b5e73..bf87dae9 100644 --- a/pos_pricelist/static/src/js/models.js +++ b/pos_pricelist/static/src/js/models.js @@ -18,7 +18,7 @@ function pos_pricelist_models(instance, module) { var _t = instance.web._t; - var round_pr = instance.web.round_precision + var round_pr = instance.web.round_precision; /** * @param funcName @@ -60,7 +60,8 @@ function pos_pricelist_models(instance, module) { module.Order = module.Order.extend({ /** * override this method to merge lines - * TODO : find a better way to do it + * TODO : Need some refactoring in the standard POS to Do it better + * TODO : from line 73 till 85, we need to move this to another method * @param product * @param options */ @@ -101,7 +102,6 @@ function pos_pricelist_models(instance, module) { this.selectLine(this.getLastOrderline()); } }); - /** * Extend the Order line */ @@ -143,6 +143,7 @@ function pos_pricelist_models(instance, module) { /** * override this method to take fiscal postions in consideration * get all price + * TODO : find a better way to do it : need some refactoring in the pos standard * @returns {{priceWithTax: *, priceWithoutTax: *, tax: number, taxDetails: {}}} */ get_all_prices: function () { @@ -153,32 +154,39 @@ function pos_pricelist_models(instance, module) { var totalTax = base; var totalNoTax = base; var product = this.get_product(); + var taxes = this.get_applicable_taxes(); + var taxtotal = 0; + var taxdetail = {}; + + // Add by pos_pricelist var partner = this.order.get_client(); - var taxes_ids = product.taxes_id; var fiscal_position_taxes = []; if (partner && partner.property_account_position) { fiscal_position_taxes = self.pos.db.find_taxes_by_fiscal_position_id(partner.property_account_position[0]); } - var product_taxes_ids = []; + var product_taxes = []; for (var i = 0, ilen = fiscal_position_taxes.length; i < ilen; i++) { var fp_tax = fiscal_position_taxes[i]; - for (var j = 0, jlen = taxes_ids.length; j < jlen; j++) { - var p_tax = taxes_ids[j]; - if (fp_tax && p_tax && fp_tax.tax_src_id[0] === p_tax) { - product_taxes_ids.push(fp_tax.tax_dest_id[0]); + for (var j = 0, jlen = taxes.length; j < jlen; j++) { + var p_tax = taxes[j]; + if (fp_tax && p_tax && fp_tax.tax_src_id[0] === p_tax.id) { + var dest_tax = _.detect(this.pos.taxes, function (t) { + return t.id === fp_tax.tax_dest_id[0]; + }); + product_taxes.push(dest_tax); } } } - if (product_taxes_ids.length === 0) { - product_taxes_ids = taxes_ids; + if (product_taxes.length === 0) { + for (var i = 0, ilen = product.taxes_id; i < ilen; i++) { + var _id = product.taxes_id[i]; + var p_tax = _.detect(this.pos.taxes, function (t) { + return t.id === _id; + }); + product_taxes.push(p_tax); + } } - var taxes = self.pos.taxes; - var taxtotal = 0; - var taxdetail = {}; - _.each(product_taxes_ids, function (el) { - var tax = _.detect(taxes, function (t) { - return t.id === el; - }); + _.each(product_taxes, function (tax) { if (tax.price_include) { var tmp; if (tax.type === "percent") { @@ -202,6 +210,9 @@ function pos_pricelist_models(instance, module) { throw "This type of tax is not supported by the point of sale: " + tax.type; } tmp = round_pr(tmp, currency_rounding); + if (tax.include_base_amount) { + base += tmp; + } taxtotal += tmp; totalTax += tmp; taxdetail[tax.id] = tmp; @@ -214,7 +225,6 @@ function pos_pricelist_models(instance, module) { "taxDetails": taxdetail }; }, - /** * compute price for all price list * @param db @@ -240,8 +250,8 @@ function pos_pricelist_models(instance, module) { */ can_be_merged_with: function (orderline) { var result = this._super('can_be_merged_with', orderline); - if(!result) { - if(!this.manuel_price) { + if (!result) { + if (!this.manuel_price) { return (this.get_product().id === orderline.get_product().id); } else { return false; @@ -422,7 +432,7 @@ function pos_pricelist_models(instance, module) { }); /** - * show error based on pop up + * show error * @param context * @param message * @param comment @@ -449,8 +459,6 @@ function pos_pricelist_models(instance, module) { var res_product_pricelist = pos_model.find_model('product.pricelist'); if (_.size(res_product_pricelist) == 1) { var pricelist_index = parseInt(Object.keys(res_product_pricelist)[0]); - - // after the pricelist we can load all pricelists, versions and items pos_model.models.splice(++pricelist_index, 0, { model: 'account.fiscal.position.tax', @@ -535,6 +543,17 @@ function pos_pricelist_models(instance, module) { fields: ['name', 'field', 'currency_id'], domain: null, loaded: function (self, price_types) { + // we need to add price type field to product.product model if not the case + var product_model = posmodel.find_model('product.product'); + for(var i = 0, len = price_types.length; i < len; i++) { + var p_type = price_types[i].field; + if (_.size(product_model) == 1) { + var product_index = parseInt(Object.keys(product_model)[0]); + if(posmodel.models[product_index].fields.indexOf(p_type) === -1) { + posmodel.models[product_index].fields.push(p_type); + } + } + } self.db.add_price_types(price_types); } } diff --git a/pos_pricelist/static/src/js/tests.js b/pos_pricelist/static/src/js/tests.js new file mode 100644 index 00000000..ed7b232c --- /dev/null +++ b/pos_pricelist/static/src/js/tests.js @@ -0,0 +1,164 @@ +/****************************************************************************** + * Point Of Sale - Pricelist for POS Odoo + * Copyright (C) 2014 Taktik (http://www.taktik.be) + * @author Adil Houmadi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + ******************************************************************************/ +(function () { + 'use strict'; + + openerp.Tour.register({ + id: 'pos_pricelist_order', + name: 'Complete a order trough the Front-End using POS Pricelist', + path: '/web#model=pos.session.opening&action=point_of_sale.action_pos_session_opening', + mode: 'test', + steps: [ + { + wait: 2000, + title: 'Wait for screen to be ready' + }, + { + wait: 2000, + title: 'Load the Session', + waitNot: '.oe_loading:visible', + element: 'span:contains("Resume Session"),span:contains("Start Session")' + }, + { + title: 'Loading Screen', + waitFor: '.loader' + }, + { + wait: 2000, + title: 'The Point of Sale', + waitFor: '.pos' + }, + { + title: "We will buy some Products!, let's add (POS Product 1)", + element: '.product-list .product-name:contains("POS Product 1")' + }, + { + wait: 5000, + title: 'The order total has been updated to the correct value : 100€', + waitFor: '.order .total .value:contains("100.00 €")' + }, + { + wait: 5000, + title: 'We will add one more unit!', + element: '.product-list .product-name:contains("POS Product 1")' + }, + { + wait: 4000, + title: 'We will add another unit', + element: '.product-list .product-name:contains("POS Product 1")' + }, + { + wait: 4000, + title: 'The order total should be updated : 270€ which means 90€/Unit (Rule 10% Discount from 3 Units)', + waitFor: '.order .total .value:contains("270.00 €")' + }, + { + wait: 8000, + title: 'We will add another product', + element: '.product-list .product-name:contains("POS Product 2")' + }, + { + wait: 4000, + title: 'We will add another unit for this product (POS Product 2)', + element: '.product-list .product-name:contains("POS Product 2")' + }, + { + wait: 4000, + title: "Let's verify the total that we should pay, it's should be equal to : 450€, which means that
" + + "10% Discount if offered if we buy 2 units of (POS Product 2), Rule based on standard price", + waitFor: '.order .total .value:contains("450.00 €")' + }, + { + wait: 10000, + title: "Now, we will add (POS Product 3), for this product if we buy more then 2 units
" + + "20% Discount is given by supplier to our customers", + element: '.product-list .product-name:contains("POS Product 3")' + }, + { + wait: 10000, + title: 'We will add another unit for this product (POS Product 3)', + element: '.product-list .product-name:contains("POS Product 3")' + }, + { + wait: 5000, + title: "Let's check the total (610€)", + waitFor: '.order .total .value:contains("610.00 €")' + }, + { + wait: 5000, + title: "Now, we will add (POS Product 4), this product belong to (Comptuer) category in which " + + "we apply 5% if customer buy more then 2 products", + element: '.product-list .product-name:contains("POS Product 4")' + }, + { + wait: 10000, + title: 'We will add another unit for this product (POS Product 4)', + element: '.product-list .product-name:contains("POS Product 4")' + }, + { + wait: 5000, + title: "Let's check the total again (800€)", + waitFor: '.order .total .value:contains("800.00 €")' + }, + { + wait: 5000, + title: "Let's pay the order", + element: ".paypad-button:contains('Bank')" + }, + { + wait: 1000, + title: "Let's accept the payment", + onload: function () { + window._print = window.print; + window.print = function () { + console.log('Print!') + }; + }, + element: ".button .iconlabel:contains('Validate'):visible" + }, + { + wait: 1000, + title: "Let's finish the order", + element: ".button:not(.disabled) .iconlabel:contains('Next'):visible" + }, + { + wait: 1000, + 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" + }, + { + wait: 1000, + title: "Let's close the Point of Sale", + element: ".header-button:contains('Close')" + }, + { + title: "Let's confirm", + element: ".header-button.confirm:contains('Confirm')" + }, + { + title: "Wait for the backend to ready itself", + element: 'span:contains("Resume Session"),span:contains("Start Session")' + } + ] + }); + +})(); diff --git a/pos_pricelist/test/test.py b/pos_pricelist/test/test.py new file mode 100644 index 00000000..cec1c319 --- /dev/null +++ b/pos_pricelist/test/test.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +############################################################################## +# Point Of Sale - Pricelist for POS Odoo +# Copyright (C) 2014 Taktik (http://www.taktik.be) +# @author Adil Houmadi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import openerp.tests + +@openerp.tests.common.at_install(False) +@openerp.tests.common.post_install(True) +class TestPOS(openerp.tests.HttpCase): + def test_01_pos(self): + self.phantom_js("/", "openerp.Tour.run('pos_pricelist_order', 'test')", + "openerp.Tour.tours.pos_pricelist_order", + login="admin") diff --git a/pos_pricelist/views/pos_pricelist_template.xml b/pos_pricelist/views/pos_pricelist_template.xml index eaa794a8..e954f1d8 100644 --- a/pos_pricelist/views/pos_pricelist_template.xml +++ b/pos_pricelist/views/pos_pricelist_template.xml @@ -19,9 +19,9 @@ -