Files
odoo_source/addons/point_of_sale/tests/test_pos_margin.py
T
Jacky (trj) 442f856bb8 [IMP] point_of_sale: opening cash control and closing session in POS UI
The opening cash control has been refactored and the closing of the session is now happening in the POS UI. This also leads to a cash control in the UI during the closing.

The advanced cash control is no longer a setting to activate in the config by the user but a computed field based on the presence of a cash payment method. We force the user to always have it now.

The opening cash control has been revamped with the addition of a new money calculator (`MoneyDetails`) allowing the user to easily compute how much money does he have based on his bills.

The closing control has been converted into a popup which allows the users to get an overview of his session details (about orders, payments, payment methods, cash moves, ...). If the `cash_control` has been set to `True`, the user still needs to count his money by introducing it or using the new calculator.

When trying to close the session, if it fails, depending on the error the user get, he can be redirected to the back end to manually close the session. At this point, the user is not able to open the POS UI.
(In order to fully bring the closing of the session in the front end, all the error handlings need to be brought to the front end as well which takes a lot more time.)

Rescue session can only be closed through the back end and the closing cash control is automatically being computed without the user's involvement. This avoid any cash profit/loss in the journals.

The session chatter is being logged with all the details regarding the opening and closing. This give the user a better view of his cash flow in a specific session.

task-2456424

closes odoo/odoo#73464

Signed-off-by: pimodoo <pimodoo@users.noreply.github.com>
2021-09-28 09:28:50 +00:00

401 lines
15 KiB
Python

