odoo.define('point_of_sale.tour.pricelist', function (require) {
    "use strict";

    var Tour = require('web_tour.tour');
    var rpc = require('web.rpc');
    var utils = require('web.utils');
    var round_di = utils.round_decimals;

    function assert (condition, message) {
        if (! condition) {
            throw message || "Assertion failed";
        }
    }

    function _build_pricelist_context (pricelist, quantity, date) {
        return {
            pricelist: pricelist.id,
            quantity: quantity,
        };
    }

    function compare_backend_frontend (product, pricelist_name, quantity) {
        return function () {
            var pricelist = _.findWhere(posmodel.pricelists, {name: pricelist_name});
            var frontend_price = product.get_price(pricelist, quantity);
            // ORM applies digits= on non-stored computed field when
            // reading. It does not however truncate like it does when
            // storing the field.
            frontend_price = round_di(frontend_price, posmodel.dp['Product Price']);

            var context = _build_pricelist_context(pricelist, quantity);
            return rpc.query({model: 'product.product', method: 'read', args: [[product.id], ['price']], context: context})
                .then(function (backend_result) {
                    var debug_info = _.extend(context, {
                        product: product.id,
                        product_display_name: product.display_name,
                        pricelist_name: pricelist.name,
                    });
                    var backend_price = backend_result[0].price;
                    assert(frontend_price === backend_price,
                           JSON.stringify(debug_info) + ' DOESN\'T MATCH -> ' + backend_price + ' (backend) != ' + frontend_price + ' (frontend)');
                    return Promise.resolve();
                });
        };
    }

    var steps = [{ // Leave category displayed by default
        content: 'waiting for loading to finish',
        trigger: 'body:not(:has(.loader))',
        run: function () {
            var product_wall_shelf = posmodel.db.search_product_in_category(0, 'Wall Shelf Unit')[0];
            var product_small_shelf = posmodel.db.search_product_in_category(0, 'Small Shelf')[0];
            var product_magnetic_board = posmodel.db.search_product_in_category(0, 'Magnetic Board')[0];
            var product_monitor_stand = posmodel.db.search_product_in_category(0, 'Monitor Stand')[0];
            var product_desk_pad = posmodel.db.search_product_in_category(0, 'Desk Pad')[0];
            var product_letter_tray = posmodel.db.search_product_in_category(0, 'Letter Tray')[0];
            var product_whiteboard = posmodel.db.search_product_in_category(0, 'Whiteboard')[0];

            compare_backend_frontend(product_letter_tray, 'Public Pricelist', 0, undefined)()
                .then(compare_backend_frontend(product_letter_tray, 'Public Pricelist', 1, undefined))
                .then(compare_backend_frontend(product_letter_tray, 'Fixed', 1, undefined))
                .then(compare_backend_frontend(product_wall_shelf, 'Fixed', 1, undefined))
                .then(compare_backend_frontend(product_small_shelf, 'Fixed', 1, undefined))
                .then(compare_backend_frontend(product_wall_shelf, 'Percentage', 1, undefined))
                .then(compare_backend_frontend(product_small_shelf, 'Percentage', 1, undefined))
                .then(compare_backend_frontend(product_magnetic_board, 'Percentage', 1, undefined))
                .then(compare_backend_frontend(product_wall_shelf, 'Formula', 1, undefined))
                .then(compare_backend_frontend(product_small_shelf, 'Formula', 1, undefined))
                .then(compare_backend_frontend(product_magnetic_board, 'Formula', 1, undefined))
                .then(compare_backend_frontend(product_monitor_stand, 'Formula', 1, undefined))
                .then(compare_backend_frontend(product_desk_pad, 'Formula', 1, undefined))
                .then(compare_backend_frontend(product_wall_shelf, 'min_quantity ordering', 1, undefined))
                .then(compare_backend_frontend(product_wall_shelf, 'min_quantity ordering', 2, undefined))
                .then(compare_backend_frontend(product_letter_tray, 'Category vs no category', 1, undefined))
                .then(compare_backend_frontend(product_letter_tray, 'Category', 1, undefined))
                .then(compare_backend_frontend(product_wall_shelf, 'Product template', 1, undefined))
                .then(compare_backend_frontend(product_wall_shelf, 'Dates', 1, undefined))
                .then(compare_backend_frontend(product_small_shelf, 'Pricelist base rounding', 1, undefined))
                .then(compare_backend_frontend(product_whiteboard, 'Public Pricelist', 1, undefined))
                .then(function () {
                    $('.pos').addClass('done-testing');
                });
        },
    }];

    steps = steps.concat([{
        content: "wait for unit tests to finish",
        trigger: ".pos.done-testing",
        run: function () {}, // it's a check
    }, {
        content: "click category switch",
        trigger: ".breadcrumb-home",
        run: 'click',
    }, {
        content: "click pricelist button",
        trigger: ".control-button.o_pricelist_button",
    }, {
        content: "verify default pricelist is set",
        trigger: ".selection-item.selected:contains('Public Pricelist')",
        run: function () {}, // it's a check
    }, {
        content: "select fixed pricelist",
        trigger: ".selection-item:contains('Fixed')",
    }, {
        content: "open customer list",
        trigger: "button.set-customer",
    }, {
        content: "select Deco Addict",
        trigger: ".client-line:contains('Deco Addict')",
    }, {
        content: "confirm selection",
        trigger: ".clientlist-screen .next",
    }, {
        content: "click pricelist button",
        trigger: ".control-button.o_pricelist_button",
    }, {
        content: "verify pricelist changed",
        trigger: ".selection-item.selected:contains('Public Pricelist')",
        run: function () {}, // it's a check
    }, {
        content: "cancel pricelist dialog",
        trigger: ".button.cancel:visible",
    }, {
        content: "open customer list",
        trigger: "button.set-customer",
    }, {
        content: "select Lumber Inc",
        trigger: ".client-line:contains('Lumber Inc')",
    },  {
        content: "confirm selection",
        trigger: ".clientlist-screen .next",
    }, {
        content: "click pricelist button",
        trigger: ".control-button.o_pricelist_button",
    }, {
        content: "verify pricelist remained public pricelist ('Not loaded' is not available)",
        trigger: ".selection-item.selected:contains('Public Pricelist')",
        run: function () {}, // it's a check
    }, {
        content: "cancel pricelist dialog",
        trigger: ".button.cancel:visible",
    },  {
        content: "click pricelist button",
        trigger: ".control-button.o_pricelist_button",
    }, {
        content: "select fixed pricelist",
        trigger: ".selection-item:contains('min_quantity ordering')",
    }, {
        content: "order 1 kg shelf",
        trigger: ".product:contains('Wall Shelf')",
    }, {
        content: "change qty to 2 kg",
        trigger: ".numpad button.input-button:visible:contains('2')",
    }, {
        content: "qty of Wall Shelf line should be 2",
        trigger: ".order-container .orderlines .orderline.selected .product-name:contains('Wall Shelf')",
        extra_trigger: ".order-container .orderlines .orderline.selected .product-name:contains('Wall Shelf') ~ .info-list .info em:contains('2.0')",
        run: function() {},
    }, {
        content: "verify that unit price of shelf changed to $1",
        trigger: ".total > .value:contains('$ 2.00')",
        run: function() {},
    }, {
        content: "order different shelf",
        trigger: ".product:contains('Small Shelf')",
    }, {
        content: "Small Shelf line should be selected with quantity 1",
        trigger: ".order-container .orderlines .orderline.selected .product-name:contains('Small Shelf')",
        extra_trigger: ".order-container .orderlines .orderline.selected .product-name:contains('Small Shelf') ~ .info-list .info em:contains('1.0')",
        run: function() {}
    }, {
        content: "change to price mode",
        trigger: ".numpad button:contains('Price')",
    }, {
        content: "make sure price mode is activated",
        trigger: ".numpad button.selected-mode:contains('Price')",
        run: function() {},
    }, {
        content: "manually override the unit price of these shelf to $5",
        trigger: ".numpad button.input-button:visible:contains('5')",
    }, {
        content: "Small Shelf line should be selected with unit price of 5",
        trigger: ".order-container .orderlines .orderline.selected .product-name:contains('Small Shelf')",
        extra_trigger: ".order-container .orderlines .orderline.selected .product-name:contains('Small Shelf') ~ .price:contains('5.0')",
    }, {
        content: "change back to qty mode",
        trigger: ".numpad button:contains('Qty')",
    }, {
        content: "make sure qty mode is activated",
        trigger: ".numpad button.selected-mode:contains('Qty')",
        run: function() {},
    }, {
        content: "click pricelist button",
        trigger: ".control-button.o_pricelist_button",
    }, {
        content: "select public pricelist",
        trigger: ".selection-item:contains('Public Pricelist')",
    }, {
        content: "verify that the boni shelf have been recomputed and the shelf have not (their price was manually overridden)",
        trigger: ".total > .value:contains('$ 8.96')",
    }, {
        content: "click pricelist button",
        trigger: ".control-button.o_pricelist_button",
    }, {
        content: "select fixed pricelist",
        trigger: ".selection-item:contains('min_quantity ordering')",
    }, {
        content: "close the Point of Sale frontend",
        trigger: ".header-button",
    }, {
        content: "confirm closing the frontend",
        trigger: ".header-button",
        run: function() {}, //it's a check,
    }]);

    Tour.register('pos_pricelist', { test: true, url: '/pos/ui' }, steps);
});

