Skip to content

Commit 1af89cb

Browse files
[IMP] purchase_manual_delivery: rely on qty_received instead of 'done' stock moves
Rename existing_qty to qty_in_receipt. It now reflects the quantity for which there are pending stock moves and no longer includes the quantities of 'done' stock moves. Co-authored-by: Cas Vissers <[email protected]>
1 parent 8d5eb60 commit 1af89cb

9 files changed

+104
-58
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from odoo.tools.sql import column_exists
2+
3+
4+
def migrate(cr, version):
5+
"""Initialize qty_in_receipt as existing_qty - qty_received"""
6+
if not column_exists(cr, "purchase_order_line", "qty_in_receipt") and column_exists(
7+
cr, "purchase_order_line", "existing_qty"
8+
):
9+
cr.execute(
10+
"""
11+
alter table purchase_order_line
12+
add column qty_in_receipt numeric;
13+
update purchase_order_line
14+
set qty_in_receipt = coalesce(existing_qty, 0) - coalesce(qty_received, 0);
15+
"""
16+
)

purchase_manual_delivery/models/product_product.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,25 @@ def _get_quantity_in_progress(self, location_ids=False, warehouse_ids=False):
3232
"product_uom",
3333
"orderpoint_id",
3434
],
35-
["product_qty:sum", "existing_qty:sum"],
35+
["product_qty:sum", "qty_in_receipt:sum", "qty_received:sum"],
3636
)
37-
for product, order, uom, orderpoint, product_qty, existing_qty in groups:
37+
for (
38+
product,
39+
order,
40+
uom,
41+
orderpoint,
42+
product_qty,
43+
qty_in_receipt,
44+
qty_received,
45+
) in groups:
3846
if orderpoint:
3947
location = orderpoint.location_id
4048
else:
4149
location = order.picking_type_id.default_location_dest_id
4250
product_qty = uom._compute_quantity(
4351
product_qty, product.uom_id, round=False
4452
)
45-
remaining_qty = product_qty - existing_qty
53+
remaining_qty = product_qty - (qty_in_receipt + qty_received)
4654
qty_by_product_location[(product.id, location.id)] += remaining_qty
4755
qty_by_product_wh[(product.id, location.warehouse_id.id)] += remaining_qty
4856
return qty_by_product_location, qty_by_product_wh

purchase_manual_delivery/models/purchase_order.py

+10-9
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,14 @@ def _create_picking(self):
5252
class PurchaseOrderLine(models.Model):
5353
_inherit = "purchase.order.line"
5454

