Browse Source

[ADD] Some functional testing

pull/6/head
Adil Houmadi 10 years ago
parent
commit
2092500fea
  1. 5
      pos_pricelist/__openerp__.py
  2. 95
      pos_pricelist/demo/pos_pricelist_demo.yml
  3. 46
      pos_pricelist/static/src/css/style.css
  4. 2
      pos_pricelist/static/src/js/db.js
  5. 67
      pos_pricelist/static/src/js/models.js
  6. 164
      pos_pricelist/static/src/js/tests.js
  7. 27
      pos_pricelist/test/test.py
  8. 6
      pos_pricelist/views/pos_pricelist_template.xml

5
pos_pricelist/__openerp__.py

@ -24,7 +24,7 @@
'author': 'Adil Houmadi @Taktik', 'author': 'Adil Houmadi @Taktik',
'summary': 'Pricelist for Point of sale', 'summary': 'Pricelist for Point of sale',
'description': """ 'description': """
New features for the Point Of Sale:
New feature for the Point Of Sale:
============================================= =============================================
Add support for pricelist on 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': [ 'data': [
"views/pos_pricelist_template.xml", "views/pos_pricelist_template.xml",
], ],
'demo': [
'demo/pos_pricelist_demo.yml',
],
'installable': True, 'installable': True,
'application': False, 'application': False,
'auto_install': False, 'auto_install': False,

95
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

46
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 <ah@taktik.be>
*
* 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 <http://www.gnu.org/licenses/>.
*
******************************************************************************/
.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;
}

2
pos_pricelist/static/src/js/db.js

@ -17,8 +17,6 @@
******************************************************************************/ ******************************************************************************/
function pos_pricelist_db(instance, module) { function pos_pricelist_db(instance, module) {
console.log('Loading ...');
module.PosDB = module.PosDB.extend({ module.PosDB = module.PosDB.extend({
init: function (options) { init: function (options) {
options = options || {}; options = options || {};

67
pos_pricelist/static/src/js/models.js

@ -18,7 +18,7 @@
function pos_pricelist_models(instance, module) { function pos_pricelist_models(instance, module) {
var _t = instance.web._t; var _t = instance.web._t;
var round_pr = instance.web.round_precision
var round_pr = instance.web.round_precision;
/** /**
* @param funcName * @param funcName
@ -60,7 +60,8 @@ function pos_pricelist_models(instance, module) {
module.Order = module.Order.extend({ module.Order = module.Order.extend({
/** /**
* override this method to merge lines * 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 product
* @param options * @param options
*/ */
@ -101,7 +102,6 @@ function pos_pricelist_models(instance, module) {
this.selectLine(this.getLastOrderline()); this.selectLine(this.getLastOrderline());
} }
}); });
/** /**
* Extend the Order line * Extend the Order line
*/ */
@ -143,6 +143,7 @@ function pos_pricelist_models(instance, module) {
/** /**
* override this method to take fiscal postions in consideration * override this method to take fiscal postions in consideration
* get all price * get all price
* TODO : find a better way to do it : need some refactoring in the pos standard
* @returns {{priceWithTax: *, priceWithoutTax: *, tax: number, taxDetails: {}}} * @returns {{priceWithTax: *, priceWithoutTax: *, tax: number, taxDetails: {}}}
*/ */
get_all_prices: function () { get_all_prices: function () {
@ -153,32 +154,39 @@ function pos_pricelist_models(instance, module) {
var totalTax = base; var totalTax = base;
var totalNoTax = base; var totalNoTax = base;
var product = this.get_product(); 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 partner = this.order.get_client();
var taxes_ids = product.taxes_id;
var fiscal_position_taxes = []; var fiscal_position_taxes = [];
if (partner && partner.property_account_position) { if (partner && partner.property_account_position) {
fiscal_position_taxes = self.pos.db.find_taxes_by_fiscal_position_id(partner.property_account_position[0]); 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++) { for (var i = 0, ilen = fiscal_position_taxes.length; i < ilen; i++) {
var fp_tax = fiscal_position_taxes[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) { if (tax.price_include) {
var tmp; var tmp;
if (tax.type === "percent") { 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; throw "This type of tax is not supported by the point of sale: " + tax.type;
} }
tmp = round_pr(tmp, currency_rounding); tmp = round_pr(tmp, currency_rounding);
if (tax.include_base_amount) {
base += tmp;
}
taxtotal += tmp; taxtotal += tmp;
totalTax += tmp; totalTax += tmp;
taxdetail[tax.id] = tmp; taxdetail[tax.id] = tmp;
@ -214,7 +225,6 @@ function pos_pricelist_models(instance, module) {
"taxDetails": taxdetail "taxDetails": taxdetail
}; };
}, },
/** /**
* compute price for all price list * compute price for all price list
* @param db * @param db
@ -240,8 +250,8 @@ function pos_pricelist_models(instance, module) {
*/ */
can_be_merged_with: function (orderline) { can_be_merged_with: function (orderline) {
var result = this._super('can_be_merged_with', 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); return (this.get_product().id === orderline.get_product().id);
} else { } else {
return false; return false;
@ -422,7 +432,7 @@ function pos_pricelist_models(instance, module) {
}); });
/** /**
* show error based on pop up
* show error
* @param context * @param context
* @param message * @param message
* @param comment * @param comment
@ -449,8 +459,6 @@ function pos_pricelist_models(instance, module) {
var res_product_pricelist = pos_model.find_model('product.pricelist'); var res_product_pricelist = pos_model.find_model('product.pricelist');
if (_.size(res_product_pricelist) == 1) { if (_.size(res_product_pricelist) == 1) {
var pricelist_index = parseInt(Object.keys(res_product_pricelist)[0]); 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, pos_model.models.splice(++pricelist_index, 0,
{ {
model: 'account.fiscal.position.tax', model: 'account.fiscal.position.tax',
@ -535,6 +543,17 @@ function pos_pricelist_models(instance, module) {
fields: ['name', 'field', 'currency_id'], fields: ['name', 'field', 'currency_id'],
domain: null, domain: null,
loaded: function (self, price_types) { 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); self.db.add_price_types(price_types);
} }
} }

164
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 <ah@taktik.be>
*
* 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 <http://www.gnu.org/licenses/>.
*
******************************************************************************/
(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 <br>" +
"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 <br>" +
"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")'
}
]
});
})();

27
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 <ah@taktik.be>
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
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")

6
pos_pricelist/views/pos_pricelist_template.xml

@ -19,9 +19,9 @@
</xpath> </xpath>
</template> </template>
<template id="qunit_suite" name="pos_pricelist_test qunit" inherit_id="web.qunit_suite">
<xpath expr="//head" position="inside">
<script type="text/javascript" src="/pos_pricelist/static/test/tests.js"></script>
<template id="pos_pricelist_assets" name="pos_pricelist_css" inherit_id="point_of_sale.index">
<xpath expr="//script[@id='loading-script']" position="before">
<link rel="stylesheet" href="/pos_pricelist/static/src/css/style.css"/>
</xpath> </xpath>
</template> </template>

Loading…
Cancel
Save