[FIX] point_of_sale: refund payment should be the same as orinal order

Current behavior:
When you refund an order that was paid with bank and so not rounded,
the refund is rounded wich result in a difference between the original
order and the refund.
This also happens when the original order was paid with multiple
payments and one of them was not rounded and the other was. The refund
will be rounded as one single payment. This also results in a difference
between the original order and the refund.

Steps to reproduce:
- Setup a rounding method with a precision of 5.0
- Create a product with a price of 138.0
- Open the POS and add the product to the order
- Pay the order with 2 payments, one bank of 55 and one cash that will
  be rounded to 80.
- Validate the order
- Go in the backend and refund the order
- The refund will be rounded to 135.0

opw-3701574

closes odoo/odoo#159395

X-original-commit: cdae1e34eddccd5ce098c79d845a8fc2658c0175
Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
Signed-off-by: Robin Engels (roen) <roen@odoo.com>
This commit is contained in:
roen-odoo
2024-04-02 10:22:58 +00:00
parent 6e84d601c0
commit ae7d18dd5f
3 changed files with 58 additions and 1 deletions
+2 -1
View File
@@ -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
@@ -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(),
});
@@ -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'