diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index 391ae4cebde..a29f7ba40cc 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -538,7 +538,8 @@ class PosOrder(models.Model): } def _is_pos_order_paid(self): - return float_is_zero(self._get_rounded_amount(self.amount_total) - self.amount_paid, precision_rounding=self.currency_id.rounding) + amount_total = -self.refunded_order_ids.amount_paid or self.amount_total + return float_is_zero(self._get_rounded_amount(amount_total) - self.amount_paid, precision_rounding=self.currency_id.rounding) def _get_rounded_amount(self, amount, force_round=False): # TODO: add support for mix of cash and non-cash payments when both cash_rounding and only_round_cash_method are True 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 ac7052a67d6..f1271262cf4 100644 --- a/addons/point_of_sale/static/tests/tours/PaymentScreen.tour.js +++ b/addons/point_of_sale/static/tests/tours/PaymentScreen.tour.js @@ -275,3 +275,24 @@ registry.category("web_tour.tours").add("PaymentScreenTotalDueWithOverPayment", PaymentScreen.totalDueIs("1.95"), ].flat(), }); + +registry.category("web_tour.tours").add("PaymentScreenRoundingDownMultiplePayment", { + test: true, + steps: () => + [ + ProductScreen.clickHomeCategory(), + ProductScreen.addOrderline("Product Test", "1"), + ProductScreen.clickPayButton(), + + PaymentScreen.clickPaymentMethod("Bank"), + PaymentScreen.pressNumpad("5 5"), + + PaymentScreen.clickPaymentMethod("Cash"), + + PaymentScreen.remainingIs("0.0"), + PaymentScreen.changeIs("0.0"), + + PaymentScreen.clickValidate(), + ReceiptScreen.receiptIsThere(), + ].flat(), +}); diff --git a/addons/point_of_sale/tests/test_frontend.py b/addons/point_of_sale/tests/test_frontend.py index 260c616626f..50f53fb8bba 100644 --- a/addons/point_of_sale/tests/test_frontend.py +++ b/addons/point_of_sale/tests/test_frontend.py @@ -1138,6 +1138,41 @@ 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, 'MultiProductOptionsTour', login="pos_user") + def test_rounding_down_refund_multiple_payment(self): + """This test makes sure that the refund amount always correspond to what has been paid in the original order. + In this example we have a rounding, so we pay 55 in bank that is not rounded, then we pay the rest in cash + that is rounded. This sum up to 130 paid, so the refund should be 130.""" + rouding_method = self.env['account.cash.rounding'].create({ + 'name': 'Rounding down', + 'rounding': 5.0, + 'rounding_method': 'DOWN', + }) + + self.env['product.product'].create({ + 'name': 'Product Test', + 'available_in_pos': True, + 'list_price': 134.38, + 'taxes_id': False, + }) + + self.main_pos_config.write({ + 'rounding_method': rouding_method.id, + 'cash_rounding': True, + }) + + self.main_pos_config.open_ui() + self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'PaymentScreenRoundingDownMultiplePayment', login="accountman") + + last_order = self.env['pos.order'].search([], order='id desc', limit=1) + refund_order = self.env['pos.order'].browse(last_order.refund()['res_id']) + payment = self.env['pos.make.payment'].with_context(active_id=refund_order.id).create({ + 'payment_method_id': self.main_pos_config.payment_method_ids[0].id, + }) + self.assertEqual(payment.amount, -130.0, "The amount should be 130.0") + payment.check() + self.assertEqual(refund_order.amount_paid, -130.0, "The amount should be 130.0") + self.assertEqual(refund_order.state, 'paid', "The order should be paid") + # This class just runs the same tests as above but with mobile emulation class MobileTestUi(TestUi): browser_size = '375x667'