diff --git a/pos_category_multi/README.rst b/pos_category_multi/README.rst index c2b9c53ddd..cded6b11d6 100644 --- a/pos_category_multi/README.rst +++ b/pos_category_multi/README.rst @@ -30,7 +30,7 @@ Maintainers To get a guaranteed support you are kindly requested to purchase the module - at `odoo apps store `__. + at `odoo apps store `__. Thank you for understanding! @@ -45,7 +45,6 @@ Usage instructions: ``_ Changelog: ``_ -Notifications on updates: `via Atom `_, `by Email `_ +Notifications on updates: `via Atom `_, `by Email `_ Tested on Odoo 11.0 ee2b9fae3519c2494f34dacf15d0a3b5bd8fbd06 - diff --git a/pos_category_multi/__init__.py b/pos_category_multi/__init__.py index a9e3372262..1e500f5fc3 100644 --- a/pos_category_multi/__init__.py +++ b/pos_category_multi/__init__.py @@ -1,2 +1,15 @@ +# Copyright 2019 Anvar kildebekov +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). from . import models + +from odoo import SUPERUSER_ID +from odoo import api + +def copy_categories(cr, registry): + env = api.Environment(cr, SUPERUSER_ID, {}) + for product in list(env["product.template"].search([])): + if product.pos_categ_id: + product.write({ + 'pos_category_ids': [(4, product.pos_categ_id.id)] + }) diff --git a/pos_category_multi/__manifest__.py b/pos_category_multi/__manifest__.py index 633271c267..034e3fcac3 100644 --- a/pos_category_multi/__manifest__.py +++ b/pos_category_multi/__manifest__.py @@ -1,8 +1,12 @@ +# Copyright 2019 Kolushov Alexandr +# Copyright 2019 Anvar Kildebekov +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). + { "name": """Multiple categories per product in POS""", "summary": """Specify as many categories for a product as you need""", "category": "Point of Sale", - "version": "11.0.1.0.1", + "version": "11.0.1.1.1", "images": [], "author": "IT-Projects LLC, Pavel Romanchenko", "support": "pos@it-projects.info", @@ -22,4 +26,10 @@ "demo": [], "installable": True, "auto_install": False, + + "post_load": None, + "pre_init_hook": None, + "post_init_hook": 'copy_categories', + "uninstall_hook": None, + } diff --git a/pos_category_multi/doc/changelog.rst b/pos_category_multi/doc/changelog.rst index a43b0a765f..079a5f5698 100644 --- a/pos_category_multi/doc/changelog.rst +++ b/pos_category_multi/doc/changelog.rst @@ -1,6 +1,11 @@ Updates ======= +`1.1.1` +------- + +**Improvement:** After install updates categories for product if there were any + `1.0.1` ------- diff --git a/pos_debt_notebook/README.rst b/pos_debt_notebook/README.rst index c59bb20a44..885463a26f 100644 --- a/pos_debt_notebook/README.rst +++ b/pos_debt_notebook/README.rst @@ -53,9 +53,9 @@ Sponsors Further information =================== -Demo: http://runbot.it-projects.info/demo/pos-addons/9.0 +Demo: http://runbot.it-projects.info/demo/pos-addons/11.0 -HTML Description: https://apps.odoo.com/apps/modules/9.0/pos_debt_notebook/ +HTML Description: https://apps.odoo.com/apps/modules/11.0/pos_debt_notebook/ Usage instructions: ``__ diff --git a/pos_debt_notebook/static/src/js/test_pos_debt.js b/pos_debt_notebook/static/src/js/test_pos_debt.js index 8e3feb3247..3bf9e7bee6 100644 --- a/pos_debt_notebook/static/src/js/test_pos_debt.js +++ b/pos_debt_notebook/static/src/js/test_pos_debt.js @@ -1,10 +1,13 @@ -odoo.define('pos_debt_notebook.tour', function (require) { +/* Copyright 2019 Anvar Kildebekov + * License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). */ + odoo.define('pos_debt_notebook.tour', function (require) { "use strict"; var tour = require("web_tour.tour"); var core = require('web.core'); var _t = core._t; + /* -----Actions----- */ function open_pos_neworder() { return [{ trigger: '.o_app[data-menu-xmlid="point_of_sale.menu_point_root"], .oe_menu_toggler[data-menu-xmlid="point_of_sale.menu_point_root"]', @@ -19,12 +22,14 @@ odoo.define('pos_debt_notebook.tour', function (require) { trigger: '.table:not(.oe_invisible .neworder-button), .order-button.selected', position: "bottom", timeout: 20000, - }, { - content: 'waiting for loading to finish', + }]; + } + function create_new_order() { + return [{ + content: 'Create new order', trigger: '.order-button.neworder-button', }]; } - function add_product_to_order(product_name) { return [{ content: 'buy ' + product_name, @@ -34,13 +39,19 @@ odoo.define('pos_debt_notebook.tour', function (require) { trigger: '.order .product-name:contains("' + product_name + '")', }]; } - + function switch_table() { + return [{ + content: "Switch to table or make dummy action", + trigger: '.table:not(.oe_invisible .neworder-button), .order-button.selected', + position: "bottom", + }]; + } function set_customer(name) { return [{ - trigger: '.button.set-customer', + trigger: '.button.js_set_customer', content: _t("Open the customer screen"), }, { - trigger: 'td:contains("' + name + '")', + trigger: 'td:contains(' + name + ')', content: _t("Click the customer"), }, { extra_trigger: '.button.next.highlight:contains("Set Customer")', @@ -48,8 +59,16 @@ odoo.define('pos_debt_notebook.tour', function (require) { content: 'Set Customer', }]; } - - function debt_method_paying(pay_method) { + function deselect_customer() { + return [{ + trigger: '.button.js_set_customer', + content: _t("Open the customer screen"), + }, { + trigger: '.button:contains("Deselect")', + content: _t("Deselect the customer"), + }]; + } + function click_to_payment() { return [{ content: "Make a dummy action", trigger: '.order-button.selected', @@ -59,27 +78,197 @@ odoo.define('pos_debt_notebook.tour', function (require) { }, { content: "Choose Administrator like a cashier or make a dummy action", trigger: '.modal-dialog.cashier:not(.oe_hidden) .cashier .selection-item:contains("Administrator"), .payment-screen:not(.oe_hidden) h1:contains("Payment")', - }, { + }]; + } + function debt_method_paying(pay_method) { + return [{ extra_trigger: '.button.paymentmethod:contains("' + pay_method +'")', trigger: '.button.paymentmethod:contains("' + pay_method +'")', content: _t("Click the payment method"), - }, { + }]; + } + function validate_order() { + return [{ extra_trigger: '.button.next.highlight:contains("Validate")', trigger: '.button.next.highlight:contains("Validate")', content: 'Validate payment', - }, { - extra_trigger: '.pos-sale-ticket', - trigger: '.button.next.highlight:contains("Next Order")', - content: 'Check proceeded validation', }]; } + function close_error_modal_dialog(message) { + return [{ + trigger: '.popup>.body:contains("' + message +'")', + content: _t("check error-message"), + },{ + trigger: '.modal-dialog .button:contains("Ok")', + content: _t("Close alert-dialog"), + }]; + } + function click_numpad_input(char) { + return [{ + trigger: '.input-button:contains("' + char +'")', + content: _t("Click input on numpad"), + }]; + } + function next_order(){ + return [{ + trigger: '.button.next', + content: _t("Next order"), + }]; + } + function set_numpad_value(value) { + var steps = []; + for (var i = 0; i < value.length; i++) { + steps = steps.concat(click_numpad_input(value.charAt(i))); + } + return steps; + } + function remove_paymentline(){ + return [{ + trigger: '.paymentline>.delete-button', + content: _t("delete selected payment-line"), + }]; + } + function click_autopay_button(){ + return [{ + trigger: '.autopay', + content: _t("click autopay-button"), + }]; + } + /* -----Scenarios----- */ + // Initial Test "Pay with all credit-journal" + function initial_scene(steps, client) { + return steps.concat( + create_new_order(), + add_product_to_order('Lemon'), + click_to_payment(), + set_customer(client), + debt_method_paying('Credits (USD)'), + set_numpad_value("1"), + debt_method_paying('Credits (Fruits & Vegetables only) (USD)'), + set_numpad_value("1"), + debt_method_paying('Credits (via discounts) (USD)'), + validate_order(), + next_order(), + switch_table() + ); + } + // Error: "You cannot use Debt payment. Select customer first." + function scene_1(steps, client, err_msg) { + return steps.concat( + create_new_order(), + add_product_to_order("Miscellaneous"), + click_to_payment(), + set_customer(client), + debt_method_paying('Credits (USD)'), + deselect_customer(), + validate_order(), + close_error_modal_dialog(err_msg) + ); + } + // Error: "You cannot sell products on credit to the customer because his max debt value will be exceeded." + function scene_2(steps, client, err_msg) { + return steps.concat( + create_new_order(), + add_product_to_order("Miscellaneous"), + click_to_payment(), + set_customer(client), + debt_method_paying('Credits (USD)'), + set_numpad_value("10000"), + validate_order(), + close_error_modal_dialog(err_msg) + ); + } + // Scenario: Increase credit amount + function scene_3(steps, client) { + return steps.concat( + create_new_order(), + add_product_to_order("Pay Debt"), + click_to_payment(), + set_customer(client), + debt_method_paying('Cash (USD)'), + set_numpad_value("4"), + debt_method_paying('Credits (USD)'), + validate_order(), + next_order(), + switch_table() + ); + } + // Scenario: Transfer money from Credit to Credit (Fruit&Vegetables only) + function scene_4(steps, client) { + return steps.concat( + create_new_order(), + add_product_to_order("Pay Debt"), + click_to_payment(), + set_customer(client), + debt_method_paying('Credits (USD)'), + set_numpad_value("2"), + debt_method_paying('Credits (Fruits & Vegetables only) (USD)'), + validate_order(), + next_order(), + switch_table() + ); + } + // Error: "Please enter the exact or lower debt amount than the cost of the order" + function scene_5(steps, client, err_msg) { + return steps.concat( + create_new_order(), + add_product_to_order("Pay Debt"), + click_to_payment(), + set_customer(client), + debt_method_paying('Credits (Fruits & Vegetables only) (USD)'), + set_numpad_value("1"), + debt_method_paying('Credits (USD)'), + validate_order(), + close_error_modal_dialog(err_msg) + ); + } + // Error: "You may only buy Fruits and Vegetables with Credits (Fruits & Vegetables only) (USD)" + function scene_6(steps, client, err_msg) { + return steps.concat( + create_new_order(), + add_product_to_order("Miscellaneous"), + click_to_payment(), + set_customer(client), + remove_paymentline(), + debt_method_paying('Credits (Fruits & Vegetables only) (USD)'), + set_numpad_value("1"), + validate_order(), + close_error_modal_dialog(err_msg) + ); + } + // Scenario: Proceed order with autopay-button (bottom-left corner) + function scene_7(steps, client) { + return steps.concat( + create_new_order(), + add_product_to_order("Miscellaneous"), + click_to_payment(), + set_customer(client), + remove_paymentline(), + debt_method_paying('Credits (via discounts) (USD)'), + set_numpad_value("1"), + click_autopay_button(), + click_autopay_button() + ); + } var steps = []; + var client = "Agrolait"; + var err_msgs = [ + "You cannot use Debt payment. Select customer first.", + "You cannot sell products on credit journal Credits (USD) to the customer because its max debt value will be exceeded.", + "Please enter the exact or lower debt amount than the cost of the order.", + "You may only buy Fruits and Vegetables with Credits (Fruits & Vegetables only) (USD)", + ]; + steps = steps.concat(open_pos_neworder()); - steps = steps.concat(add_product_to_order('Miscellaneous')); - steps = steps.concat(set_customer('Agrolait')); - steps = steps.concat(debt_method_paying('Credits (USD)')); + steps = initial_scene(steps, client); + steps = scene_1(steps, client, err_msgs[0]); + steps = scene_2(steps, client, err_msgs[1]); + steps = scene_3(steps, client); + steps = scene_4(steps, client); + steps = scene_5(steps, client, err_msgs[2]); + steps = scene_6(steps, client, err_msgs[3]); + steps = scene_7(steps, client); tour.register('tour_pos_debt_notebook', { test: true, url: '/web' }, steps); - }); diff --git a/pos_debt_notebook/tests/test_pos_debt.py b/pos_debt_notebook/tests/test_pos_debt.py index f8e2c625c7..00fd3b123a 100644 --- a/pos_debt_notebook/tests/test_pos_debt.py +++ b/pos_debt_notebook/tests/test_pos_debt.py @@ -1,3 +1,5 @@ +# Copyright 2019 Anvar Kildebekov +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). import odoo.tests from odoo.api import Environment @@ -6,7 +8,7 @@ @odoo.tests.common.post_install(True) class TestUi(odoo.tests.HttpCase): - def test_pos_debt(self): + def common_test(self, test_login): # needed because tests are run before the module is marked as # installed. In js web will only load qweb coming from modules # that are returned by the backend in module_boot. Without @@ -14,11 +16,37 @@ def test_pos_debt(self): cr = self.registry.cursor() env = Environment(cr, self.uid, {}) env['ir.module.module'].search([('name', '=', 'pos_debt_notebook')], limit=1).state = 'installed' - cr.release() - # without a delay there might be problems on the steps whilst opening a POS # caused by a not yet loaded button's action + # Step 1: Init + # import wdb;wdb.set_trace() + test_partner = env['res.partner'].search([('name', '=', 'Agrolait')]) + test_journal = [ + env['account.journal'].search([('name', '=', 'Credits')]), + env['account.journal'].search([('name', '=', 'Credits (Fruits & Vegetables only)')]), + env['account.journal'].search([('name', '=', 'Credits (via discounts)')]) + ] + self.env['product.template'].search([("name", "=", "Lemon")]).write({ + 'list_price': 3.0, + 'lst_price': 3.0 + }) + self.env['product.template'].search([("name", "=", "Miscellaneous")]).write({ + 'list_price': 1.0, + 'lst_price': 1.0 + }) + cr.release() + # Step 2: Tour Test self.phantom_js("/web", - "odoo.__DEBUG__.services['web_tour.tour'].run('tour_pos_debt_notebook', 1000)", + "odoo.__DEBUG__.services['web_tour.tour'].run('tour_pos_debt_notebook', 500)", "odoo.__DEBUG__.services['web_tour.tour'].tours.tour_pos_debt_notebook.ready", - login="admin", timeout=140) + login=test_login, timeout=200) + # Step 3: Check debt-balance + # check for 'Credits' balance + self.assertEqual(test_partner.debt_history()[test_partner.id]['debts'][test_journal[0].id]['balance'], 1.0) + # check for 'Credits (Fruits&Vegetables)' balance + self.assertEqual(test_partner.debt_history()[test_partner.id]['debts'][test_journal[1].id]['balance'], 1.0) + # check for 'Credits (via discounts)' balance + self.assertEqual(test_partner.debt_history()[test_partner.id]['debts'][test_journal[2].id]['balance'], -2.0) + + def test_01_pos_debt(self): + self.common_test("admin") diff --git a/pos_debt_notebook_sync/__manifest__.py b/pos_debt_notebook_sync/__manifest__.py index 96fab9f627..701f4e7ed6 100644 --- a/pos_debt_notebook_sync/__manifest__.py +++ b/pos_debt_notebook_sync/__manifest__.py @@ -25,6 +25,7 @@ ], "external_dependencies": {"python": [], "bin": []}, "data": [ + 'views/assets.xml', "views/template.xml", "data/base_action_rule.xml", ], diff --git a/pos_debt_notebook_sync/static/src/js/test_debt_sync.js b/pos_debt_notebook_sync/static/src/js/test_debt_sync.js new file mode 100644 index 0000000000..1df72d8e3e --- /dev/null +++ b/pos_debt_notebook_sync/static/src/js/test_debt_sync.js @@ -0,0 +1,132 @@ +/* Copyright 2019 Anvar Kildebekov + License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). */ +odoo.define('pos_debt_notebook_sync.tour', function (require) { + "use strict"; + + var tour = require("web_tour.tour"); + var core = require('web.core'); + var _t = core._t; + + /* -----Actions----- */ + function open_pos_neworder() { + return [{ + trigger: '.o_app[data-menu-xmlid="point_of_sale.menu_point_root"], .oe_menu_toggler[data-menu-xmlid="point_of_sale.menu_point_root"]', + content: _t("Ready to launch your point of sale? Click here."), + position: 'bottom', + }, { + trigger: ".o_pos_kanban button.oe_kanban_action_button", + content: _t("

