# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. import odoo from odoo import fields from odoo.addons.point_of_sale.tests.common import TestPoSCommon from freezegun import freeze_time from dateutil.relativedelta import relativedelta @odoo.tests.tagged('post_install', '-at_install') class TestPoSBasicConfig(TestPoSCommon): """ Test PoS with basic configuration The tests contain base scenarios in using pos. More specialized cases are tested in other tests. """ def setUp(self): super(TestPoSBasicConfig, self).setUp() self.config = self.basic_config self.product0 = self.create_product('Product 0', self.categ_basic, 0.0, 0.0) self.product1 = self.create_product('Product 1', self.categ_basic, 10.0, 5) self.product2 = self.create_product('Product 2', self.categ_basic, 20.0, 10) self.product3 = self.create_product('Product 3', self.categ_basic, 30.0, 15) self.product4 = self.create_product('Product_4', self.categ_basic, 9.96, 4.98) self.product99 = self.create_product('Product_99', self.categ_basic, 99, 50) self.product_multi_tax = self.create_product('Multi-tax product', self.categ_basic, 100, 100, (self.taxes['tax8'] | self.taxes['tax9']).ids) self.adjust_inventory([self.product1, self.product2, self.product3], [100, 50, 50]) def test_orders_no_invoiced(self): """ Test for orders without invoice 3 orders - first 2 orders with cash payment - last order with bank payment Orders ====== +---------+----------+-----------+----------+-----+-------+ | order | payments | invoiced? | product | qty | total | +---------+----------+-----------+----------+-----+-------+ | order 1 | cash | no | product1 | 10 | 100 | | | | | product2 | 5 | 100 | +---------+----------+-----------+----------+-----+-------+ | order 2 | cash | no | product2 | 7 | 140 | | | | | product3 | 1 | 30 | +---------+----------+-----------+----------+-----+-------+ | order 3 | bank | no | product1 | 1 | 10 | | | | | product2 | 3 | 60 | | | | | product3 | 5 | 150 | +---------+----------+-----------+----------+-----+-------+ Expected Result =============== +---------------------+---------+ | account | balance | +---------------------+---------+ | sale | -590 | | pos receivable cash | 370 | | pos receivable bank | 220 | +---------------------+---------+ | Total balance | 0.0 | +---------------------+---------+ """ start_qty_available = { self.product1: self.product1.qty_available, self.product2: self.product2.qty_available, self.product3: self.product3.qty_available, } def _before_closing_cb(): # check values before closing the session self.assertEqual(3, self.pos_session.order_count) orders_total = sum(order.amount_total for order in self.pos_session.order_ids) self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.') # check product qty_available after syncing the order self.assertEqual( self.product1.qty_available + 11, start_qty_available[self.product1], ) self.assertEqual( self.product2.qty_available + 15, start_qty_available[self.product2], ) self.assertEqual( self.product3.qty_available + 6, start_qty_available[self.product3], ) # picking and stock moves should be in done state for order in self.pos_session.order_ids: self.assertEqual( order.picking_ids[0].state, 'done', 'Picking should be in done state.' ) move_ids = order.picking_ids[0].move_ids self.assertEqual( move_ids.mapped('state'), ['done'] * len(move_ids), 'Move Lines should be in done state.' ) self._run_test({ 'payment_methods': self.cash_pm1 | self.bank_pm1, 'orders': [ {'pos_order_lines_ui_args': [(self.product1, 10), (self.product2, 5)], 'uid': '00100-010-0001'}, {'pos_order_lines_ui_args': [(self.product2, 7), (self.product3, 1)], 'uid': '00100-010-0002'}, {'pos_order_lines_ui_args': [(self.product1, 1), (self.product3, 5), (self.product2, 3)], 'payments': [(self.bank_pm1, 220)], 'uid': '00100-010-0003'}, ], 'before_closing_cb': _before_closing_cb, 'journal_entries_before_closing': {}, 'journal_entries_after_closing': { 'session_journal_entry': { 'line_ids': [ {'account_id': self.sales_account.id, 'partner_id': False, 'debit': 0, 'credit': 590, 'reconciled': False}, {'account_id': self.bank_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 220, 'credit': 0, 'reconciled': True}, {'account_id': self.cash_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 370, 'credit': 0, 'reconciled': True}, ], }, 'cash_statement': [ ((370, ), { 'line_ids': [ {'account_id': self.cash_pm1.journal_id.default_account_id.id, 'partner_id': False, 'debit': 370, 'credit': 0, 'reconciled': False}, {'account_id': self.cash_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 0, 'credit': 370, 'reconciled': True}, ] }), ], 'bank_payments': [ ((220, ), { 'line_ids': [ {'account_id': self.bank_pm1.outstanding_account_id.id, 'partner_id': False, 'debit': 220, 'credit': 0, 'reconciled': False}, {'account_id': self.bank_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 0, 'credit': 220, 'reconciled': True}, ] }), ], }, }) def test_orders_with_invoiced(self): """ Test for orders: one with invoice 3 orders - order 1, paid by cash - order 2, paid by bank - order 3, paid by bank, invoiced Orders ====== +---------+----------+---------------+----------+-----+-------+ | order | payments | invoiced? | product | qty | total | +---------+----------+---------------+----------+-----+-------+ | order 1 | cash | no | product1 | 6 | 60 | | | | | product2 | 3 | 60 | | | | | product3 | 1 | 30 | +---------+----------+---------------+----------+-----+-------+ | order 2 | bank | no | product1 | 1 | 10 | | | | | product2 | 20 | 400 | +---------+----------+---------------+----------+-----+-------+ | order 3 | bank | yes, customer | product1 | 10 | 100 | | | | | product3 | 1 | 30 | +---------+----------+---------------+----------+-----+-------+ Expected Result =============== +---------------------+---------+ | account | balance | +---------------------+---------+ | sale | -560 | | pos receivable cash | 150 | | pos receivable bank | 540 | | receivable | -130 | +---------------------+---------+ | Total balance | 0.0 | +---------------------+---------+ """ start_qty_available = { self.product1: self.product1.qty_available, self.product2: self.product2.qty_available, self.product3: self.product3.qty_available, } def _before_closing_cb(): # check values before closing the session self.assertEqual(3, self.pos_session.order_count) orders_total = sum(order.amount_total for order in self.pos_session.order_ids) self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.') # check product qty_available after syncing the order self.assertEqual( self.product1.qty_available + 17, start_qty_available[self.product1], ) self.assertEqual( self.product2.qty_available + 23, start_qty_available[self.product2], ) self.assertEqual( self.product3.qty_available + 2, start_qty_available[self.product3], ) # picking and stock moves should be in done state # no exception for invoiced orders for order in self.pos_session.order_ids: self.assertEqual( order.picking_ids[0].state, 'done', 'Picking should be in done state.' ) move_ids = order.picking_ids[0].move_ids self.assertEqual( move_ids.mapped('state'), ['done'] * len(move_ids), 'Move Lines should be in done state.' ) # check account move in the invoiced order invoiced_order = self.pos_session.order_ids.filtered(lambda order: order.account_move) self.assertEqual(1, len(invoiced_order), 'Only one order is invoiced in this test.') # check state of orders before validating the session. self.assertEqual('invoiced', invoiced_order.state, msg="state should be 'invoiced' for invoiced orders.") uninvoiced_orders = self.pos_session.order_ids - invoiced_order self.assertTrue( all([order.state == 'paid' for order in uninvoiced_orders]), msg="state should be 'paid' for uninvoiced orders before validating the session." ) def _after_closing_cb(): # check state of orders after validating the session. uninvoiced_orders = self.pos_session.order_ids.filtered(lambda order: not order.is_invoiced) self.assertTrue( all([order.state == 'done' for order in uninvoiced_orders]), msg="State should be 'done' for uninvoiced orders after validating the session." ) self._run_test({ 'payment_methods': self.cash_pm1 | self.bank_pm1, 'orders': [ {'pos_order_lines_ui_args': [(self.product1, 6), (self.product2, 3), (self.product3, 1), ], 'payments': [(self.cash_pm1, 150)], 'uid': '00100-010-0001'}, {'pos_order_lines_ui_args': [(self.product1, 1), (self.product2, 20), ], 'payments': [(self.bank_pm1, 410)], 'uid': '00100-010-0002'}, {'pos_order_lines_ui_args': [(self.product1, 10), (self.product3, 1), ], 'payments': [(self.bank_pm1, 130)], 'is_invoiced': True, 'customer': self.customer, 'uid': '00100-010-0003'}, ], 'before_closing_cb': _before_closing_cb, 'journal_entries_before_closing': { '00100-010-0003': { 'invoice': { 'line_ids': [ {'account_id': self.sales_account.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 100, 'reconciled': False}, {'account_id': self.sales_account.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 30, 'reconciled': False}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 130, 'credit': 0, 'reconciled': True}, ] }, 'payments': [ ((self.bank_pm1, 130), { 'line_ids': [ {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 130, 'reconciled': True}, {'account_id': self.pos_receivable_account.id, 'partner_id': False, 'debit': 130, 'credit': 0, 'reconciled': False}, ] }), ], } }, 'after_closing_cb': _after_closing_cb, 'journal_entries_after_closing': { 'session_journal_entry': { 'line_ids': [ {'account_id': self.sales_account.id, 'partner_id': False, 'debit': 0, 'credit': 560, 'reconciled': False}, {'account_id': self.bank_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 540, 'credit': 0, 'reconciled': True}, {'account_id': self.cash_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 150, 'credit': 0, 'reconciled': True}, {'account_id': self.pos_receivable_account.id, 'partner_id': False, 'debit': 0, 'credit': 130, 'reconciled': True}, ], }, 'cash_statement': [ ((150, ), { 'line_ids': [ {'account_id': self.cash_pm1.journal_id.default_account_id.id, 'partner_id': False, 'debit': 150, 'credit': 0, 'reconciled': False}, {'account_id': self.cash_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 0, 'credit': 150, 'reconciled': True}, ] }), ], 'bank_payments': [ ((540, ), { 'line_ids': [ {'account_id': self.bank_pm1.outstanding_account_id.id, 'partner_id': False, 'debit': 540, 'credit': 0, 'reconciled': False}, {'account_id': self.bank_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 0, 'credit': 540, 'reconciled': True}, ] }), ], }, }) def test_orders_with_zero_valued_invoiced(self): """One invoiced order but with zero receivable line balance.""" self._run_test({ 'payment_methods': self.cash_pm1 | self.bank_pm1, 'orders': [ {'pos_order_lines_ui_args': [(self.product0, 1)], 'payments': [(self.bank_pm1, 0)], 'customer': self.customer, 'is_invoiced': True, 'uid': '00100-010-0001'}, ], 'journal_entries_before_closing': { '00100-010-0001': { 'invoice': { 'line_ids': [ {'account_id': self.sales_account.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 0, 'reconciled': False}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 0, 'reconciled': True}, ] }, 'payments': [ ((self.bank_pm1, 0), False), ], } }, 'journal_entries_after_closing': { 'session_journal_entry': False, 'cash_statement': [], 'bank_payments': [], }, }) def test_return_order_invoiced(self): def _before_closing_cb(): order = self.pos_session.order_ids.filtered(lambda order: '666-666-666' in order.pos_reference) # refund order.refund() refund_order = self.pos_session.order_ids.filtered(lambda order: order.state == 'draft') # pay the refund context_make_payment = {"active_ids": [refund_order.id], "active_id": refund_order.id} make_payment = self.env['pos.make.payment'].with_context(context_make_payment).create({ 'payment_method_id': self.cash_pm1.id, 'amount': -100, }) make_payment.check() # invoice refund refund_order.action_pos_order_invoice() self._run_test({ 'payment_methods': self.cash_pm1 | self.bank_pm1, 'orders': [ {'pos_order_lines_ui_args': [(self.product1, 10)], 'payments': [(self.cash_pm1, 100)], 'customer': self.customer, 'is_invoiced': True, 'uid': '666-666-666'}, ], 'before_closing_cb': _before_closing_cb, 'journal_entries_before_closing': { '666-666-666': { 'invoice': { 'line_ids': [ {'account_id': self.sales_account.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 100, 'reconciled': False}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 100, 'credit': 0, 'reconciled': True}, ] }, 'payments': [ ((self.cash_pm1, 100), { 'line_ids': [ {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 100, 'reconciled': True}, {'account_id': self.pos_receivable_account.id, 'partner_id': False, 'debit': 100, 'credit': 0, 'reconciled': False}, ] }), ], } }, 'journal_entries_after_closing': { 'session_journal_entry': { 'line_ids': [ {'account_id': self.pos_receivable_account.id, 'partner_id': False, 'debit': 0, 'credit': 0, 'reconciled': True}, ], }, 'cash_statement': [], 'bank_payments': [], }, }) def test_return_order(self): """ Test return order 2 orders - 2nd order is returned Orders ====== +------------------+----------+-----------+----------+-----+-------+ | order | payments | invoiced? | product | qty | total | +------------------+----------+-----------+----------+-----+-------+ | order 1 | bank | no | product1 | 1 | 10 | | | | | product2 | 5 | 100 | +------------------+----------+-----------+----------+-----+-------+ | order 2 | cash | no | product1 | 3 | 30 | | | | | product2 | 2 | 40 | | | | | product3 | 1 | 30 | +------------------+----------+-----------+----------+-----+-------+ | order 3 (return) | cash | no | product1 | -3 | -30 | | | | | product2 | -2 | -40 | | | | | product3 | -1 | -30 | +------------------+----------+-----------+----------+-----+-------+ Expected Result =============== +---------------------+---------+ | account | balance | +---------------------+---------+ | sale (sales) | -210 | | sale (refund) | 100 | | pos receivable bank | 110 | +---------------------+---------+ | Total balance | 0.0 | +---------------------+---------+ """ start_qty_available = { self.product1: self.product1.qty_available, self.product2: self.product2.qty_available, self.product3: self.product3.qty_available, } def _before_closing_cb(): # check values before closing the session self.assertEqual(2, self.pos_session.order_count) orders_total = sum(order.amount_total for order in self.pos_session.order_ids) self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.') # return order order_to_return = self.pos_session.order_ids.filtered(lambda order: '12345-123-1234' in order.pos_reference) order_to_return.refund() refund_order = self.pos_session.order_ids.filtered(lambda order: order.state == 'draft') # check if amount to pay self.assertAlmostEqual(refund_order.amount_total - refund_order.amount_paid, -100) # pay the refund context_make_payment = {"active_ids": [refund_order.id], "active_id": refund_order.id} make_payment = self.env['pos.make.payment'].with_context(context_make_payment).create({ 'payment_method_id': self.cash_pm1.id, 'amount': -100, }) make_payment.check() self.assertEqual(refund_order.state, 'paid', 'Payment is registered, order should be paid.') self.assertAlmostEqual(refund_order.amount_paid, -100.0, msg='Amount paid for return order should be negative.') # check product qty_available after syncing the order self.assertEqual( self.product1.qty_available + 1, start_qty_available[self.product1], ) self.assertEqual( self.product2.qty_available + 5, start_qty_available[self.product2], ) self.assertEqual( self.product3.qty_available, start_qty_available[self.product3], ) # picking and stock moves should be in done state # no exception of return orders for order in self.pos_session.order_ids: self.assertEqual( order.picking_ids[0].state, 'done', 'Picking should be in done state.' ) move_ids = order.picking_ids[0].move_ids self.assertEqual( move_ids.mapped('state'), ['done'] * len(move_ids), 'Move Lines should be in done state.' ) self._run_test({ 'payment_methods': self.cash_pm1 | self.bank_pm1, 'orders': [ {'pos_order_lines_ui_args': [(self.product1, 1), (self.product2, 5)], 'payments': [(self.bank_pm1, 110)], 'uid': '00100-010-0001'}, {'pos_order_lines_ui_args': [(self.product1, 3), (self.product2, 2), (self.product3, 1)], 'payments': [(self.cash_pm1, 100)], 'uid': '12345-123-1234'}, ], 'before_closing_cb': _before_closing_cb, 'journal_entries_before_closing': {}, 'journal_entries_after_closing': { 'session_journal_entry': { 'line_ids': [ {'account_id': self.sales_account.id, 'partner_id': False, 'debit': 0, 'credit': 210, 'reconciled': False}, {'account_id': self.sales_account.id, 'partner_id': False, 'debit': 100, 'credit': 0, 'reconciled': False}, {'account_id': self.bank_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 110, 'credit': 0, 'reconciled': True}, ], }, 'cash_statement': [], 'bank_payments': [ ((110, ), { 'line_ids': [ {'account_id': self.bank_pm1.outstanding_account_id.id, 'partner_id': False, 'debit': 110, 'credit': 0, 'reconciled': False}, {'account_id': self.bank_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 0, 'credit': 110, 'reconciled': True}, ] }), ], }, }) def test_split_cash_payments(self): self._run_test({ 'payment_methods': self.cash_split_pm1 | self.bank_pm1, 'orders': [ {'pos_order_lines_ui_args': [(self.product1, 10), (self.product2, 5)], 'payments': [(self.cash_split_pm1, 100), (self.bank_pm1, 100)], 'customer': self.customer, 'is_invoiced': False, 'uid': '00100-010-0001'}, {'pos_order_lines_ui_args': [(self.product2, 7), (self.product3, 1)], 'payments': [(self.cash_split_pm1, 70), (self.bank_pm1, 100)], 'customer': self.customer, 'is_invoiced': False, 'uid': '00100-010-0002'}, {'pos_order_lines_ui_args': [(self.product1, 1), (self.product3, 5), (self.product2, 3)], 'payments': [(self.cash_split_pm1, 120), (self.bank_pm1, 100)], 'customer': self.customer, 'is_invoiced': False, 'uid': '00100-010-0003'}, ], 'journal_entries_before_closing': {}, 'journal_entries_after_closing': { 'session_journal_entry': { 'line_ids': [ {'account_id': self.sales_account.id, 'partner_id': False, 'debit': 0, 'credit': 590, 'reconciled': False}, {'account_id': self.bank_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 300, 'credit': 0, 'reconciled': True}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 100, 'credit': 0, 'reconciled': True}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 70, 'credit': 0, 'reconciled': True}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 120, 'credit': 0, 'reconciled': True}, ], }, 'cash_statement': [ ((100, ), { 'line_ids': [ {'account_id': self.cash_split_pm1.journal_id.default_account_id.id, 'partner_id': self.customer.id, 'debit': 100, 'credit': 0, 'reconciled': False}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 100, 'reconciled': True}, ] }), ((70, ), { 'line_ids': [ {'account_id': self.cash_split_pm1.journal_id.default_account_id.id, 'partner_id': self.customer.id, 'debit': 70, 'credit': 0, 'reconciled': False}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 70, 'reconciled': True}, ] }), ((120, ), { 'line_ids': [ {'account_id': self.cash_split_pm1.journal_id.default_account_id.id, 'partner_id': self.customer.id, 'debit': 120, 'credit': 0, 'reconciled': False}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 120, 'reconciled': True}, ] }), ], 'bank_payments': [ ((300, ), { 'line_ids': [ {'account_id': self.bank_pm1.outstanding_account_id.id, 'partner_id': False, 'debit': 300, 'credit': 0, 'reconciled': False}, {'account_id': self.bank_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 0, 'credit': 300, 'reconciled': True}, ] }) ], }, }) def test_rounding_method(self): # set the cash rounding method self.config.cash_rounding = True self.config.rounding_method = self.env['account.cash.rounding'].create({ 'name': 'add_invoice_line', 'rounding': 0.05, 'strategy': 'add_invoice_line', 'profit_account_id': self.company['default_cash_difference_income_account_id'].copy().id, 'loss_account_id': self.company['default_cash_difference_expense_account_id'].copy().id, 'rounding_method': 'HALF-UP', }) self.open_new_session() """ Test for orders: one with invoice 3 orders - order 1, paid by cash - order 2, paid by bank - order 3, paid by bank, invoiced Orders ====== +---------+----------+---------------+----------+-----+-------+ | order | payments | invoiced? | product | qty | total | +---------+----------+---------------+----------+-----+-------+ | order 1 | bank | no | product1 | 6 | 60 | | | | | product4 | 4 | 39.84 | +---------+----------+---------------+----------+-----+-------+ | order 2 | bank | yes | product4 | 3 | 29.88 | | | | | product2 | 20 | 400 | +---------+----------+---------------+----------+-----+-------+ Expected Result =============== +---------------------+---------+ | account | balance | +---------------------+---------+ | sale | -596,56 | | pos receivable bank | 516,64 | | Rounding applied | -0,01 | +---------------------+---------+ | Total balance | 0.0 | +---------------------+---------+ """ # create orders orders = [] # create orders orders = [] orders.append(self.create_ui_order_data( [(self.product4, 3), (self.product2, 20)], payments=[(self.bank_pm1, 429.90)] )) orders.append(self.create_ui_order_data( [(self.product1, 6), (self.product4, 4)], payments=[(self.bank_pm1, 99.85)] )) # sync orders order = self.env['pos.order'].create_from_ui(orders) self.assertEqual(orders[0]['data']['amount_return'], 0, msg='The amount return should be 0') self.assertEqual(orders[1]['data']['amount_return'], 0, msg='The amount return should be 0') # close the session self.pos_session.action_pos_session_validate() # check values after the session is closed session_account_move = self.pos_session.move_id rounding_line = session_account_move.line_ids.filtered(lambda line: line.name == 'Rounding line') self.assertAlmostEqual(rounding_line.credit, 0.03, msg='The credit should be equals to 0.03') def test_correct_partner_on_invoice_receivables(self): self._run_test({ 'payment_methods': self.cash_pm1 | self.cash_split_pm1 | self.bank_pm1 | self.bank_split_pm1, 'orders': [ {'pos_order_lines_ui_args': [(self.product1, 10)], 'payments':[(self.cash_pm1, 100)], 'customer': self.customer, 'is_invoiced': True, 'uid': '00100-010-0001'}, {'pos_order_lines_ui_args': [(self.product1, 10)], 'payments':[(self.bank_pm1, 100)], 'customer': self.customer, 'is_invoiced': True, 'uid': '00100-010-0002'}, {'pos_order_lines_ui_args': [(self.product1, 10)], 'payments':[(self.cash_split_pm1, 100)], 'customer': self.customer, 'is_invoiced': True, 'uid': '00100-010-0003'}, {'pos_order_lines_ui_args': [(self.product1, 10)], 'payments':[(self.bank_split_pm1, 100)], 'customer': self.customer, 'is_invoiced': True, 'uid': '00100-010-0004'}, {'pos_order_lines_ui_args': [(self.product1, 10)], 'payments':[(self.cash_pm1, 100)], 'customer': self.customer, 'is_invoiced': False, 'uid': '00100-010-0005'}, {'pos_order_lines_ui_args': [(self.product1, 10)], 'payments':[(self.bank_pm1, 100)], 'customer': self.customer, 'is_invoiced': False, 'uid': '00100-010-0006'}, {'pos_order_lines_ui_args': [(self.product99, 1)], 'payments':[(self.cash_split_pm1, 99)], 'customer': self.customer, 'is_invoiced': False, 'uid': '00100-010-0007'}, {'pos_order_lines_ui_args': [(self.product99, 1)], 'payments':[(self.bank_split_pm1, 99)], 'customer': self.customer, 'is_invoiced': False, 'uid': '00100-010-0008'}, {'pos_order_lines_ui_args': [(self.product1, 10)], 'payments':[(self.bank_pm1, 100)], 'customer': self.other_customer, 'is_invoiced': True, 'uid': '00100-010-0009'}, {'pos_order_lines_ui_args': [(self.product1, 10)], 'payments':[(self.bank_pm1, 100)], 'customer': self.other_customer, 'is_invoiced': True, 'uid': '00100-010-0010'}, {'pos_order_lines_ui_args': [(self.product1, 10)], 'payments':[(self.bank_pm1, 100)], 'customer': self.customer, 'is_invoiced': True, 'uid': '00100-010-0011'}, ], 'journal_entries_before_closing': { '00100-010-0001': { 'invoice': { 'line_ids': [ {'account_id': self.sales_account.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 100, 'reconciled': False}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 100, 'credit': 0, 'reconciled': True}, ] }, 'payments': [ ((self.cash_pm1, 100), { 'line_ids': [ {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 100, 'reconciled': True}, {'account_id': self.pos_receivable_account.id, 'partner_id': False, 'debit': 100, 'credit': 0, 'reconciled': False}, ] }), ], }, '00100-010-0002': { 'invoice': { 'line_ids': [ {'account_id': self.sales_account.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 100, 'reconciled': False}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 100, 'credit': 0, 'reconciled': True}, ] }, 'payments': [ ((self.bank_pm1, 100), { 'line_ids': [ {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 100, 'reconciled': True}, {'account_id': self.pos_receivable_account.id, 'partner_id': False, 'debit': 100, 'credit': 0, 'reconciled': False}, ] }), ], }, '00100-010-0003': { 'invoice': { 'line_ids': [ {'account_id': self.sales_account.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 100, 'reconciled': False}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 100, 'credit': 0, 'reconciled': True}, ] }, 'payments': [ ((self.cash_split_pm1, 100), { 'line_ids': [ {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 100, 'reconciled': True}, {'account_id': self.pos_receivable_account.id, 'partner_id': False, 'debit': 100, 'credit': 0, 'reconciled': False}, ] }), ], }, '00100-010-0004': { 'invoice': { 'line_ids': [ {'account_id': self.sales_account.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 100, 'reconciled': False}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 100, 'credit': 0, 'reconciled': True}, ] }, 'payments': [ ((self.bank_split_pm1, 100), { 'line_ids': [ {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 100, 'reconciled': True}, {'account_id': self.pos_receivable_account.id, 'partner_id': False, 'debit': 100, 'credit': 0, 'reconciled': False}, ] }), ], }, '00100-010-0009': { 'invoice': { 'line_ids': [ {'account_id': self.sales_account.id, 'partner_id': self.other_customer.id, 'debit': 0, 'credit': 100, 'reconciled': False}, {'account_id': self.other_receivable_account.id, 'partner_id': self.other_customer.id, 'debit': 100, 'credit': 0, 'reconciled': True}, ] }, 'payments': [ ((self.bank_pm1, 100), { 'line_ids': [ {'account_id': self.other_receivable_account.id, 'partner_id': self.other_customer.id, 'debit': 0, 'credit': 100, 'reconciled': True}, {'account_id': self.pos_receivable_account.id, 'partner_id': False, 'debit': 100, 'credit': 0, 'reconciled': False}, ] }), ], }, '00100-010-0010': { 'invoice': { 'line_ids': [ {'account_id': self.sales_account.id, 'partner_id': self.other_customer.id, 'debit': 0, 'credit': 100, 'reconciled': False}, {'account_id': self.other_receivable_account.id, 'partner_id': self.other_customer.id, 'debit': 100, 'credit': 0, 'reconciled': True}, ] }, 'payments': [ ((self.bank_pm1, 100), { 'line_ids': [ {'account_id': self.other_receivable_account.id, 'partner_id': self.other_customer.id, 'debit': 0, 'credit': 100, 'reconciled': True}, {'account_id': self.pos_receivable_account.id, 'partner_id': False, 'debit': 100, 'credit': 0, 'reconciled': False}, ] }), ], }, '00100-010-0011': { 'invoice': { 'line_ids': [ {'account_id': self.sales_account.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 100, 'reconciled': False}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 100, 'credit': 0, 'reconciled': True}, ] }, 'payments': [ ((self.bank_pm1, 100), { 'line_ids': [ {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 100, 'reconciled': True}, {'account_id': self.pos_receivable_account.id, 'partner_id': False, 'debit': 100, 'credit': 0, 'reconciled': False}, ] }), ], }, }, 'journal_entries_after_closing': { 'session_journal_entry': { 'line_ids': [ {'account_id': self.sales_account.id, 'partner_id': False, 'debit': 0, 'credit': 398, 'reconciled': False}, {'account_id': self.bank_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 500, 'credit': 0, 'reconciled': True}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 100, 'credit': 0, 'reconciled': True}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 99, 'credit': 0, 'reconciled': True}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 100, 'credit': 0, 'reconciled': True}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 99, 'credit': 0, 'reconciled': True}, {'account_id': self.cash_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 200, 'credit': 0, 'reconciled': True}, {'account_id': self.pos_receivable_account.id, 'partner_id': False, 'debit': 0, 'credit': 100, 'reconciled': True}, {'account_id': self.pos_receivable_account.id, 'partner_id': False, 'debit': 0, 'credit': 400, 'reconciled': True}, {'account_id': self.pos_receivable_account.id, 'partner_id': False, 'debit': 0, 'credit': 100, 'reconciled': True}, {'account_id': self.pos_receivable_account.id, 'partner_id': False, 'debit': 0, 'credit': 100, 'reconciled': True}, ], }, 'cash_statement': [ ((100, ), { 'line_ids': [ {'account_id': self.cash_split_pm1.journal_id.default_account_id.id, 'partner_id': self.customer.id, 'debit': 100, 'credit': 0, 'reconciled': False}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 100, 'reconciled': True}, ] }), ((99, ), { 'line_ids': [ {'account_id': self.cash_split_pm1.journal_id.default_account_id.id, 'partner_id': self.customer.id, 'debit': 99, 'credit': 0, 'reconciled': False}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 99, 'reconciled': True}, ] }), ((200, ), { 'line_ids': [ {'account_id': self.cash_pm1.journal_id.default_account_id.id, 'partner_id': False, 'debit': 200, 'credit': 0, 'reconciled': False}, {'account_id': self.cash_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 0, 'credit': 200, 'reconciled': True}, ] }), ], 'bank_payments': [ ((100, ), { 'line_ids': [ {'account_id': self.bank_split_pm1.outstanding_account_id.id, 'partner_id': self.customer.id, 'debit': 100, 'credit': 0, 'reconciled': False}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 100, 'reconciled': True}, ] }), ((99, ), { 'line_ids': [ {'account_id': self.bank_split_pm1.outstanding_account_id.id, 'partner_id': self.customer.id, 'debit': 99, 'credit': 0, 'reconciled': False}, {'account_id': self.c1_receivable.id, 'partner_id': self.customer.id, 'debit': 0, 'credit': 99, 'reconciled': True}, ] }), ((500, ), { 'line_ids': [ {'account_id': self.bank_pm1.outstanding_account_id.id, 'partner_id': False, 'debit': 500, 'credit': 0, 'reconciled': False}, {'account_id': self.bank_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 0, 'credit': 500, 'reconciled': True}, ] }), ], }, }) def test_cash_register_if_no_order(self): # Process one order with product3 self.open_new_session(0) session = self.pos_session order_data = self.create_ui_order_data([(self.product3, 1)]) amount_paid = order_data['data']['amount_paid'] self.env['pos.order'].create_from_ui([order_data]) session.post_closing_cash_details(amount_paid) session.close_session_from_ui() self.assertEqual(session.cash_register_balance_start, 0) self.assertEqual(session.cash_register_balance_end_real, amount_paid) # Open/Close session without any order in cash control self.open_new_session(amount_paid) session = self.pos_session session.post_closing_cash_details(amount_paid) session.close_session_from_ui() self.assertEqual(session.cash_register_balance_start, amount_paid) self.assertEqual(session.cash_register_balance_end_real, amount_paid) self.assertEqual(self.config.last_session_closing_cash, amount_paid) def test_start_balance_with_two_pos(self): """ When having several POS with cash control, this tests ensures that each POS has its correct opening amount """ def open_and_check(pos_data): self.config = pos_data['config'] self.open_new_session() session = self.pos_session session.set_cashbox_pos(pos_data['amount_paid'], False) self.assertEqual(session.cash_register_balance_start, pos_data['amount_paid']) pos01_config = self.config self.cash_journal = self.env['account.journal'].create( {'name': 'CASH journal', 'type': 'cash', 'code': 'CSH00'}) self.cash_payment_method = self.env['pos.payment.method'].create({ 'name': 'Cash Test', 'journal_id': self.cash_journal.id, 'receivable_account_id': pos01_config.payment_method_ids.filtered(lambda s: s.is_cash_count)[ 1].receivable_account_id.id }) pos02_config = pos01_config.copy({ 'payment_method_ids': self.cash_payment_method }) pos01_data = {'config': pos01_config, 'p_qty': 1, 'amount_paid': 0} pos02_data = {'config': pos02_config, 'p_qty': 3, 'amount_paid': 0} for pos_data in [pos01_data, pos02_data]: open_and_check(pos_data) session = self.pos_session order_data = self.create_ui_order_data([(self.product3, pos_data['p_qty'])]) pos_data['amount_paid'] += order_data['data']['amount_paid'] self.env['pos.order'].create_from_ui([order_data]) session.post_closing_cash_details(pos_data['amount_paid']) session.close_session_from_ui() open_and_check(pos01_data) open_and_check(pos02_data) def test_load_pos_data_should_not_fail(self): """load_pos_data shouldn't fail (Include test conditions here if possible) - When there are partners that belong to different company """ # create a partner that belongs to different company company2 = self.company_data_2['company'] self.env['res.partner'].create({ 'name': 'Test', 'company_id': company2.id, }) self.open_new_session() # calling load_pos_data should not raise an error self.pos_session.load_pos_data() def test_invoice_past_refund(self): """ Test invoicing a past refund Orders ====== +------------------+----------+-----------+----------+-----+-------+ | order | payments | invoiced? | product | qty | total | +------------------+----------+-----------+----------+-----+-------+ | order 1 | cash | no | product3 | 1 | 30 | +------------------+----------+-----------+----------+-----+-------+ | order 2 (return) | cash | no | product3 | -1 | -30 | +------------------+----------+-----------+----------+-----+-------+ Expected Result =============== +---------------------+---------+ | account | balance | +---------------------+---------+ | sale (sales) | -30 | | sale (refund) | 30 | +---------------------+---------+ | Total balance | 0.0 | +---------------------+---------+ """ def _before_closing_cb(): # Return the order order_to_return = self.pos_session.order_ids.filtered(lambda order: '12345-123-1234' in order.pos_reference) order_to_return.refund() refund_order = self.pos_session.order_ids.filtered(lambda order: order.state == 'draft') # Check if there's an amount to pay self.assertAlmostEqual(refund_order.amount_total - refund_order.amount_paid, -30) # Pay the refund context_make_payment = {"active_ids": [refund_order.id], "active_id": refund_order.id} make_payment = self.env['pos.make.payment'].with_context(context_make_payment).create({ 'payment_method_id': self.cash_pm1.id, 'amount': -30, }) make_payment.check() self._run_test({ 'payment_methods': self.cash_pm1 | self.bank_pm1, 'orders': [ {'pos_order_lines_ui_args': [(self.product3, 1)], 'payments': [(self.cash_pm1, 30)], 'uid': '12345-123-1234'}, ], 'before_closing_cb': _before_closing_cb, 'journal_entries_before_closing': {}, 'journal_entries_after_closing': { 'session_journal_entry': { 'line_ids': [ {'account_id': self.sales_account.id, 'partner_id': False, 'debit': 0, 'credit': 30, 'reconciled': False}, {'account_id': self.sales_account.id, 'partner_id': False, 'debit': 30, 'credit': 0, 'reconciled': False}, ], }, 'cash_statement': [], 'bank_payments': [], }, }) closed_session = self.pos_session self.assertTrue(closed_session.state == 'closed', 'Session should be closed.') return_to_invoice = closed_session.order_ids[1] test_customer = self.env['res.partner'].create({'name': 'Test Customer'}) new_session_date = return_to_invoice.date_order + relativedelta(days=2) with freeze_time(new_session_date): # Create a new session after 2 days self.open_new_session(0) # Invoice the uninvoiced refund return_to_invoice.write({'partner_id': test_customer.id}) return_to_invoice.action_pos_order_invoice() # Check the credit note self.assertTrue(return_to_invoice.account_move, 'Invoice should be created.') self.assertEqual(return_to_invoice.account_move.move_type, 'out_refund', 'Invoice should be a credit note.') self.assertEqual(return_to_invoice.account_move.invoice_date, new_session_date, 'Invoice date should be the same as the session it is created in.') self.assertRecordValues(return_to_invoice.account_move, [{ 'amount_untaxed': 30, 'amount_tax': 0, 'amount_total': 30, }]) self.assertRecordValues(return_to_invoice.account_move.line_ids, [ {'account_id': self.sales_account.id, 'balance': 30}, {'account_id': self.receivable_account.id, 'balance': -30}, ]) def test_invoice_past_order(self): # create 1 uninvoiced order then close the session self._run_test({ 'payment_methods': self.cash_pm1 | self.bank_pm1, 'orders': [ {'pos_order_lines_ui_args': [(self.product99, 1)], 'payments': [(self.bank_pm1, 99)], 'customer': False, 'is_invoiced': False, 'uid': '00100-010-0001'}, ], 'journal_entries_before_closing': {}, 'journal_entries_after_closing': { 'session_journal_entry': { 'line_ids': [ {'account_id': self.sales_account.id, 'partner_id': False, 'debit': 0, 'credit': 99, 'reconciled': False}, {'account_id': self.bank_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 99, 'credit': 0, 'reconciled': True}, ], }, 'cash_statement': [], 'bank_payments': [ ((99, ), { 'line_ids': [ {'account_id': self.bank_pm1.outstanding_account_id.id, 'partner_id': False, 'debit': 99, 'credit': 0, 'reconciled': False}, {'account_id': self.bank_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 0, 'credit': 99, 'reconciled': True}, ] }) ], }, }) # keep reference of the closed session closed_session = self.pos_session self.assertTrue(closed_session.state == 'closed', 'Session should be closed.') order_to_invoice = closed_session.order_ids[0] test_customer = self.env['res.partner'].create({'name': 'Test Customer'}) with freeze_time(fields.Datetime.now() + relativedelta(days=2)): # create new session after 2 days self.open_new_session(0) # invoice the uninvoiced order order_to_invoice.write({'partner_id': test_customer.id}) order_to_invoice.action_pos_order_invoice() # check invoice invoice = order_to_invoice.account_move self.assertTrue(invoice, 'Invoice should be created.') self.assertNotEqual(invoice.invoice_date, order_to_invoice.date_order.date(), 'Invoice date should not be the same as order date since the session was closed.') # check that the payment date is set to the order date which # is the real payment date and not to the invoice_date payment = invoice.line_ids.full_reconcile_id.reconciled_line_ids.move_id - invoice self.assertEqual(payment.date, order_to_invoice.date_order.date()) def test_invoice_past_order_affecting_taxes(self): """ Test whether two taxes affecting each other don't trigger a recomputation on invoice generation """ # Create 1 uninvoiced order then close the session self._run_test({ 'payment_methods': self.cash_pm1 | self.bank_pm1, 'orders': [ {'pos_order_lines_ui_args': [(self.product_multi_tax, 1)], 'payments': [(self.bank_pm1, 117.72)], 'customer': False, 'is_invoiced': False, 'uid': '00100-010-0001'}, ], 'journal_entries_before_closing': {}, 'journal_entries_after_closing': { 'session_journal_entry': { 'line_ids': [ {'account_id': self.tax_received_account.id, 'partner_id': False, 'debit': 0, 'credit': 8, 'reconciled': False}, {'account_id': self.tax_received_account.id, 'partner_id': False, 'debit': 0, 'credit': 9.72, 'reconciled': False}, {'account_id': self.sales_account.id, 'partner_id': False, 'debit': 0, 'credit': 100, 'reconciled': False}, {'account_id': self.bank_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 117.72, 'credit': 0, 'reconciled': True}, ], }, 'cash_statement': [], 'bank_payments': [ ((117.72, ), { 'line_ids': [ {'account_id': self.bank_pm1.outstanding_account_id.id, 'partner_id': False, 'debit': 117.72, 'credit': 0, 'reconciled': False}, {'account_id': self.bank_pm1.receivable_account_id.id, 'partner_id': False, 'debit': 0, 'credit': 117.72, 'reconciled': True}, ] }) ], }, }) closed_session = self.pos_session self.assertTrue(closed_session.state == 'closed', 'Session should be closed.') order_to_invoice = closed_session.order_ids[0] test_customer = self.env['res.partner'].create({'name': 'Test Customer'}) # Create a new session self.open_new_session(0) # Invoice the uninvoiced order order_to_invoice.write({'partner_id': test_customer.id}) order_to_invoice.action_pos_order_invoice() # Check the invoice for the lines self.assertTrue(order_to_invoice.account_move, 'Invoice should be created.') self.assertRecordValues(order_to_invoice.account_move.line_ids, [ {'account_id': self.sales_account.id, 'balance': -100, 'reconciled': False}, {'account_id': self.tax_received_account.id, 'balance': -8, 'reconciled': False}, {'account_id': self.tax_received_account.id, 'balance': -9.72, 'reconciled': False}, {'account_id': self.receivable_account.id, 'balance': 117.72, 'reconciled': True}, ])