Skip to content

Commit 4b082f6

Browse files
committed
[IMP] sale_order_line_cancel: support kits
1 parent 483041b commit 4b082f6

File tree

5 files changed

+64
-23
lines changed

5 files changed

+64
-23
lines changed

sale_order_line_cancel/__manifest__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
# © 2016 Sylvain Van Hoof
1+
# Copyright 2018 Sylvain Van Hoof (Okia SPRL)
2+
# Copyright 2018 Jacques-Etienne Baudoux (BCIM) <je@bcim.be>
23
# Copyright 2023 ACSONE SA/NV
34
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
45

sale_order_line_cancel/models/sale_order_line.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Copyright 2018 Okia SPRL
2+
# Copyright 2018 Jacques-Etienne Baudoux (BCIM) <je@bcim.be>
23
# Copyright 2020 ACSONE SA/NV
34
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
45

5-
from odoo import api, fields, models
6+
from odoo import _, api, fields, models
67
from odoo.tools import float_compare
78

89

@@ -34,7 +35,7 @@ def _compute_can_cancel_remaining_qty(self):
3435
)
3536
== 1
3637
and rec.state in ("sale", "done")
37-
and rec.move_ids
38+
and rec.qty_delivered_method == "stock_move"
3839
)
3940

4041
@api.depends(
@@ -44,10 +45,33 @@ def _compute_can_cancel_remaining_qty(self):
4445
)
4546
def _compute_product_qty_remains_to_deliver(self):
4647
for line in self:
47-
remaining_to_deliver = (
48-
line.product_uom_qty - line.qty_delivered - line.product_qty_canceled
49-
)
50-
line.product_qty_remains_to_deliver = remaining_to_deliver
48+
qty_remaining = line.qty_to_deliver - line.product_qty_canceled
49+
line.product_qty_remains_to_deliver = qty_remaining
50+
51+
def _get_moves_to_cancel(self):
52+
return self.move_ids.filtered(lambda m: m.state not in ("done", "cancel"))
5153

5254
def _check_moves_to_cancel(self, moves):
53-
"""override this method to add checks before cancel"""
55+
"""Override this method to add checks before cancel"""
56+
self.ensure_one()
57+
58+
def _update_qty_canceled(self):
59+
"""Update SO line qty canceled only when all remaining moves are canceled"""
60+
for line in self:
61+
if line._get_moves_to_cancel():
62+
continue
63+
line.product_qty_canceled = line.qty_to_deliver
64+
65+
def cancel_remaining_qty(self):
66+
lines = self.filtered(lambda l: l.can_cancel_remaining_qty)
67+
for line in lines:
68+
moves_to_cancel = line._get_moves_to_cancel()
69+
line._check_moves_to_cancel(moves_to_cancel)
70+
moves_to_cancel._action_cancel()
71+
line.order_id.message_post(
72+
body=_(
73+
"<b>%(product)s</b>: The order line has been canceled",
74+
product=line.product_id.display_name,
75+
)
76+
)
77+
return True

sale_order_line_cancel/models/stock_move.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Copyright 2023 ACSONE SA/NV
2+
# Copyright 2024 Jacques-Etienne Baudoux (BCIM) <je@bcim.be>
23
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
34

45
from odoo import models
@@ -13,8 +14,13 @@ def _action_cancel(self):
1314
lambda m: m.sale_line_id and m.state not in ("done", "cancel")
1415
)
1516
res = super()._action_cancel()
16-
for rec in sale_moves:
17-
if rec.state != "cancel":
18-
continue
19-
rec.sale_line_id.product_qty_canceled = rec.product_uom_qty
17+
sale_lines = sale_moves.filtered(lambda m: m.state == "cancel").sale_line_id
18+
sale_lines._update_qty_canceled()
2019
return res
20+
21+
def _action_done(self, cancel_backorder=False):
22+
moves_todo = super()._action_done(cancel_backorder=cancel_backorder)
23+
if cancel_backorder and moves_todo:
24+
# _action_cancel is called before marking as done, so the hook on
25+
# _action_cancel will not be triggered
26+
self.sale_line_id._update_qty_canceled()

sale_order_line_cancel/tests/test_sale_order_line_cancel.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,26 @@ def test_cancel_pickings(self):
6060
active_id=self.sale.order_line.id, active_model="sale.order.line"
6161
).cancel_remaining_qty()
6262

63+
def test_cancel_move_kit(self):
64+
"""when all remaining moves are canceled product_qty_canceled increased"""
65+
self.assertTrue(self.sale.order_line.can_cancel_remaining_qty)
66+
move = self.sale.picking_ids.move_ids
67+
self.assertEqual(move.sale_line_id, self.sale.order_line)
68+
# simulate a kit with a second move linked to the sale SO line
69+
move2 = move.copy()
70+
move2._action_confirm()
71+
self.assertEqual(move2.sale_line_id, self.sale.order_line)
72+
move._action_cancel()
73+
self.assertEqual(self.sale.order_line.product_qty_canceled, 0)
74+
move2._action_cancel()
75+
self.assertEqual(self.sale.order_line.product_qty_canceled, 10)
76+
self.assertEqual(self.sale.order_line.product_qty_remains_to_deliver, 0)
77+
self.assertFalse(self.sale.order_line.can_cancel_remaining_qty)
78+
self.wiz.with_context(
79+
active_id=self.sale.order_line.id, active_model="sale.order.line"
80+
).cancel_remaining_qty()
81+
82+
6383
def test_reset_to_draft(self):
6484
ship = self.sale.picking_ids
6585
ship.action_assign()

sale_order_line_cancel/wizards/sale_order_line_cancel.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,5 @@ def _get_moves_to_cancel(self, line):
2323

2424
def cancel_remaining_qty(self):
2525
line = self._get_sale_order_line()
26-
if not line.can_cancel_remaining_qty:
27-
return False
28-
cancel_moves = self._get_moves_to_cancel(line)
29-
line._check_moves_to_cancel(cancel_moves)
30-
cancel_moves._action_cancel()
31-
line.order_id.message_post(
32-
body=_(
33-
"<b>%(product)s</b>: The order line has been canceled",
34-
product=line.product_id.display_name,
35-
)
36-
)
26+
line.cancel_remaining_qty()
3727
return True

0 commit comments

Comments
 (0)