Click to start the point of sale interface. It runs on tablets, laptops, or industrial hardware.

Once the session launched, the system continues to run without an internet connection.

"), + position: "bottom" + }, { + content: "Switch to table or make dummy action", + trigger: '.table:not(.oe_invisible .neworder-button), .order-button.selected', + position: "bottom", + timeout: 20000, + }]; + } + function create_new_order() { + return [{ + content: 'Create new order', + trigger: '.order-button.neworder-button', + }]; + } + 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 + '")', + }]; + } + function switch_table() { + return [{ + content: "Switch to table or make dummy action", + trigger: '.table:not(.oe_invisible .neworder-button), .order-button.selected', + position: "bottom", + }]; + } + function set_customer(name) { + return [{ + trigger: '.button.js_set_customer', + content: _t("Open the customer screen"), + }, { + trigger: 'td:contains(' + name + ')', + content: _t("Click the customer"), + }, { + extra_trigger: '.button.next.highlight:contains("Set Customer")', + trigger: '.button.next.highlight:contains("Set Customer")', + content: 'Set Customer', + }]; + } + function click_to_payment() { + return [{ + content: "Make a dummy action", + trigger: '.order-button.selected', + }, { + trigger: '.button.pay', + content: _t("Open the payment screen"), + }, { + content: "Choose Administrator like a cashier or make a dummy action", + trigger: '.modal-dialog.cashier:not(.oe_hidden) .cashier .selection-item:contains("Administrator"), .payment-screen:not(.oe_hidden) h1:contains("Payment")', + }]; + } + function debt_method_paying(pay_method) { + return [{ + extra_trigger: '.button.paymentmethod:contains("' + pay_method +'")', + trigger: '.button.paymentmethod:contains("' + pay_method +'")', + content: _t("Click the payment method"), + }]; + } + function validate_order() { + return [{ + extra_trigger: '.button.next.highlight:contains("Validate")', + trigger: '.button.next.highlight:contains("Validate")', + content: 'Validate payment', + }]; + } + function click_numpad_input(char) { + return [{ + trigger: '.input-button:contains("' + char +'")', + content: _t("Click input on numpad"), + }]; + } + function next_order(){ + return [{ + trigger: '.button.next', + content: _t("Next order"), + }]; + } + function set_numpad_value(value) { + var steps = []; + for (var i = 0; i < value.length; i++) { + steps = steps.concat(click_numpad_input(value.charAt(i))); + } + return steps; + } + /* -----Scenarios----- */ + // Initial Test "Pay with all credit-journal" + function initial_scene(steps, client) { + return steps.concat( + create_new_order(), + add_product_to_order('Lemon'), + click_to_payment(), + set_customer(client), + debt_method_paying('Credits (USD)'), + set_numpad_value("1"), + debt_method_paying('Credits (Fruits & Vegetables only) (USD)'), + set_numpad_value("1"), + debt_method_paying('Credits (via discounts) (USD)'), + validate_order(), + next_order(), + switch_table() + ); + } + var steps = []; + var client = "Agrolait"; + steps = steps.concat(open_pos_neworder()); + steps = initial_scene(steps, client); + + tour.register('tour_pos_debt_notebook_sync', { test: true, url: '/web' }, steps); + +}); diff --git a/pos_debt_notebook_sync/tests/__init__.py b/pos_debt_notebook_sync/tests/__init__.py index 2b5a89e2b1..5895ad7c36 100644 --- a/pos_debt_notebook_sync/tests/__init__.py +++ b/pos_debt_notebook_sync/tests/__init__.py @@ -1,2 +1,4 @@ # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). from . import test_debt_sync +from . import test_pos_debt_sync + diff --git a/pos_debt_notebook_sync/tests/test_debt_sync.py b/pos_debt_notebook_sync/tests/test_debt_sync.py index 88d296b41e..02dbb93031 100644 --- a/pos_debt_notebook_sync/tests/test_debt_sync.py +++ b/pos_debt_notebook_sync/tests/test_debt_sync.py @@ -16,11 +16,11 @@ def test_pos_debt(self): # this you end up with js, css but no qweb. cr = self.registry.cursor() env = Environment(cr, self.uid, {}) - env['ir.module.module'].search([('name', '=', 'pos_debt_notebook')], limit=1).state = 'installed' + env['ir.module.module'].search([('name', '=', 'pos_debt_notebook_sync')], limit=1).state = 'installed' cr.release() # without a delay there might be problems caused by a not yet loaded button's action self.phantom_js("/web", - "odoo.__DEBUG__.services['web_tour.tour'].run('tour_pos_debt_notebook', 1000)", - "odoo.__DEBUG__.services['web_tour.tour'].tours.tour_pos_debt_notebook.ready", + "odoo.__DEBUG__.services['web_tour.tour'].run('tour_pos_debt_notebook_sync', 1000)", + "odoo.__DEBUG__.services['web_tour.tour'].tours.tour_pos_debt_notebook_sync.ready", login="admin", timeout=140) diff --git a/pos_debt_notebook_sync/views/assets.xml b/pos_debt_notebook_sync/views/assets.xml new file mode 100644 index 0000000000..c9ff2e0e9c --- /dev/null +++ b/pos_debt_notebook_sync/views/assets.xml @@ -0,0 +1,12 @@ + + + + + + +