55-
existing_qty = fields.Float(
56-
compute="_compute_existing_qty",
55+
qty_in_receipt = fields.Float(
56+
compute="_compute_qty_in_receipt",
5757
store=True,
58-
string="Existing Quantity",
5958
digits="Product Unit of Measure",
60-
help="Quantity already planned or shipped (stock movements " "already created)",
59+
help="Quantity for which there are pending stock moves",
6160
)
6261
pending_to_receive = fields.Boolean(
63-
compute="_compute_existing_qty",
62+
compute="_compute_qty_in_receipt",
6463
store=True,
6564
string="Pending Qty to Receive",
6665
help="There is pending quantity to receive not yet planned",
@@ -72,15 +71,17 @@ class PurchaseOrderLine(models.Model):
7271
"move_ids.location_id",
7372
"move_ids.location_dest_id",
7473
"product_uom_qty",
74+
"qty_received",
75+
"state",
7576
)
76-
def _compute_existing_qty(self):
77+
def _compute_qty_in_receipt(self):
7778
for line in self:
7879
precision_digits = self.env["decimal.precision"].precision_get(
7980
"Product Unit of Measure"
8081
)
8182
total = 0.0
8283
for move in line.move_ids:
83-
if move.state not in ["cancel"]:
84+
if move.state not in ["cancel", "done"]:
8485
if (
8586
move.location_id
8687
== self.order_id.picking_type_id.default_location_dest_id
@@ -106,11 +107,11 @@ def _compute_existing_qty(self):
106107
total += move.product_uom._compute_quantity(
107108
move.quantity, line.product_uom
108109
)
109-
line.existing_qty = total
110+
line.qty_in_receipt = total
110111
if (
111112
float_compare(
112113
line.product_qty,
113-
line.existing_qty,
114+
line.qty_in_receipt + line.qty_received,
114115
precision_digits=precision_digits,
115116
)
116117
== 1

purchase_manual_delivery/report/stock_forecasted.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ def _get_report_header(self, product_template_ids, product_ids, wh_location_ids)
1818
if warehouse_id:
1919
domain += [("order_id.picking_type_id.warehouse_id", "=", warehouse_id)]
2020
po_lines = self.env["purchase.order.line"].search(domain)
21-
in_sum = sum(po_lines.mapped(lambda po: po.product_qty - po.existing_qty))
21+
in_sum = sum(
22+
po_lines.mapped(
23+
lambda pol: pol.product_qty - (pol.qty_in_receipt + pol.qty_received)
24+
)
25+
)
2226
res["no_delivery_purchase_qty"] = in_sum
2327
res["no_delivery_purchase_orders"] = (
2428
po_lines.mapped("order_id").sorted("name").read(fields=["id", "name"])

purchase_manual_delivery/tests/test_purchase_manual_delivery.py

+32-21
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ def test_01_purchase_order_manual_delivery(self):
9090
self.po1.button_confirm_manual()
9191
self.assertTrue(self.po1_line1.pending_to_receive)
9292
self.assertTrue(self.po1_line2.pending_to_receive)
93-
self.assertEqual(self.po1_line1.existing_qty, 0)
94-
self.assertEqual(self.po1_line2.existing_qty, 0)
93+
self.assertEqual(self.po1_line1.qty_in_receipt, 0)
94+
self.assertEqual(self.po1_line2.qty_in_receipt, 0)
9595
self.assertFalse(
9696
self.po1.picking_ids,
9797
"Purchase Manual Delivery: no picking should had been created",
@@ -172,8 +172,8 @@ def test_01_purchase_order_manual_delivery(self):
172172
)
173173
self.assertFalse(self.po1_line1.pending_to_receive)
174174
self.assertFalse(self.po1_line2.pending_to_receive)
175-
self.assertEqual(self.po1_line1.existing_qty, self.po1_line1.product_qty)
176-
self.assertEqual(self.po1_line2.existing_qty, self.po1_line2.product_qty)
175+
self.assertEqual(self.po1_line1.qty_in_receipt, self.po1_line1.product_qty)
176+
self.assertEqual(self.po1_line2.qty_in_receipt, self.po1_line2.product_qty)
177177
self.assertFalse(self.po1.pending_to_receive)
178178

179179
# Process the picking
@@ -209,11 +209,11 @@ def test_01_purchase_order_manual_delivery(self):
209209
)
210210
return_wiz.create_returns()
211211

212-
# The refund line is open to receive the returned item
212+
# The refund line is open to re-receive the returned item
213213
self.assertTrue(self.po1_line1.pending_to_receive)
214-
self.assertEqual(self.po1_line1.existing_qty, self.po1_line1.product_qty - 2)
214+
self.assertEqual(self.po1_line1.qty_in_receipt, -2)
215215
# But the non-refund line is not
216-
self.assertEqual(self.po1_line2.existing_qty, self.po1_line2.product_qty)
216+
self.assertFalse(self.po1_line2.qty_in_receipt)
217217
self.assertFalse(self.po1_line2.pending_to_receive)
218218

219219
self.assertTrue(self.po1.pending_to_receive)
@@ -231,10 +231,10 @@ def test_02_purchase_order_line_manual_delivery(self):
231231
self.assertTrue(self.po1_line2.pending_to_receive)
232232
self.assertTrue(self.po2_line1.pending_to_receive)
233233
self.assertTrue(self.po2_line2.pending_to_receive)
234-
self.assertEqual(self.po1_line1.existing_qty, 0)
235-
self.assertEqual(self.po1_line2.existing_qty, 0)
236-
self.assertEqual(self.po2_line1.existing_qty, 0)
237-
self.assertEqual(self.po2_line2.existing_qty, 0)
234+
self.assertEqual(self.po1_line1.qty_in_receipt, 0)
235+
self.assertEqual(self.po1_line2.qty_in_receipt, 0)
236+
self.assertEqual(self.po2_line1.qty_in_receipt, 0)
237+
self.assertEqual(self.po2_line2.qty_in_receipt, 0)
238238
with self.assertRaises(UserError):
239239
# create a manual delivery for two lines different PO
240240
self.env["create.stock.picking.wizard"].with_context(
@@ -266,10 +266,10 @@ def test_02_purchase_order_line_manual_delivery(self):
266266
self.assertTrue(self.po1_line2.pending_to_receive)
267267
self.assertFalse(self.po2_line1.pending_to_receive)
268268
self.assertFalse(self.po2_line2.pending_to_receive)
269-
self.assertEqual(self.po1_line1.existing_qty, 0)
270-
self.assertEqual(self.po1_line2.existing_qty, 0)
271-
self.assertEqual(self.po2_line1.existing_qty, self.po2_line1.product_qty)
272-
self.assertEqual(self.po2_line2.existing_qty, self.po2_line2.product_qty)
269+
self.assertEqual(self.po1_line1.qty_in_receipt, 0)
270+
self.assertEqual(self.po1_line2.qty_in_receipt, 0)
271+
self.assertEqual(self.po2_line1.qty_in_receipt, self.po2_line1.product_qty)
272+
self.assertEqual(self.po2_line2.qty_in_receipt, self.po2_line2.product_qty)
273273

274274
def test_03_purchase_order_line_location(self):
275275
"""
@@ -282,8 +282,8 @@ def test_03_purchase_order_line_location(self):
282282
self.po1.button_confirm_manual()
283283
self.assertTrue(self.po1_line1.pending_to_receive)
284284
self.assertTrue(self.po1_line2.pending_to_receive)
285-
self.assertEqual(self.po1_line1.existing_qty, 0)
286-
self.assertEqual(self.po1_line2.existing_qty, 0)
285+
self.assertEqual(self.po1_line1.qty_in_receipt, 0)
286+
self.assertEqual(self.po1_line2.qty_in_receipt, 0)
287287
# create a manual delivery for one line (product1)
288288
wizard = (
289289
self.env["create.stock.picking.wizard"]
@@ -307,8 +307,8 @@ def test_03_purchase_order_line_location(self):
307307
self.assertEqual(picking_id.location_dest_id, self.shelf2)
308308
self.assertFalse(self.po1_line1.pending_to_receive)
309309
self.assertTrue(self.po1_line2.pending_to_receive)
310-
self.assertEqual(self.po1_line1.existing_qty, self.po1_line1.product_qty)
311-
self.assertEqual(self.po1_line2.existing_qty, 0)
310+
self.assertEqual(self.po1_line1.qty_in_receipt, self.po1_line1.product_qty)
311+
self.assertEqual(self.po1_line2.qty_in_receipt, 0)
312312

313313
def test_04_pending_to_receive(self):
314314
"""
@@ -409,11 +409,22 @@ def test_05_purchase_order_in_progress(self):
409409
wizard.line_ids[0].qty = 2
410410
wizard.create_stock_picking()
411411
po_in_progress.picking_ids[0].button_validate()
412+
413+
self.assertEqual(po_in_progress.order_line.qty_received, 2)
414+
self.assertEqual(po_in_progress.order_line.qty_in_receipt, 0)
415+
self.assertTrue(po_in_progress.order_line.pending_to_receive)
416+
412417
qty, _ = product_in_progress._get_quantity_in_progress(
413418
location_ids=location.ids
414419
)
415420
self.assertEqual(qty.get((product_in_progress.id, location.id)), 3)
416421

422+
wizard.line_ids[0].qty = 3
423+
wizard.create_stock_picking()
424+
self.assertEqual(po_in_progress.order_line.qty_received, 2)
425+
self.assertEqual(po_in_progress.order_line.qty_in_receipt, 3)
426+
self.assertFalse(po_in_progress.order_line.pending_to_receive)
427+
417428
def test_06_purchase_order_manual_delivery_double_validation(self):
418429
"""
419430
Confirm Purchase Order 1, check no incoming shipments have been
@@ -460,7 +471,7 @@ def test_06_purchase_order_manual_delivery_double_validation(self):
460471
# confirm RFQ
461472
self.po.button_confirm_manual()
462473
self.assertTrue(self.po.order_line.pending_to_receive)
463-
self.assertEqual(self.po.order_line.existing_qty, 0)
474+
self.assertEqual(self.po.order_line.qty_in_receipt, 0)
464475
self.assertFalse(
465476
self.po.picking_ids,
466477
"Purchase Manual Delivery: no picking should had been created",
@@ -471,7 +482,7 @@ def test_06_purchase_order_manual_delivery_double_validation(self):
471482
self.po.env.user.groups_id += self.env.ref("purchase.group_purchase_manager")
472483
self.po.button_approve()
473484
self.assertTrue(self.po.order_line.pending_to_receive)
474-
self.assertEqual(self.po.order_line.existing_qty, 0)
485+
self.assertEqual(self.po.order_line.qty_in_receipt, 0)
475486
self.assertFalse(
476487
self.po.picking_ids,
477488
"Purchase Manual Delivery: no picking should had been created",

purchase_manual_delivery/tests/test_report_forecast.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,15 @@ def test_report_forecast(self):
4242
.create({})
4343
)
4444
wizard.fill_lines(self.po1.order_line)
45-
wizard.line_ids.qty = 35
45+
# Transfer a quantity of 12
46+
wizard.line_ids.qty = 12
47+
wizard.create_stock_picking()
48+
picking = self.po1.picking_ids
49+
picking.move_line_ids.quantity = 12
50+
picking.button_validate()
51+
52+
# Create an unvalidated picking with a quantity of 23
53+
wizard.line_ids.qty = 23
4654
wizard.create_stock_picking()
4755

4856
# Checks the report.
@@ -51,6 +59,7 @@ def test_report_forecast(self):
5159
)
5260
self.assertEqual(docs["no_delivery_purchase_qty"], 7)
5361
self.assertEqual(docs["qty"]["in"], 7)
62+
self.assertEqual(docs["quantity_on_hand"], 12)
5463
self.assertEqual(docs["virtual_available"], 35)
5564
self.assertEqual(
5665
docs["no_delivery_purchase_orders"],

purchase_manual_delivery/views/purchase_order_views.xml

+4-4
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@
4242
/>
4343
</button>
4444
<field name="qty_received" position="before">
45-
<field name="pending_to_receive" invisible="1" />
45+
<field name="pending_to_receive" column_invisible="1" />
4646
<field
47-
name="existing_qty"
47+
name="qty_in_receipt"
4848
optional="hide"
4949
column_invisible="parent.state not in ('purchase', 'done')"
5050
/>
@@ -73,10 +73,10 @@
7373
>state == 'purchase' and pending_to_receive</attribute>
7474
</xpath>
7575
<field name="product_qty" position="after">
76-
<field name="existing_qty" />
76+
<field name="qty_in_receipt" />
7777
</field>
7878
<field name="date_planned" position="after">
79-
<field name="pending_to_receive" />
79+
<field name="pending_to_receive" optional="hide" />
8080
<field name="state" invisible="1" />
8181
</field>
8282
</field>

purchase_manual_delivery/wizard/create_manual_stock_picking.py

+13-17
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,9 @@ def fill_lines(self, po_lines):
6868
{
6969
"purchase_order_line_id": line.id,
7070
"name": line.name,
71-
"product_id": line.product_id.id,
72-
"date_planned": line.date_planned,
73-
"price_unit": line.price_unit,
74-
"product_qty": line.product_qty,
75-
"existing_qty": line.existing_qty,
76-
"remaining_qty": line.product_qty - line.existing_qty,
77-
"qty": line.product_qty - line.existing_qty,
78-
"product_uom": line.product_uom.id,
79-
"currency_id": line.currency_id.id,
80-
"partner_id": line.partner_id.id,
81-
# 'taxes_id': line.taxes_id.ids,
71+
"remaining_qty": line.product_qty
72+
- (line.qty_in_receipt + line.qty_received),
73+
"qty": line.product_qty - (line.qty_in_receipt + line.qty_received),
8274
},
8375
)
8476
for line in po_lines
@@ -191,15 +183,17 @@ class CreateManualStockPickingWizardLine(models.TransientModel):
191183
related="purchase_order_line_id.product_qty",
192184
digits="Product Unit of Measure",
193185
)
194-
existing_qty = fields.Float(
195-
string="Existing Quantity",
196-
related="purchase_order_line_id.existing_qty",
186+
qty_in_receipt = fields.Float(
187+
related="purchase_order_line_id.qty_in_receipt",
188+
digits="Product Unit of Measure",
189+
)
190+
qty_received = fields.Float(
191+
related="purchase_order_line_id.qty_received",
197192
digits="Product Unit of Measure",
198193
)
199194
remaining_qty = fields.Float(
200195
string="Remaining Quantity",
201196
compute="_compute_remaining_qty",
202-
readonly=True,
203197
digits="Product Unit of Measure",
204198
)
205199
qty = fields.Float(
@@ -208,7 +202,7 @@ class CreateManualStockPickingWizardLine(models.TransientModel):
208202
help="This is the quantity taken into account to create the picking",
209203
)
210204
price_unit = fields.Float(
211-
related="purchase_order_line_id.price_unit", readonly=True
205+
related="purchase_order_line_id.price_unit",
212206
)
213207
currency_id = fields.Many2one(
214208
"res.currency", related="purchase_order_line_id.currency_id"
@@ -224,7 +218,9 @@ class CreateManualStockPickingWizardLine(models.TransientModel):
224218

225219
def _compute_remaining_qty(self):
226220
for line in self:
227-
line.remaining_qty = line.product_qty - line.existing_qty
221+
line.remaining_qty = line.product_qty - (
222+
line.qty_in_receipt + line.qty_received
223+
)
228224

229225
def _prepare_stock_moves(self, picking):
230226
po_line = self.purchase_order_line_id

purchase_manual_delivery/wizard/create_manual_stock_picking.xml

+3-2
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@
2525
</group>
2626
<field name="line_ids" nolabel="1">
2727
<tree create="false" editable="bottom">
28-
<field name="purchase_order_line_id" invisible="1" />
28+
<field name="purchase_order_line_id" column_invisible="1" />
2929
<field name="product_id" />
3030
<field name="partner_id" />
3131
<field name="date_planned" />
3232
<field name="price_unit" />
3333
<field name="product_qty" string="Ordered Qty" />
34-
<field name="existing_qty" string="Existing Qty" />
34+
<field name="qty_received" />
35+
<field name="qty_in_receipt" />
3536
<field name="remaining_qty" string="Remaining Qty" />
3637
<field name="qty" string="Quantity" />
3738
<field name="product_uom" groups="uom.group_uom" />

0 commit comments

Comments
 (0)