diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index c623ec827ce..ca67cbb0e99 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -531,12 +531,28 @@ class PosOrder(models.Model): account_id = new_move.invoice_cash_rounding_id.loss_account_id.id else: account_id = new_move.invoice_cash_rounding_id.profit_account_id.id - if rounding_line_difference: - rounding_line.with_context(check_move_validity=False).write({ - 'debit': rounding_applied < 0.0 and -rounding_applied or 0.0, - 'credit': rounding_applied > 0.0 and rounding_applied or 0.0, + if rounding_line: + if rounding_line_difference: + rounding_line.with_context(skip_invoice_sync=True, check_move_validity=False).write({ + 'debit': rounding_applied < 0.0 and -rounding_applied or 0.0, + 'credit': rounding_applied > 0.0 and rounding_applied or 0.0, + 'account_id': account_id, + 'price_unit': rounding_applied, + }) + + else: + self.env['account.move.line'].with_context(skip_invoice_sync=True, check_move_validity=False).create({ + 'balance': -rounding_applied, + 'quantity': 1.0, + 'partner_id': new_move.partner_id.id, + 'move_id': new_move.id, + 'currency_id': new_move.currency_id.id, + 'company_id': new_move.company_id.id, + 'company_currency_id': new_move.company_id.currency_id.id, + 'display_type': 'rounding', + 'sequence': 9999, + 'name': self.config_id.rounding_method.name, 'account_id': account_id, - 'price_unit': rounding_applied, }) else: if rounding_line: @@ -818,7 +834,7 @@ class PosOrder(models.Model): new_move = order._create_invoice(move_vals) order.write({'account_move': new_move.id, 'state': 'invoiced'}) - new_move.sudo().with_company(order.company_id)._post() + new_move.sudo().with_company(order.company_id).with_context(skip_invoice_sync=True)._post() moves += new_move payment_moves = order._apply_invoice_payments() diff --git a/addons/point_of_sale/static/tests/tours/PaymentScreen.tour.js b/addons/point_of_sale/static/tests/tours/PaymentScreen.tour.js index 9a18c7f66cb..e4c2ad5e10b 100644 --- a/addons/point_of_sale/static/tests/tours/PaymentScreen.tour.js +++ b/addons/point_of_sale/static/tests/tours/PaymentScreen.tour.js @@ -4,6 +4,7 @@ import { Chrome } from "@point_of_sale/../tests/tours/helpers/ChromeTourMethods" import { ProductScreen } from "@point_of_sale/../tests/tours/helpers/ProductScreenTourMethods"; import { PaymentScreen } from "@point_of_sale/../tests/tours/helpers/PaymentScreenTourMethods"; import { TicketScreen } from "@point_of_sale/../tests/tours/helpers/TicketScreenTourMethods"; +import { ReceiptScreen } from "@point_of_sale/../tests/tours/helpers/ReceiptScreenTourMethods"; import { getSteps, startSteps } from "@point_of_sale/../tests/tours/helpers/utils"; import { registry } from "@web/core/registry"; @@ -206,6 +207,55 @@ registry startSteps(); +ProductScreen.do.confirmOpeningPopup(); +ProductScreen.do.clickHomeCategory(); +ProductScreen.exec.addOrderline('Product Test 40', '1'); +ProductScreen.do.clickPartnerButton(); +ProductScreen.do.clickCustomer('Nicole Ford'); +ProductScreen.do.clickPayButton(); + +PaymentScreen.check.totalIs('40.00'); +PaymentScreen.do.clickPaymentMethod('Bank'); +PaymentScreen.do.pressNumpad('3 8'); +PaymentScreen.do.fillPaymentLineAmountMobile('Bank', '38'); +PaymentScreen.check.remainingIs('2.0'); +PaymentScreen.do.clickPaymentMethod('Cash'); + +PaymentScreen.check.remainingIs('0.0'); +PaymentScreen.check.changeIs('0.0'); + +PaymentScreen.do.clickInvoiceButton(); +PaymentScreen.do.clickValidate(); +ReceiptScreen.check.receiptIsThere(); +ReceiptScreen.do.clickNextOrder(); + +ProductScreen.do.clickHomeCategory(); +ProductScreen.exec.addOrderline('Product Test 41', '1'); +ProductScreen.do.clickPartnerButton(); +ProductScreen.do.clickCustomer('Nicole Ford'); +ProductScreen.do.clickPayButton(); + +PaymentScreen.check.totalIs('41.00'); +PaymentScreen.do.clickPaymentMethod('Bank'); +PaymentScreen.do.pressNumpad('3 8'); +PaymentScreen.do.fillPaymentLineAmountMobile('Bank', '38'); +PaymentScreen.check.remainingIs('3.0'); +PaymentScreen.do.clickPaymentMethod('Cash'); + +PaymentScreen.check.remainingIs('0.0'); +PaymentScreen.check.changeIs('0.0'); + +PaymentScreen.do.clickInvoiceButton(); +PaymentScreen.do.clickValidate(); +ReceiptScreen.check.receiptIsThere(); + + +registry + .category("web_tour.tours") + .add("PaymentScreenRoundingHalfUpCashAndBank", { test: true, url: "/pos/ui", steps: getSteps() }); + +startSteps(); + ProductScreen.do.confirmOpeningPopup(); ProductScreen.do.clickHomeCategory(); ProductScreen.exec.addOrderline("Product Test", "1"); diff --git a/addons/point_of_sale/tests/test_frontend.py b/addons/point_of_sale/tests/test_frontend.py index db73f69061c..1971cf189ae 100644 --- a/addons/point_of_sale/tests/test_frontend.py +++ b/addons/point_of_sale/tests/test_frontend.py @@ -706,6 +706,49 @@ class TestUi(TestPointOfSaleHttpCommon): self.main_pos_config.with_user(self.pos_user).open_ui() self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'PaymentScreenRoundingHalfUp', login="pos_user") + def test_rounding_half_up_cash_and_bank(self): + company = self.main_pos_config.company_id + rouding_method = self.env['account.cash.rounding'].create({ + 'name': 'Rounding HALF-UP', + 'rounding': 5, + 'rounding_method': 'HALF-UP', + 'strategy': 'add_invoice_line', + 'profit_account_id': company['default_cash_difference_income_account_id'].id, + 'loss_account_id': company['default_cash_difference_expense_account_id'].id, + }) + + self.env['product.product'].create({ + 'name': 'Product Test 40', + 'available_in_pos': True, + 'list_price': 40, + 'taxes_id': False, + }) + + self.env['product.product'].create({ + 'name': 'Product Test 41', + 'available_in_pos': True, + 'list_price': 41, + 'taxes_id': False, + }) + + self.main_pos_config.write({ + 'rounding_method': rouding_method.id, + 'cash_rounding': True, + 'only_round_cash_method': True + }) + + self.main_pos_config.open_ui() + self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'PaymentScreenRoundingHalfUpCashAndBank', login="accountman") + + invoiced_orders = self.env['pos.order'].search([('state', '=', 'invoiced')]) + self.assertEqual(len(invoiced_orders), 2, 'There should be 2 invoiced orders.') + + for order in invoiced_orders: + rounding_line = order.account_move.line_ids.filtered(lambda line: line.display_type == 'rounding') + self.assertEqual(len(rounding_line), 1, 'There should be 1 rounding line.') + rounding_applied = order.amount_total - order.amount_paid + self.assertEqual(rounding_line.balance, rounding_applied, 'Rounding amount is incorrect!') + def test_pos_closing_cash_details(self): """Test if the cash closing details correctly show the cash difference if there is a difference at the opening of the PoS session. This also test if the accounting