# -*- coding: utf-8 -*-
import odoo
from odoo.addons.point_of_sale.tests.common import TestPoSCommon
@odoo.tests.tagged('post_install', '-at_install')
class TestPosMargin(TestPoSCommon):
"""
Test the margin computation on orders with basic configuration
The tests contain the base scenarios.
"""
def setUp(self):
super(TestPosMargin, self).setUp()
self.config = self.basic_config
self.stock_location = self.env['stock.warehouse'].create({
'partner_id': self.env.user.partner_id.id,
'name': 'Stock location',
'code': 'WH'
}).lot_stock_id
self.customer_location = self.env.ref('stock.stock_location_customers')
self.supplier_location = self.env.ref('stock.stock_location_suppliers')
self.uom_unit = self.env.ref('uom.product_uom_unit')
def test_positive_margin(self):
"""
Test margin where it should be more than zero
"""
product1 = self.create_product('Product 1', self.categ_basic, 10, 5)
product2 = self.create_product('Product 2', self.categ_basic, 50, 30)
# open a session
self.open_new_session()
# create orders
orders = [self.create_ui_order_data([(product1, 1)]),
self.create_ui_order_data([(product2, 1)]),
self.create_ui_order_data([(product1, 2), (product2, 2)])]
# sync orders
self.env['pos.order'].create_from_ui(orders)
# check margins
self.assertEqual(self.pos_session.order_ids[0].margin, 5)
self.assertEqual(self.pos_session.order_ids[1].margin, 20)
self.assertEqual(self.pos_session.order_ids[2].margin, 50)
# check margins percent
self.assertEqual(self.pos_session.order_ids[0].margin_percent, 0.5)
self.assertEqual(self.pos_session.order_ids[1].margin_percent, 0.4)
self.assertEqual(round(self.pos_session.order_ids[2].margin_percent, 2), 0.42)
# close session
self.pos_session.action_pos_session_validate()
def test_negative_margin(self):
"""
Test margin where it should be less than zero
"""
product1 = self.create_product('Product 1', self.categ_basic, 10, 15)
product2 = self.create_product('Product 2', self.categ_basic, 50, 100)
# open a session
self.open_new_session()
# create orders
orders = [self.create_ui_order_data([(product1, 1)]),
self.create_ui_order_data([(product2, 1)]),
self.create_ui_order_data([(product1, 2), (product2, 2)])]
# sync orders
self.env['pos.order'].create_from_ui(orders)
# check margins
self.assertEqual(self.pos_session.order_ids[0].margin, -5)
self.assertEqual(self.pos_session.order_ids[1].margin, -50)
self.assertEqual(self.pos_session.order_ids[2].margin, -110)
# check margins percent
self.assertEqual(self.pos_session.order_ids[0].margin_percent, -0.5)
self.assertEqual(self.pos_session.order_ids[1].margin_percent, -1)
self.assertEqual(round(self.pos_session.order_ids[2].margin_percent, 2), -0.92)
# close session
self.pos_session.action_pos_session_validate()
def test_full_margin(self):
"""
Test margin where the product cost is always 0
"""
product1 = self.create_product('Product 1', self.categ_basic, 10)
product2 = self.create_product('Product 2', self.categ_basic, 50)
# open a session
self.open_new_session()
# create orders
orders = [self.create_ui_order_data([(product1, 1)]),
self.create_ui_order_data([(product2, 1)]),
self.create_ui_order_data([(product1, 2), (product2, 2)])]
# sync orders
self.env['pos.order'].create_from_ui(orders)
# check margins
self.assertEqual(self.pos_session.order_ids[0].margin, 10)
self.assertEqual(self.pos_session.order_ids[1].margin, 50)
self.assertEqual(self.pos_session.order_ids[2].margin, 120)
# check margins percent
self.assertEqual(self.pos_session.order_ids[0].margin_percent, 1)
self.assertEqual(self.pos_session.order_ids[1].margin_percent, 1)
self.assertEqual(self.pos_session.order_ids[2].margin_percent, 1)
# close session
self.pos_session.action_pos_session_validate()
def test_tax_margin(self):
"""
Test margin with tax on products
Product 1 price without tax = 10
Product 2 price without tax = 50
"""
product1 = self.create_product('Product 1', self.categ_basic, 10, 5, self.taxes['tax7'].ids)
product2 = self.create_product('Product 2', self.categ_basic, 55, 30, self.taxes['tax10'].ids)
# open a session
self.open_new_session()
# create orders
orders = [self.create_ui_order_data([(product1, 1)]),
self.create_ui_order_data([(product2, 1)]),
self.create_ui_order_data([(product1, 2), (product2, 2)])]
# sync orders
self.env['pos.order'].create_from_ui(orders)
# check margins
self.assertEqual(self.pos_session.order_ids[0].margin, 5)
self.assertEqual(self.pos_session.order_ids[1].margin, 20)
self.assertEqual(self.pos_session.order_ids[2].margin, 50)
# check margins percent
self.assertEqual(self.pos_session.order_ids[0].margin_percent, 0.5)
self.assertEqual(self.pos_session.order_ids[1].margin_percent, 0.4)
self.assertEqual(round(self.pos_session.order_ids[2].margin_percent, 2), 0.42)
# close session
self.pos_session.action_pos_session_validate()
def test_other_currency_margin(self):
"""
Test margin with tax on products and with different currency
The currency rate is 0.5 so the product price is halved in this currency.
"""
# change the config
current_config = self.config
self.config = self.other_currency_config
# same parameters as test_positive_margin
product1 = self.create_product('Product 1', self.categ_basic, 10, 5)
product2 = self.create_product('Product 2', self.categ_basic, 50, 30)
# open a session
self.open_new_session()
# create orders
orders = [self.create_ui_order_data([(product1, 1)]),
self.create_ui_order_data([(product2, 1)]),
self.create_ui_order_data([(product1, 2), (product2, 2)])]
# sync orders
self.env['pos.order'].create_from_ui(orders)
# check margins in the config currency
self.assertEqual(self.pos_session.order_ids[0].margin, 2.5)
self.assertEqual(self.pos_session.order_ids[1].margin, 10)
self.assertEqual(self.pos_session.order_ids[2].margin, 25)
# check margins percent which should be the same as test_positive_margin
self.assertEqual(self.pos_session.order_ids[0].margin_percent, 0.5)
self.assertEqual(self.pos_session.order_ids[1].margin_percent, 0.4)
self.assertEqual(round(self.pos_session.order_ids[2].margin_percent, 2), 0.42)
# close session
self.pos_session.action_pos_session_validate()
# set the config back
self.config = current_config
def test_tax_and_other_currency_margin(self):
"""
Test margin with different currency between products and config with taxes.
Product 1 price without tax = 10
Product 2 price without tax = 50
The currency rate is 0.5 so the product price is halved in this currency.
"""
# change the config
current_config = self.config
self.config = self.other_currency_config
product1 = self.create_product('Product 1', self.categ_basic, 10, 5, self.taxes['tax7'].ids)
product2 = self.create_product('Product 2', self.categ_basic, 55, 30, self.taxes['tax10'].ids)
# open a session
self.open_new_session()
# create orders
orders = [self.create_ui_order_data([(product1, 1)]),
self.create_ui_order_data([(product2, 1)]),
self.create_ui_order_data([(product1, 2), (product2, 2)])]
# sync orders
self.env['pos.order'].create_from_ui(orders)
# check margins in the config currency
self.assertEqual(self.pos_session.order_ids[0].margin, 2.5)
self.assertEqual(self.pos_session.order_ids[1].margin, 10)
self.assertEqual(self.pos_session.order_ids[2].margin, 25)
# check margins percent which should be the same as test_tax_margin
self.assertEqual(self.pos_session.order_ids[0].margin_percent, 0.5)
self.assertEqual(self.pos_session.order_ids[1].margin_percent, 0.4)
self.assertEqual(self.pos_session.order_ids[2].margin_percent, 0.4167)
# close session
self.pos_session.action_pos_session_validate()
# set the config back
self.config = current_config
def test_return_margin(self):
"""
Test margin where we return product (negative line quantity)
"""
product1 = self.create_product('Product 1', self.categ_basic, 10, 5)
product2 = self.create_product('Product 2', self.categ_basic, 50, 30)
# open a session
self.open_new_session()
# create orders
orders = [self.create_ui_order_data([(product1, -1)]),
self.create_ui_order_data([(product2, -1)]),
self.create_ui_order_data([(product1, -2), (product2, -2)])]
# sync orders
self.env['pos.order'].create_from_ui(orders)
# check margins
self.assertEqual(self.pos_session.order_ids[0].margin, -5)
self.assertEqual(self.pos_session.order_ids[1].margin, -20)
self.assertEqual(self.pos_session.order_ids[2].margin, -50)
# check margins percent
self.assertEqual(self.pos_session.order_ids[0].margin_percent, 0.5)
self.assertEqual(self.pos_session.order_ids[1].margin_percent, 0.4)
self.assertEqual(round(self.pos_session.order_ids[2].margin_percent, 2), 0.42)
# close session
self.pos_session.action_pos_session_validate()
def test_fifo_margin_real_time(self):
"""
Test margin where there is product in FIFO with stock update in real time
"""
product1 = self.create_product('Product 1', self.categ_anglo, 10, 5)
product2 = self.create_product('Product 2', self.categ_basic, 50, 30)
move1 = self.env['stock.move'].create({
'name': 'IN 2 unit @ 3 per unit',
'location_id': self.supplier_location.id,
'location_dest_id': self.stock_location.id,
'product_id': product1.id,
'product_uom': self.uom_unit.id,
'product_uom_qty': 2,
'price_unit': 3,
}).sudo()
move1._action_confirm()
move1._action_assign()
move1.move_line_ids.qty_done = 2
move1._action_done()
move2 = self.env['stock.move'].create({
'name': 'IN 1 unit @ 7 per unit',
'location_id': self.supplier_location.id,
'location_dest_id': self.stock_location.id,
'product_id': product1.id,
'product_uom': self.uom_unit.id,
'product_uom_qty': 1,
'price_unit': 7,
}).sudo()
move2._action_confirm()
move2._action_assign()
move2.move_line_ids.qty_done = 1
move2._action_done()
# open a session
self.open_new_session()
# create orders
orders = [self.create_ui_order_data([(product1, 1), (product2, 1)]),
self.create_ui_order_data([(product1, 2)])]
# sync orders
self.env['pos.order'].create_from_ui(orders)
# check margins
self.assertEqual(self.pos_session.order_ids[0].margin, 27)
self.assertEqual(self.pos_session.order_ids[1].margin, 10)
# check margins percent
self.assertEqual(self.pos_session.order_ids[0].margin_percent, 0.45)
self.assertEqual(self.pos_session.order_ids[1].margin_percent, 0.5)
# close session
self.pos_session.action_pos_session_validate()
def test_avco_margin_closing_time(self):
"""
Test margin where there is product in AVCO with stock update in closing
"""
self.categ_anglo.property_cost_method = 'average'
product1 = self.create_product('Product 1', self.categ_anglo, 10, 5)
product2 = self.create_product('Product 2', self.categ_basic, 50, 30)
self.env.company.point_of_sale_update_stock_quantities = 'closing'
move1 = self.env['stock.move'].create({
'name': 'IN 2 unit @ 3 per unit',
'location_id': self.supplier_location.id,
'location_dest_id': self.stock_location.id,
'product_id': product1.id,
'product_uom': self.uom_unit.id,
'product_uom_qty': 2,
'price_unit': 3,
}).sudo()
move1._action_confirm()
move1._action_assign()
move1.move_line_ids.qty_done = 2
move1._action_done()
move2 = self.env['stock.move'].create({
'name': 'IN 1 unit @ 6 per unit',
'location_id': self.supplier_location.id,
'location_dest_id': self.stock_location.id,
'product_id': product1.id,
'product_uom': self.uom_unit.id,
'product_uom_qty': 1,
'price_unit': 6,
}).sudo()
move2._action_confirm()
move2._action_assign()
move2.move_line_ids.qty_done = 1
move2._action_done()
# open a session
self.open_new_session()
# create orders
orders = [self.create_ui_order_data([(product1, 1), (product2, 1)]),
self.create_ui_order_data([(product1, 2)])]
# sync orders
self.env['pos.order'].create_from_ui(orders)
# check margins which are not really computed so it should be 0
self.assertEqual(self.pos_session.order_ids[0].margin, 0)
self.assertEqual(self.pos_session.order_ids[1].margin, 0)
# check margins percent (same as above)
self.assertEqual(self.pos_session.order_ids[1].margin_percent, 0)
self.assertEqual(self.pos_session.order_ids[1].margin_percent, 0)
# close session
total_cash_payment = sum(self.pos_session.mapped('order_ids.payment_ids').filtered(lambda payment: payment.payment_method_id.type == 'cash').mapped('amount'))
self.pos_session.post_closing_cash_details(total_cash_payment)
self.pos_session.close_session_from_ui()
# check margins
self.assertEqual(self.pos_session.order_ids[0].margin, 26)
self.assertEqual(self.pos_session.order_ids[1].margin, 12)
# check margins percent
self.assertEqual(self.pos_session.order_ids[0].margin_percent, 0.4333)
self.assertEqual(self.pos_session.order_ids[1].margin_percent, 0.6)
self.env.company.point_of_sale_update_stock_quantities = 'real'