odoo.define('point_of_sale.tour.acceptance', function (require) {
    "use strict";

    var Tour = require("web_tour.tour");

    function add_product_to_order(product_name) {
        return [{
            content: 'buy ' + product_name,
            trigger: '.product-list .product-name:contains("' + product_name + '")',
        }, {
            content: 'the ' + product_name + ' have been added to the order',
            trigger: '.order .product-name:contains("' + product_name + '")',
            run: function () {},
        }];
    }

    function set_fiscal_position_on_order(fp_name) {
        return [{
            content: 'set fiscal position',
            trigger: '.control-button.o_fiscal_position_button',
        }, {
            content: 'choose fiscal position ' + fp_name + ' to add to the order',
            trigger: '.popups .popup .selection .selection-item:contains("' + fp_name + '")',
        }, {
            content: 'the fiscal position ' + fp_name + ' has been set to the order',
            trigger: '.control-button.o_fiscal_position_button:contains("' + fp_name + '")',
            run: function () {},
        }];
    }

    function generate_keypad_steps(amount_str, keypad_selector) {
        var i, steps = [], current_char;
        for (i = 0; i < amount_str.length; ++i) {
            current_char = amount_str[i];
            steps.push({
                content: 'press ' + current_char + ' on payment keypad',
                trigger: keypad_selector + ' .input-button:contains("' + current_char + '"):visible'
            });
        }
        return steps;
    }

    function press_payment_numpad(val) {
        return [{
            content: `press ${val} on payment screen numpad`,
            trigger: `.payment-numpad .input-button:contains("${val}"):visible`,
        }]
    }

    function press_product_numpad(val) {
        return [{
            content: `press ${val} on product screen numpad`,
            trigger: `.numpad .input-button:contains("${val}"):visible`,
        }]
    }

    function selected_payment_has(name, val) {
        return [{
            content: `selected payment is ${name} and has ${val}`,
            trigger: `.paymentlines .paymentline.selected .payment-name:contains("${name}")`,
            extra_trigger: `.paymentlines .paymentline.selected .payment-name:contains("${name}") ~ .payment-amount:contains("${val}")`,
            run: function () {},
        }]
    }

    function selected_orderline_has({ product, price = null, quantity = null }) {
        const result = [];
        if (price !== null) {
            result.push({
                content: `Selected line has product '${product}' and price '${price}'`,
                trigger: `.order-container .orderlines .orderline.selected .product-name:contains("${product}") ~ span.price:contains("${price}")`,
                run: function () {},
            });
        }
        if (quantity !== null) {
            result.push({
                content: `Selected line has product '${product}' and quantity '${quantity}'`,
                trigger: `.order-container .orderlines .orderline.selected .product-name:contains('${product}') ~ .info-list .info em:contains('${quantity}')`,
                run: function () {},
            });
        }
        return result;
    }

    function verify_order_total(total_str) {
        return [{
            content: 'order total contains ' + total_str,
            trigger: '.order .total .value:contains("' + total_str + '")',
            run: function () {}, // it's a check
        }];
    }

    function goto_payment_screen_and_select_payment_method() {
        return [{
            content: "go to payment screen",
            trigger: '.button.pay',
        }, {
            content: "pay with cash",
            trigger: '.paymentmethod:contains("Cash")',
        }];
    }

    function finish_order() {
        return [{
            content: "validate the order",
            trigger: '.payment-screen .button.next.highlight:visible',
        }, {
            content: "verify that the order has been successfully sent to the backend",
            trigger: ".js_connected:visible",
            run: function () {},
        }, {
            content: "click Next Order",
            trigger: '.receipt-screen .button.next.highlight:visible',
        }, {
            content: "check if we left the receipt screen",
            trigger: '.pos-content .screen:not(:has(.receipt-screen))',
            run: function () {},
        }];
    }

    var steps = [{
            content: 'waiting for loading to finish',
            trigger: 'body:not(:has(.loader))',
            run: function () {},
        }, { // Leave category displayed by default
            content: "click category switch",
            trigger: ".breadcrumb-home",
        }];

    steps = steps.concat(add_product_to_order('Desk Organizer'));
    steps = steps.concat(verify_order_total('5.10'));

    steps = steps.concat(add_product_to_order('Desk Organizer'));
    steps = steps.concat(verify_order_total('10.20'));
    steps = steps.concat(goto_payment_screen_and_select_payment_method());

    /*  add payment line of only 5.20
        status:
            order-total := 10.20
            total-payment := 11.70
        expect:
            remaining := 0.00
            change := 1.50
    */
    steps = steps.concat(press_payment_numpad('5'));
    steps = steps.concat(selected_payment_has('Cash', '5.0'));
    steps = steps.concat([{
        content: "verify remaining",
        trigger: '.payment-status-remaining .amount:contains("5.20")',
        run: function () {},
    }, {
        content: "verify change",
        trigger: '.payment-status-change .amount:contains("0.00")',
        run: function () {},
    }]);

    /*  make additional payment line of 6.50
        status:
            order-total := 10.20
            total-payment := 11.70
        expect:
            remaining := 0.00
            change := 1.50
    */
    steps = steps.concat([{
        content: "pay with cash",
        trigger: '.paymentmethod:contains("Cash")',
    }]);
    steps = steps.concat(selected_payment_has('Cash', '5.2'));
    steps = steps.concat(press_payment_numpad('6'))
    steps = steps.concat(selected_payment_has('Cash', '6.0'));
    steps = steps.concat([{
        content: "verify remaining",
        trigger: '.payment-status-remaining .amount:contains("0.00")',
        run: function () {},
    }, {
        content: "verify change",
        trigger: '.payment-status-change .amount:contains("0.80")',
        run: function () {},
    }]);

    steps = steps.concat(finish_order());

    // test opw-672118 orderline subtotal rounding
    steps = steps.concat(add_product_to_order('Desk Organizer'));
    steps = steps.concat(selected_orderline_has({product: 'Desk Organizer', quantity: '1.0'}));
    steps = steps.concat(press_product_numpad('.'))
    steps = steps.concat(selected_orderline_has({product: 'Desk Organizer', quantity: '0.0', price: '0.0'}));
    steps = steps.concat(press_product_numpad('9'))
    steps = steps.concat(selected_orderline_has({product: 'Desk Organizer', quantity: '0.9', price: '4.59'}));
    steps = steps.concat(press_product_numpad('9'))
    steps = steps.concat(selected_orderline_has({product: 'Desk Organizer', quantity: '0.99', price: '5.05'}));
    steps = steps.concat(goto_payment_screen_and_select_payment_method());
    steps = steps.concat(selected_payment_has('Cash', '5.05'));
    steps = steps.concat(finish_order());

    // Test fiscal position one2many map (align with backend)
    steps = steps.concat(add_product_to_order('Letter Tray'));
    steps = steps.concat(selected_orderline_has({product: 'Letter Tray', quantity: '1.0'}));
    steps = steps.concat(verify_order_total('5.28'));
    steps = steps.concat(set_fiscal_position_on_order('FP-POS-2M'));
    steps = steps.concat(verify_order_total('5.52'));

    steps = steps.concat([{
        content: "close the Point of Sale frontend",
        trigger: ".header-button",
    }, {
        content: "confirm closing the frontend",
        trigger: ".header-button.confirm",
        run: function() {}, //it's a check,
    }]);

    Tour.register('pos_basic_order', { test: true, url: '/pos/ui' }, steps);

});
