mirror of
https://github.com/bringout/oca-ocb-pos.git
synced 2026-04-23 04:02:08 +02:00
933 lines
40 KiB
Python
933 lines
40 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import odoo.tests
|
|
from odoo.addons.point_of_sale.tests.common_setup_methods import setup_product_combo_items
|
|
from odoo.addons.point_of_sale.tests.common import archive_products
|
|
from odoo.addons.point_of_sale.tests.test_frontend import TestPointOfSaleHttpCommon
|
|
from odoo import Command
|
|
import json
|
|
from datetime import datetime, timedelta
|
|
|
|
@odoo.tests.tagged('post_install', '-at_install')
|
|
class TestFrontendCommon(TestPointOfSaleHttpCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
archive_products(cls.env)
|
|
|
|
food_category = cls.env['pos.category'].create({'name': 'Food', 'sequence': 1})
|
|
drinks_category = cls.env['pos.category'].create({'name': 'Drinks', 'sequence': 2})
|
|
|
|
printer = cls.env['pos.printer'].create({
|
|
'name': 'Preparation Printer',
|
|
'epson_printer_ip': '127.0.0.1',
|
|
'printer_type': 'epson_epos',
|
|
'product_categories_ids': [drinks_category.id]
|
|
})
|
|
|
|
main_company = cls.env.company
|
|
test_sale_journal_2 = cls.env['account.journal'].create({
|
|
'name': 'Sales Journal - Test2',
|
|
'code': 'TSJ2',
|
|
'type': 'sale',
|
|
'company_id': main_company.id
|
|
})
|
|
cash_journal_2 = cls.env['account.journal'].create({
|
|
'name': 'Cash 2',
|
|
'type': 'cash',
|
|
'company_id': main_company.id,
|
|
})
|
|
cls.pos_config = cls.env['pos.config'].create({
|
|
'name': 'Bar Prout',
|
|
'module_pos_restaurant': True,
|
|
'iface_splitbill': True,
|
|
'iface_printbill': True,
|
|
'is_order_printer': True,
|
|
'printer_ids': [(4, printer.id)],
|
|
'iface_tipproduct': False,
|
|
'company_id': cls.env.company.id,
|
|
'journal_id': test_sale_journal_2.id,
|
|
'invoice_journal_id': test_sale_journal_2.id,
|
|
'payment_method_ids': [
|
|
(4, cls.bank_payment_method.id),
|
|
(0, 0, {
|
|
'name': 'Cash',
|
|
'split_transactions': False,
|
|
'receivable_account_id': cls.account_receivable.id,
|
|
'journal_id': cash_journal_2.id,
|
|
})
|
|
],
|
|
})
|
|
cls.main_pos_config = cls.pos_config
|
|
|
|
cls.pos_config.floor_ids.unlink()
|
|
|
|
cls.main_floor = cls.env['restaurant.floor'].create({
|
|
'name': 'Main Floor',
|
|
'pos_config_ids': [(4, cls.pos_config.id)],
|
|
})
|
|
cls.second_floor = cls.env['restaurant.floor'].create({
|
|
'name': 'Second Floor',
|
|
'pos_config_ids': [(4, cls.pos_config.id)],
|
|
})
|
|
|
|
cls.main_floor_table_5 = cls.env['restaurant.table'].create([{
|
|
'table_number': 5,
|
|
'floor_id': cls.main_floor.id,
|
|
'seats': 4,
|
|
'position_h': 100,
|
|
'position_v': 100,
|
|
}])
|
|
cls.env['restaurant.table'].create([{
|
|
'table_number': 4,
|
|
'floor_id': cls.main_floor.id,
|
|
'seats': 4,
|
|
'shape': 'square',
|
|
'position_h': 350,
|
|
'position_v': 100,
|
|
},
|
|
{
|
|
'table_number': 2,
|
|
'floor_id': cls.main_floor.id,
|
|
'seats': 4,
|
|
'position_h': 250,
|
|
'position_v': 100,
|
|
},
|
|
{
|
|
|
|
'table_number': 1,
|
|
'floor_id': cls.second_floor.id,
|
|
'seats': 4,
|
|
'shape': 'square',
|
|
'position_h': 100,
|
|
'position_v': 150,
|
|
},
|
|
{
|
|
'table_number': 3,
|
|
'floor_id': cls.second_floor.id,
|
|
'seats': 4,
|
|
'position_h': 100,
|
|
'position_v': 250,
|
|
}])
|
|
|
|
cls.env['ir.default'].set(
|
|
'res.partner',
|
|
'property_account_receivable_id',
|
|
cls.account_receivable.id,
|
|
company_id=main_company.id,
|
|
)
|
|
|
|
cls.coca_cola_test = cls.env['product.product'].create({
|
|
'available_in_pos': True,
|
|
'list_price': 2.20,
|
|
'name': 'Coca-Cola',
|
|
'weight': 0.01,
|
|
'pos_categ_ids': [(4, drinks_category.id)],
|
|
'taxes_id': [(6, 0, [])],
|
|
})
|
|
|
|
cls.water_test = cls.env['product.product'].create({
|
|
'available_in_pos': True,
|
|
'list_price': 2.20,
|
|
'name': 'Water',
|
|
'weight': 0.01,
|
|
'pos_categ_ids': [(4, drinks_category.id)],
|
|
'taxes_id': [(6, 0, [])],
|
|
})
|
|
|
|
cls.minute_maid_test = cls.env['product.product'].create({
|
|
'available_in_pos': True,
|
|
'list_price': 2.20,
|
|
'name': 'Minute Maid',
|
|
'weight': 0.01,
|
|
'pos_categ_ids': [(4, drinks_category.id)],
|
|
'taxes_id': [(6, 0, [])],
|
|
})
|
|
|
|
# multiple categories product
|
|
cls.env['product.product'].create({
|
|
'available_in_pos': True,
|
|
'list_price': 2.20,
|
|
'name': 'Test Multi Category Product',
|
|
'weight': 0.01,
|
|
'pos_categ_ids': [(4, drinks_category.id), (4, food_category.id)],
|
|
'taxes_id': [(6, 0, [])],
|
|
})
|
|
|
|
# desk organizer (variant product)
|
|
cls.desk_organizer = cls.env['product.product'].create({
|
|
'name': 'Desk Organizer',
|
|
'available_in_pos': True,
|
|
'list_price': 5.10,
|
|
'pos_categ_ids': [(4, drinks_category.id)], # will put it as a drink for convenience
|
|
})
|
|
desk_size_attribute = cls.env['product.attribute'].create({
|
|
'name': 'Size',
|
|
'display_type': 'radio',
|
|
'create_variant': 'no_variant',
|
|
})
|
|
desk_size_s = cls.env['product.attribute.value'].create({
|
|
'name': 'S',
|
|
'attribute_id': desk_size_attribute.id,
|
|
})
|
|
desk_size_m = cls.env['product.attribute.value'].create({
|
|
'name': 'M',
|
|
'attribute_id': desk_size_attribute.id,
|
|
})
|
|
desk_size_l = cls.env['product.attribute.value'].create({
|
|
'name': 'L',
|
|
'attribute_id': desk_size_attribute.id,
|
|
})
|
|
cls.env['product.template.attribute.line'].create({
|
|
'product_tmpl_id': cls.desk_organizer.product_tmpl_id.id,
|
|
'attribute_id': desk_size_attribute.id,
|
|
'value_ids': [(6, 0, [desk_size_s.id, desk_size_m.id, desk_size_l.id])]
|
|
})
|
|
desk_fabrics_attribute = cls.env['product.attribute'].create({
|
|
'name': 'Fabric',
|
|
'display_type': 'select',
|
|
'create_variant': 'no_variant',
|
|
})
|
|
desk_fabrics_leather = cls.env['product.attribute.value'].create({
|
|
'name': 'Leather',
|
|
'attribute_id': desk_fabrics_attribute.id,
|
|
})
|
|
desk_fabrics_other = cls.env['product.attribute.value'].create({
|
|
'name': 'Custom',
|
|
'attribute_id': desk_fabrics_attribute.id,
|
|
'is_custom': True,
|
|
})
|
|
cls.env['product.template.attribute.line'].create({
|
|
'product_tmpl_id': cls.desk_organizer.product_tmpl_id.id,
|
|
'attribute_id': desk_fabrics_attribute.id,
|
|
'value_ids': [(6, 0, [desk_fabrics_leather.id, desk_fabrics_other.id])]
|
|
})
|
|
|
|
pricelist = cls.env['product.pricelist'].create({'name': 'Restaurant Pricelist'})
|
|
second_pricelist = cls.env['product.pricelist'].create({'name': 'Second Pricelist'})
|
|
cls.pos_config.write({'pricelist_id': pricelist.id})
|
|
cls.pos_config.write({'available_pricelist_ids': [(6, 0, [pricelist.id, second_pricelist.id])]})
|
|
|
|
|
|
class TestFrontend(TestFrontendCommon):
|
|
|
|
def test_01_pos_restaurant(self):
|
|
self.pos_user.write({
|
|
'group_ids': [
|
|
(4, self.env.ref('account.group_account_invoice').id),
|
|
]
|
|
})
|
|
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('pos_restaurant_sync')
|
|
|
|
self.assertEqual(1, self.env['pos.order'].search_count([('amount_total', '=', 4.4), ('state', '=', 'draft')]))
|
|
self.assertEqual(1, self.env['pos.order'].search_count([('amount_total', '=', 4.4), ('state', '=', 'paid')]))
|
|
|
|
self.start_pos_tour('pos_restaurant_sync_second_login')
|
|
|
|
self.assertEqual(0, self.env['pos.order'].search_count([('amount_total', '=', 4.4), ('state', '=', 'draft')]))
|
|
self.assertEqual(1, self.env['pos.order'].search_count([('amount_total', '=', 2.2), ('state', '=', 'draft')]))
|
|
self.assertEqual(2, self.env['pos.order'].search_count([('amount_total', '=', 4.4), ('state', '=', 'paid')]))
|
|
|
|
def test_02_others(self):
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('SplitBillScreenTour')
|
|
self.start_pos_tour('FloorScreenTour', login="pos_admin")
|
|
self.start_pos_tour('TableMergeUnmergeTour', login="pos_admin")
|
|
|
|
def test_02_others_bis(self):
|
|
# disable kitchen printer to avoid printing errors
|
|
self.pos_config.is_order_printer = False
|
|
self.pos_config.with_user(self.pos_admin).open_ui()
|
|
self.start_pos_tour('ControlButtonsTour', login="pos_admin")
|
|
|
|
def test_04_ticket_screen(self):
|
|
self.pos_config.is_order_printer = False
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('PosResTicketScreenTour')
|
|
|
|
def test_05_tip_screen(self):
|
|
self.pos_config.write({'set_tip_after_payment': True, 'iface_tipproduct': True, 'tip_product_id': self.env.ref('point_of_sale.product_product_tip')})
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('PosResTipScreenTour')
|
|
|
|
orders = self.env['pos.order'].search([], limit=5, order="id desc")
|
|
order_tips = [o.tip_amount for o in orders]
|
|
|
|
# orders order can be different depending on which module is install so we sort the tips
|
|
order_tips.sort()
|
|
self.assertEqual(order_tips, [0.0, 0.4, 1.0, 1.0, 1.5])
|
|
|
|
order4 = self.env['pos.order'].search([('pos_reference', 'ilike', '%-000004')], limit=1, order='id desc')
|
|
self.assertEqual(order4.customer_count, 2)
|
|
|
|
def test_06_split_bill_screen(self):
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('SplitBillScreenTour2')
|
|
orders = self.env['pos.order'].search([('pos_reference', '!=', '')], limit=2, order='id desc')
|
|
self.assertEqual(len(orders), 2)
|
|
|
|
def test_07_split_bill_screen(self):
|
|
# disable kitchen printer to avoid printing errors
|
|
self.pos_config.is_order_printer = False
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('SplitBillScreenTour3')
|
|
self.start_pos_tour('SplitBillScreenTourPay')
|
|
|
|
def test_08_refund_stay_current_table(self):
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('RefundStayCurrentTableTour')
|
|
|
|
def test_09_combo_split_bill(self):
|
|
setup_product_combo_items(self)
|
|
self.office_combo.product_variant_id.write({'lst_price': 40})
|
|
# disable kitchen printer to avoid printing errors
|
|
self.pos_config.is_order_printer = False
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('SplitBillScreenTour4ProductCombo')
|
|
|
|
def test_10_save_last_preparation_changes(self):
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('SaveLastPreparationChangesTour')
|
|
self.assertTrue(self.pos_config.current_session_id.order_ids.last_order_preparation_change, "There should be a last order preparation change")
|
|
self.assertTrue("Coca" in self.pos_config.current_session_id.order_ids.last_order_preparation_change, "The last order preparation change should contain 'Coca'")
|
|
|
|
def test_12_order_tracking(self):
|
|
self.pos_config.write({'order_edit_tracking': True})
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('OrderTrackingTour')
|
|
order1 = self.env['pos.order'].search([('pos_reference', 'ilike', '%-000001')], limit=1, order='id desc')
|
|
self.assertTrue(order1.is_edited)
|
|
|
|
def test_13_category_check(self):
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('CategLabelCheck')
|
|
|
|
def test_14_change_synced_order(self):
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('OrderChange')
|
|
|
|
def test_13_crm_team(self):
|
|
if self.env['ir.module.module']._get('pos_sale').state != 'installed':
|
|
self.skipTest("'pos_sale' module is required")
|
|
sale_team = self.env['crm.team'].search([], limit=1)
|
|
self.pos_config.crm_team_id = sale_team
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('CrmTeamTour')
|
|
order = self.env['pos.order'].search([], limit=1)
|
|
self.assertEqual(order.crm_team_id.id, sale_team.id)
|
|
|
|
def test_14_pos_payment_sync(self):
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
def assert_payment(lines_count, amount):
|
|
self.assertEqual(len(order.payment_ids), lines_count)
|
|
self.assertEqual(round(sum(payment.amount for payment in order.payment_ids), 2), amount)
|
|
self.start_pos_tour('PoSPaymentSyncTour1')
|
|
order = self.pos_config.current_session_id.order_ids
|
|
self.assertEqual(len(order), 1)
|
|
assert_payment(1, 2.2)
|
|
self.start_pos_tour('PoSPaymentSyncTour2')
|
|
assert_payment(1, 4.4)
|
|
self.start_pos_tour('PoSPaymentSyncTour3')
|
|
assert_payment(2, 6.6)
|
|
|
|
def test_15_split_bill_screen_actions(self):
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.pos_config.write({'printer_ids': False})
|
|
self.start_pos_tour('SplitBillScreenTour5Actions')
|
|
|
|
def test_pos_restaurant_course(self):
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_pos_restaurant_course')
|
|
order = self.pos_config.current_session_id.order_ids
|
|
self.assertEqual(len(order), 1)
|
|
# Verify whether the two courses have different timestamps
|
|
self.assertNotEqual(order.course_ids[0].fired_date, order.course_ids[1].fired_date)
|
|
|
|
def test_preparation_printer_content(self):
|
|
self.preset_eat_in = self.env['pos.preset'].create({
|
|
'name': 'Eat in',
|
|
})
|
|
self.preset_takeaway = self.env['pos.preset'].create({
|
|
'name': 'Takeaway',
|
|
'identification': 'name',
|
|
})
|
|
self.main_pos_config.write({
|
|
'use_presets': True,
|
|
'default_preset_id': self.preset_eat_in.id,
|
|
'available_preset_ids': [(6, 0, [self.preset_takeaway.id])],
|
|
})
|
|
resource_calendar = self.env['resource.calendar'].create({
|
|
'name': 'Takeaway',
|
|
'attendance_ids': [(0, 0, {
|
|
'name': 'Takeaway',
|
|
'dayofweek': str(day),
|
|
'hour_from': 0,
|
|
'hour_to': 24,
|
|
'day_period': 'morning',
|
|
}) for day in range(0, 7)],
|
|
})
|
|
self.preset_takeaway.write({
|
|
'use_timing': True,
|
|
'resource_calendar_id': resource_calendar
|
|
})
|
|
self.env['pos.printer'].create({
|
|
'name': 'Printer',
|
|
'printer_type': 'epson_epos',
|
|
'epson_printer_ip': '0.0.0.0',
|
|
'product_categories_ids': [Command.set(self.env['pos.category'].search([]).ids)],
|
|
})
|
|
|
|
self.main_pos_config.write({
|
|
'is_order_printer': True,
|
|
'printer_ids': [Command.set(self.env['pos.printer'].search([]).ids)],
|
|
})
|
|
|
|
self.product_test = self.env['product.product'].create({
|
|
'name': 'Product Test',
|
|
'available_in_pos': True,
|
|
'list_price': 10,
|
|
'pos_categ_ids': [(6, 0, [self.env['pos.category'].search([], limit=1).id])],
|
|
'taxes_id': False,
|
|
})
|
|
|
|
attribute = self.env['product.attribute'].create({
|
|
'name': 'Attribute 1',
|
|
'create_variant': 'no_variant',
|
|
})
|
|
attribute_value = self.env['product.attribute.value'].create({
|
|
'name': 'Value 1',
|
|
'attribute_id': attribute.id,
|
|
})
|
|
attribute_value_2 = self.env['product.attribute.value'].create({
|
|
'name': 'Value 2',
|
|
'attribute_id': attribute.id,
|
|
})
|
|
self.env['product.template.attribute.line'].create({
|
|
'product_tmpl_id': self.product_test.product_tmpl_id.id,
|
|
'attribute_id': attribute.id,
|
|
'value_ids': [(6, 0, [attribute_value.id, attribute_value_2.id])],
|
|
})
|
|
|
|
attribute_2 = self.env['product.attribute'].create({
|
|
'name': 'Attribute 1',
|
|
'create_variant': 'always',
|
|
})
|
|
attribute_2_value = self.env['product.attribute.value'].create({
|
|
'name': 'Value 1',
|
|
'attribute_id': attribute_2.id,
|
|
})
|
|
attribute_2_value_2 = self.env['product.attribute.value'].create({
|
|
'name': 'Value 2',
|
|
'attribute_id': attribute_2.id,
|
|
})
|
|
self.env['product.template.attribute.line'].create({
|
|
'product_tmpl_id': self.product_test.product_tmpl_id.id,
|
|
'attribute_id': attribute_2.id,
|
|
'value_ids': [(6, 0, [attribute_2_value.id, attribute_2_value_2.id])],
|
|
})
|
|
self.main_pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_tour(f"/pos/ui/{self.main_pos_config.id}", 'PreparationPrinterContent', login="pos_user")
|
|
|
|
def test_course_restaurant_preparation_tour(self):
|
|
self.env['pos.printer'].create({
|
|
'name': 'Printer',
|
|
'printer_type': 'epson_epos',
|
|
'epson_printer_ip': '0.0.0.0',
|
|
'product_categories_ids': [Command.set(self.env['pos.category'].search([]).ids)],
|
|
})
|
|
|
|
self.main_pos_config.write({
|
|
'is_order_printer': True,
|
|
'printer_ids': [Command.set(self.env['pos.printer'].search([]).ids)],
|
|
})
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_course_restaurant_preparation_tour', login="pos_user")
|
|
|
|
def test_create_floor_tour(self):
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_create_floor_tour', login="pos_admin")
|
|
|
|
def test_combo_preparation_receipt(self):
|
|
setup_product_combo_items(self)
|
|
pos_printer = self.env['pos.printer'].create({
|
|
'name': 'Printer',
|
|
'printer_type': 'epson_epos',
|
|
'epson_printer_ip': '0.0.0.0',
|
|
'product_categories_ids': [Command.set(self.env['pos.category'].search([]).ids)],
|
|
})
|
|
self.pos_config.write({
|
|
'is_order_printer' : True,
|
|
'printer_ids': [Command.set(pos_printer.ids)],
|
|
})
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_combo_preparation_receipt')
|
|
|
|
def test_multiple_preparation_printer(self):
|
|
"""This test make sure that no empty receipt are sent when using multiple printer with different categories
|
|
The tour will check that we tried did not try to print two receipt. We can achieve that by checking the content
|
|
of the error message. Because we do not have real printer an error message will be displayed, this will contain
|
|
all the receipt that failed to print. If it contains more than 1 it means that we tried to print a second receipt
|
|
and it should not be the case here. The only one we should see is 'Detailed Receipt'
|
|
"""
|
|
pos_category_1 = self.env['pos.category'].create({'name': 'Category 1'})
|
|
pos_category_2 = self.env['pos.category'].create({'name': 'Category 2'})
|
|
printer_1 = self.env['pos.printer'].create({
|
|
'name': 'Printer 1',
|
|
'printer_type': 'epson_epos',
|
|
'epson_printer_ip': '0.0.0.0',
|
|
'product_categories_ids': [Command.set(pos_category_2.ids)],
|
|
})
|
|
printer_2 = self.env['pos.printer'].create({
|
|
'name': 'Printer 2',
|
|
'printer_type': 'epson_epos',
|
|
'epson_printer_ip': '0.0.0.0',
|
|
'product_categories_ids': [Command.set(pos_category_1.ids)],
|
|
})
|
|
|
|
|
|
self.main_pos_config.write({
|
|
'is_order_printer' : True,
|
|
'printer_ids': [Command.set([printer_1.id, printer_2.id])],
|
|
})
|
|
|
|
self.product_1 = self.env['product.product'].create({
|
|
'name': 'Product 1',
|
|
'available_in_pos': True,
|
|
'list_price': 10,
|
|
'pos_categ_ids': [(6, 0, [pos_category_1.id])],
|
|
'taxes_id': False,
|
|
})
|
|
|
|
self.main_pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_tour(f"/pos/ui/{self.main_pos_config.id}", 'MultiPreparationPrinter', login="pos_user")
|
|
|
|
def test_user_on_residual_order(self):
|
|
self.pos_config.write({'printer_ids': False})
|
|
self.pos_config.with_user(self.pos_admin).open_ui()
|
|
self.start_pos_tour('LeaveResidualOrder', login="pos_admin")
|
|
self.start_pos_tour('FinishResidualOrder', login="pos_user")
|
|
orders = self.env['pos.order'].search([])
|
|
self.assertEqual(orders[0].user_id.id, self.pos_user.id, "Pos user not registered on order")
|
|
self.assertEqual(orders[1].user_id.id, self.pos_admin.id, "Pos admin not registered on order")
|
|
|
|
def test_tax_in_merge_table_order_line(self):
|
|
"""
|
|
Test that when merging orders of two tables in POS restaurant, the product tax is applied on the order lines of the destination table.
|
|
"""
|
|
drinks_category = self.env['pos.category'].search([('name', '=', 'Drinks'), ('sequence', '=', 2)])
|
|
product_1 = self.env['product.product'].create({
|
|
'available_in_pos': True,
|
|
'list_price': 2.20,
|
|
'name': 'product_1',
|
|
'taxes_id': self.tax_sale_a,
|
|
'pos_categ_ids': [(4, drinks_category.id)]
|
|
})
|
|
product_2 = self.env['product.product'].create({
|
|
'available_in_pos': True,
|
|
'list_price': 2.20,
|
|
'name': 'product_2',
|
|
'taxes_id': self.tax_sale_a,
|
|
'pos_categ_ids': [(4, drinks_category.id)]
|
|
})
|
|
self.pos_config.is_order_printer = False
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_tax_in_merge_table_order_line_tour', login="pos_admin")
|
|
line_1 = self.env['pos.order.line'].search([('full_product_name', '=', 'product_1')])
|
|
line_2 = self.env['pos.order.line'].search([('full_product_name', '=', 'product_2')])
|
|
self.assertEqual(line_1.tax_ids, self.tax_sale_a)
|
|
self.assertEqual(line_2.tax_ids, self.tax_sale_a)
|
|
|
|
def test_no_ghost_floor(self):
|
|
self.pos_config.is_order_printer = False
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('no_ghost_floor', login="pos_admin")
|
|
|
|
def test_multiple_preparation_printer_different_categories(self):
|
|
"""This test make sure that no empty receipt are sent when using multiple printer with different categories
|
|
The tour will check that we tried did not try to print two receipt. We can achieve that by checking the content
|
|
of the error message. Because we do not have real printer an error message will be displayed, this will contain
|
|
all the receipt that failed to print. If it contains more than 1 it means that we tried to print a second receipt
|
|
and it should not be the case here. The only one we should see is 'Detailed Receipt'
|
|
"""
|
|
pos_category_1 = self.env['pos.category'].create({'name': 'Category 1'})
|
|
pos_category_2 = self.env['pos.category'].create({'name': 'Category 2'})
|
|
printer_1 = self.env['pos.printer'].create({
|
|
'name': 'Printer 1',
|
|
'printer_type': 'epson_epos',
|
|
'epson_printer_ip': '0.0.0.0',
|
|
'product_categories_ids': [Command.set(pos_category_2.ids)],
|
|
})
|
|
printer_2 = self.env['pos.printer'].create({
|
|
'name': 'Printer 2',
|
|
'printer_type': 'epson_epos',
|
|
'epson_printer_ip': '0.0.0.0',
|
|
'product_categories_ids': [Command.set(pos_category_1.ids)],
|
|
})
|
|
|
|
self.main_pos_config.write({
|
|
'is_order_printer': True,
|
|
'printer_ids': [Command.set([printer_1.id, printer_2.id])],
|
|
})
|
|
|
|
self.product_1 = self.env['product.product'].create({
|
|
'name': 'Product 1',
|
|
'available_in_pos': True,
|
|
'list_price': 10,
|
|
'pos_categ_ids': [(6, 0, [pos_category_1.id])],
|
|
'taxes_id': False,
|
|
})
|
|
|
|
self.product_2 = self.env['product.product'].create({
|
|
'name': 'Product 2',
|
|
'available_in_pos': True,
|
|
'list_price': 10,
|
|
'pos_categ_ids': [(6, 0, [pos_category_2.id])],
|
|
'taxes_id': False,
|
|
})
|
|
|
|
self.main_pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_tour(f"/pos/ui/{self.main_pos_config.id}", 'test_multiple_preparation_printer_different_categories', login="pos_user")
|
|
|
|
def test_preset_timing_restaurant(self):
|
|
"""
|
|
Test to set order preset hour inside a tour
|
|
"""
|
|
self.preset_eat_in = self.env['pos.preset'].create({
|
|
'name': 'Eat in',
|
|
})
|
|
self.preset_takeaway = self.env['pos.preset'].create({
|
|
'name': 'Takeaway',
|
|
'identification': 'name',
|
|
})
|
|
self.preset_delivery = self.env['pos.preset'].create({
|
|
'name': 'Delivery',
|
|
'identification': 'address',
|
|
})
|
|
self.main_pos_config.write({
|
|
'use_presets': True,
|
|
'default_preset_id': self.preset_delivery.id,
|
|
'available_preset_ids': [(6, 0, [
|
|
self.preset_takeaway.id,
|
|
self.preset_eat_in.id,
|
|
self.preset_delivery.id,
|
|
])],
|
|
})
|
|
self.start_pos_tour('test_preset_delivery_restaurant')
|
|
resource_calendar = self.env['resource.calendar'].create({
|
|
'name': 'Takeaway',
|
|
'attendance_ids': [(0, 0, {
|
|
'name': 'Takeaway',
|
|
'dayofweek': str(day),
|
|
'hour_from': 0,
|
|
'hour_to': 24,
|
|
'day_period': 'morning',
|
|
}) for day in range(0, 7)],
|
|
})
|
|
self.preset_takeaway.write({
|
|
'use_timing': True,
|
|
'resource_calendar_id': resource_calendar
|
|
})
|
|
self.main_pos_config.write({'default_preset_id': self.preset_takeaway.id})
|
|
self.start_pos_tour('test_open_register_with_preset_takeaway')
|
|
self.main_pos_config.write({'default_preset_id': self.preset_eat_in.id})
|
|
cancelled_orders = self.env['pos.order'].search([('state', '=', 'cancel')], limit=3)
|
|
self.assertEqual(len(cancelled_orders), 1)
|
|
orders = self.env['pos.order'].search([('state', '!=', 'cancel')], limit=3)
|
|
self.assertEqual(len(orders), 0)
|
|
self.start_pos_tour('test_preset_timing_restaurant')
|
|
self.preset_eat_in.write({
|
|
'use_guest': True,
|
|
})
|
|
self.start_pos_tour('test_guest_count_bank_payment')
|
|
|
|
def test_preset_future_timing_restaurant(self):
|
|
"""
|
|
Test to set order preset future date inside a tour
|
|
"""
|
|
self.preset_eat_in = self.env['pos.preset'].create({
|
|
'name': 'Eat in',
|
|
})
|
|
self.preset_takeaway = self.env['pos.preset'].create({
|
|
'name': 'Takeaway',
|
|
'identification': 'name',
|
|
})
|
|
self.main_pos_config.write({
|
|
'use_presets': True,
|
|
'default_preset_id': self.preset_eat_in.id,
|
|
'available_preset_ids': [(6, 0, [self.preset_takeaway.id])],
|
|
})
|
|
resource_calendar = self.env['resource.calendar'].create({
|
|
'name': 'Takeaway',
|
|
'attendance_ids': [(0, 0, {
|
|
'name': 'Takeaway',
|
|
'dayofweek': str(day),
|
|
'hour_from': 0,
|
|
'hour_to': 24,
|
|
'day_period': 'morning',
|
|
}) for day in range(0, 7)],
|
|
})
|
|
self.preset_takeaway.write({
|
|
'use_timing': True,
|
|
'resource_calendar_id': resource_calendar
|
|
})
|
|
self.start_pos_tour('test_cancel_future_order')
|
|
|
|
def test_restaurant_preset_eatin_tour(self):
|
|
self.pos_config.write({
|
|
'use_presets': True,
|
|
'default_preset_id': self.env.ref('pos_restaurant.pos_takein_preset', False).id,
|
|
})
|
|
self.pos_user.name = "test_user"
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('RestaurantPresetEatInTour', login="pos_user")
|
|
|
|
def test_combo_preparation_receipt_layout(self):
|
|
setup_product_combo_items(self)
|
|
pos_printer = self.env['pos.printer'].create({
|
|
'name': 'Printer',
|
|
'printer_type': 'epson_epos',
|
|
'epson_printer_ip': '0.0.0.0',
|
|
'product_categories_ids': [Command.set(self.env['pos.category'].search([]).ids)],
|
|
})
|
|
self.pos_config.write({
|
|
'is_order_printer': True,
|
|
'printer_ids': [Command.set(pos_printer.ids)],
|
|
})
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_tour(f"/pos/ui/{self.pos_config.id}", 'test_combo_preparation_receipt_layout', login="pos_user")
|
|
|
|
def test_tip_after_payment(self):
|
|
self.pos_config.write({'iface_tipproduct': True, 'tip_product_id': self.tip.id})
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_tip_after_payment')
|
|
|
|
def test_customer_alone_saved(self):
|
|
"""
|
|
Tests that when a customer is set, it will be saved and not be reset even if this is the only thing that changed in the order
|
|
"""
|
|
self.start_tour(f"/pos/ui?config_id={self.main_pos_config.id}", 'test_customer_alone_saved', login="pos_user")
|
|
|
|
def test_open_default_register_screen_config(self):
|
|
"""
|
|
Tests that the default register screen is opened when the config is set to do so
|
|
"""
|
|
self.pos_config.write({'default_screen': 'register'})
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_open_default_register_screen_config')
|
|
|
|
def test_fast_payment_validation_from_restaurant_product_screen_with_automatic_receipt_printing(self):
|
|
self.env['pos.printer'].create({
|
|
'name': 'Printer',
|
|
'printer_type': 'epson_epos',
|
|
'epson_printer_ip': '0.0.0.0',
|
|
'product_categories_ids': [Command.set(self.env['pos.category'].search([]).ids)],
|
|
})
|
|
self.main_pos_config.write({
|
|
'use_fast_payment': True,
|
|
'fast_payment_method_ids': [(6, 0, self.bank_payment_method.ids)],
|
|
'is_order_printer': True,
|
|
'printer_ids': [Command.set(self.env['pos.printer'].search([]).ids)],
|
|
'iface_print_auto': True,
|
|
'iface_print_skip_screen': True,
|
|
'other_devices': True,
|
|
'epson_printer_ip': '127.0.0.1:8069/receipt_receiver',
|
|
})
|
|
self.main_pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_fast_payment_validation_from_restaurant_product_screen_with_automatic_receipt_printing')
|
|
order = self.main_pos_config.current_session_id.order_ids[0]
|
|
self.assertEqual(order.state, 'paid', "The order should be paid after the fast payment validation")
|
|
self.assertEqual(len(order.payment_ids), 1, "There should be one payment method used for the fast payment")
|
|
self.assertEqual(order.payment_ids.payment_method_id, self.bank_payment_method, "The payment method used should be the bank payment method")
|
|
|
|
def test_fast_payment_validation_from_restaurant_product_screen_without_automatic_receipt_printing(self):
|
|
self.env['pos.printer'].create({
|
|
'name': 'Printer',
|
|
'printer_type': 'epson_epos',
|
|
'epson_printer_ip': '0.0.0.0',
|
|
'product_categories_ids': [Command.set(self.env['pos.category'].search([]).ids)],
|
|
})
|
|
self.main_pos_config.write({
|
|
'use_fast_payment': True,
|
|
'fast_payment_method_ids': [(6, 0, self.bank_payment_method.ids)],
|
|
'is_order_printer': True,
|
|
'printer_ids': [Command.set(self.env['pos.printer'].search([]).ids)],
|
|
'other_devices': True,
|
|
'epson_printer_ip': '127.0.0.1:8069/receipt_receiver',
|
|
})
|
|
self.main_pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_fast_payment_validation_from_restaurant_product_screen_without_automatic_receipt_printing')
|
|
order = self.main_pos_config.current_session_id.order_ids[0]
|
|
self.assertEqual(order.state, 'paid', "The order should be paid after the fast payment validation")
|
|
self.assertEqual(len(order.payment_ids), 1, "There should be one payment method used for the fast payment")
|
|
self.assertEqual(order.payment_ids.payment_method_id, self.bank_payment_method, "The payment method used should be the bank payment method")
|
|
|
|
def test_transfering_orders(self):
|
|
"""
|
|
We can now transfer order from one table to another and from floating order to another etc.
|
|
"""
|
|
self.start_pos_tour('test_transfering_orders', login="pos_user")
|
|
|
|
def test_direct_sales(self):
|
|
"""
|
|
Direct sales should not be synced without table_id or floating_order_name, if an order is
|
|
sync without one of them, we assign pos_reference in floating_order_name.
|
|
"""
|
|
self.start_pos_tour('test_direct_sales', login="pos_user")
|
|
orders = self.env['pos.order'].search([], limit=3, order='id desc')
|
|
self.assertEqual(orders[2].floating_order_name, orders[2].tracking_number)
|
|
self.assertEqual(orders[1].floating_order_name, "Test")
|
|
self.assertEqual(orders[0].floating_order_name, False)
|
|
self.assertIsNotNone(orders[0].table_id)
|
|
|
|
def test_cancel_order_from_ui(self):
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_cancel_order_from_ui')
|
|
order = self.pos_config.current_session_id.order_ids[0]
|
|
self.assertEqual(order.state, "cancel", "The order should be in cancel state")
|
|
|
|
def test_sync_lines_qty_update(self):
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_sync_lines_qty_update')
|
|
order = self.pos_config.current_session_id.order_ids[0]
|
|
self.assertEqual(order.lines[0].qty, 3)
|
|
|
|
def test_sync_lines_qty_update_ticket_screen(self):
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_sync_lines_qty_update_ticket_screen')
|
|
order = self.pos_config.current_session_id.order_ids[0]
|
|
self.assertEqual(order.lines[0].qty, 3, "Quantity should be updated to 3 in the backend")
|
|
|
|
def test_sync_set_partner(self):
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_sync_set_partner')
|
|
order = self.pos_config.current_session_id.order_ids[0]
|
|
self.assertEqual(order.partner_id.name, "Acme Corporation")
|
|
|
|
def test_sync_set_note(self):
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_sync_set_note')
|
|
order = self.pos_config.current_session_id.order_ids[0]
|
|
note = json.loads(order.internal_note)
|
|
self.assertEqual(note[0]["text"], "Hello world")
|
|
|
|
def test_sync_set_line_note(self):
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_sync_set_line_note')
|
|
order = self.pos_config.current_session_id.order_ids[0]
|
|
note = json.loads(order.lines[0].note)
|
|
self.assertEqual(note[0]["text"], "Demo note")
|
|
|
|
def test_sync_set_pricelist(self):
|
|
self.pos_config.write({
|
|
'use_pricelist': True,
|
|
})
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_sync_set_pricelist')
|
|
order = self.pos_config.current_session_id.order_ids[0]
|
|
self.assertEqual(order.pricelist_id.name, "Second Pricelist")
|
|
|
|
def test_delete_line_release_table(self):
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_delete_line_release_table')
|
|
order = self.pos_config.current_session_id.order_ids[0]
|
|
self.assertEqual(order.state, "cancel")
|
|
|
|
def test_combo_synchronisation(self):
|
|
"""This test checks that when a combo line is set as dirty, the parent combo line is also set as dirty.
|
|
if this is not the case, the combo lines would lose their link to the parent combo line and appear as
|
|
normal line"""
|
|
setup_product_combo_items(self)
|
|
self.pos_config.is_order_printer = False
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_combo_synchronisation')
|
|
|
|
def test_global_discount_split(self):
|
|
if self.env['ir.module.module']._get('pos_discount').state != 'installed':
|
|
self.skipTest("pos_discount module is required for this test")
|
|
|
|
self.discount_product = self.env["product.product"].create({
|
|
"name": "Discount Product",
|
|
"type": "service",
|
|
"list_price": 0,
|
|
"available_in_pos": True,
|
|
})
|
|
|
|
self.pos_config.write({
|
|
'iface_discount': True,
|
|
'module_pos_discount': True,
|
|
'discount_product_id': self.discount_product.id,
|
|
})
|
|
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('SplitBillScreenTourTransfer')
|
|
|
|
def test_name_preset_skip_screen(self):
|
|
self.preset_takeaway = self.env['pos.preset'].create({
|
|
'name': 'Takeaway',
|
|
'identification': 'name',
|
|
})
|
|
self.pos_config.write({
|
|
'use_presets': True,
|
|
'default_preset_id': self.preset_takeaway.id,
|
|
'available_preset_ids': [(6, 0, [self.preset_takeaway.id])],
|
|
})
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
self.start_pos_tour('test_name_preset_skip_screen')
|
|
|
|
def test_future_orders_are_not_cancelled(self):
|
|
"""
|
|
This test ensures that a future order is not cancelled when the PoS session is closed.
|
|
"""
|
|
self.pos_config.with_user(self.pos_user).open_ui()
|
|
|
|
session = self.pos_config.current_session_id
|
|
product = self.env['product.product'].search([('available_in_pos', '=', True)], limit=1)
|
|
|
|
present_order = self.env['pos.order'].create({
|
|
'company_id': self.env.company.id,
|
|
'session_id': session.id,
|
|
'lines': [(0, 0, {
|
|
'name': "OL/0001",
|
|
'product_id': product.id,
|
|
'price_unit': 10.0,
|
|
'discount': 0.0,
|
|
'qty': 1.0,
|
|
'tax_ids': False,
|
|
'price_subtotal': 10.0,
|
|
'price_subtotal_incl': 10.0,
|
|
})],
|
|
'amount_tax': 0.0,
|
|
'amount_total': 10.0,
|
|
'amount_paid': 0.0,
|
|
'amount_return': 0.0,
|
|
'preset_time': datetime.now(),
|
|
})
|
|
future_order = self.env['pos.order'].create({
|
|
'company_id': self.env.company.id,
|
|
'session_id': session.id,
|
|
'lines': [(0, 0, {
|
|
'name': "OL/0002",
|
|
'product_id': product.id,
|
|
'price_unit': 10.0,
|
|
'discount': 0.0,
|
|
'qty': 1.0,
|
|
'tax_ids': False,
|
|
'price_subtotal': 10.0,
|
|
'price_subtotal_incl': 10.0,
|
|
})],
|
|
'amount_tax': 0.0,
|
|
'amount_total': 10.0,
|
|
'amount_paid': 0.0,
|
|
'amount_return': 0.0,
|
|
'preset_time': datetime.now() + timedelta(days=1),
|
|
})
|
|
|
|
self.start_pos_tour('test_futur_orders_are_not_cancelled')
|
|
self.pos_config.current_session_id.close_session_from_ui()
|
|
self.assertEqual(present_order.state, 'cancel')
|
|
self.assertEqual(future_order.state, 'draft')
|
|
self.assertEqual(future_order.session_id.id, False)
|