Merge lp:~camptocamp/ocb-addons/7.0-fix-1238508-nbi into lp:ocb-addons

Proposed by Nicolas Bessi - Camptocamp
Status: Merged
Merged at revision: 9598
Proposed branch: lp:~camptocamp/ocb-addons/7.0-fix-1238508-nbi
Merge into: lp:ocb-addons
Diff against target: 144 lines (+104/-7)
3 files modified
stock/__openerp__.py (+1/-0)
stock/stock.py (+5/-7)
stock/test/stock_move_chain_validation.yml (+98/-0)
To merge this branch: bzr merge lp:~camptocamp/ocb-addons/7.0-fix-1238508-nbi
Reviewer Review Type Date Requested Status
Alexandre Fayolle - camptocamp code review, automated test Approve
Holger Brunn (Therp) code review Approve
Stefan Rijnhart (Opener) Approve
Review via email: mp+190558@code.launchpad.net

Description of the change

To post a comment you must log in.
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

Hi Nicolas,

thanks! Can you please explain why this works? To my untrained eye in this area, it seems as if you only put action_confirm and the write action into the loop. That would make sense if action_confirm only takes one id into account like some workflow methods, but that does not seem to be the case either.

review: Needs Information
Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) wrote :

The removed code is outside the for loop:

                  if todo:
19 - self.action_confirm(cr, uid, todo, context=context)
20 -
21 - self.write(cr, uid, move_ids, {'state': 'done', 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}, context=context)

So the moves pass to state done at the end of the loop.

But the in the loop they do this search:
other_upstream_move_ids = self.search(cr, uid, [('id','!=',move.id),('state','not in',['done','cancel']),
                                            ('move_dest_id','=',move.move_dest_id.id)], context=context)

that has for finality to check that the current move is the last one in state confirmed/assigned.
But as the confirmation and the write is done after the loop this will never happen.

I just put the removed code inside the current loop.

Regards

Nicolas

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

OK, thanks for clarifying! Code looks good.

review: Approve
Revision history for this message
Holger Brunn (Therp) (hbrunn) :
review: Approve (code review)
Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote :

To be more precise, the problem occurs when several moves have the same move_dest_id (which happens when a move is split into different production lots, e.g. with the product_serial community addon on which we are working), and then all moves are processed.

The problem does not happen if the moves are processed one by one.

Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote :

I added a test, which I hope clarifies the case in which the issue occurs.

review: Approve (code review, automated test)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'stock/__openerp__.py'
2--- stock/__openerp__.py 2013-08-23 13:20:49 +0000
3+++ stock/__openerp__.py 2013-10-14 13:29:49 +0000
4@@ -94,6 +94,7 @@
5 # 'test/opening_stock.yml',
6 # 'test/shipment.yml',
7 # 'test/stock_report.yml',
8+ 'test/stock_move_chain_validation.yml',
9 ],
10 'installable': True,
11 'application': True,
12
13=== modified file 'stock/stock.py'
14--- stock/stock.py 2013-10-04 12:22:57 +0000
15+++ stock/stock.py 2013-10-14 13:29:49 +0000
16@@ -2383,7 +2383,6 @@
17 todo.append(move.id)
18 if todo:
19 self.action_confirm(cr, uid, todo, context=context)
20- todo = []
21
22 for move in self.browse(cr, uid, ids, context=context):
23 if move.state in ['done','cancel']:
24@@ -2407,12 +2406,11 @@
25
26 self._create_product_valuation_moves(cr, uid, move, context=context)
27 if move.state not in ('confirmed','done','assigned'):
28- todo.append(move.id)
29-
30- if todo:
31- self.action_confirm(cr, uid, todo, context=context)
32-
33- self.write(cr, uid, move_ids, {'state': 'done', 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}, context=context)
34+ self.action_confirm(cr, uid, [move.id], context=context)
35+ self.write(cr, uid, [move.id],
36+ {'state': 'done',
37+ 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)},
38+ context=context)
39 for id in move_ids:
40 wf_service.trg_trigger(uid, 'stock.move', id, cr)
41
42
43=== added file 'stock/test/stock_move_chain_validation.yml'
44--- stock/test/stock_move_chain_validation.yml 1970-01-01 00:00:00 +0000
45+++ stock/test/stock_move_chain_validation.yml 2013-10-14 13:29:49 +0000
46@@ -0,0 +1,98 @@
47+-
48+ I create an outgoing move for 2 LCD17
49+-
50+ !record {model: stock.move, id: dest_move_lcd17}:
51+ product_qty: 2
52+ product_id: product.product_product_7
53+ product_uom: product.product_uom_unit
54+ location_id: stock_location_components
55+ location_dest_id: stock_location_customers
56+-
57+ I create 2 moves for 1 LCD17 chained with the outgoing one
58+-
59+ !record {model: stock.move, id: move_lcd17_1}:
60+ product_qty: 1
61+ product_id: product.product_product_7
62+ product_uom: product.product_uom_unit
63+ location_id: stock_location_stock
64+ location_dest_id: stock_location_components
65+ move_dest_id: dest_move_lcd17
66+-
67+ !record {model: stock.move, id: move_lcd17_2}:
68+ product_qty: 1
69+ product_id: product.product_product_7
70+ product_uom: product.product_uom_unit
71+ location_id: stock_location_stock
72+ location_dest_id: stock_location_components
73+ move_dest_id: dest_move_lcd17
74+-
75+ I confirm all moves
76+-
77+ !python {model: stock.move}: |
78+ self.action_confirm(cr, uid, [ref('stock.dest_move_lcd17'),ref('stock.move_lcd17_1'),ref('stock.move_lcd17_2')], context=context)
79+-
80+ I process the 2 moves chained with the outgoing move
81+-
82+ !python {model: stock.move}: |
83+ self.action_done(cr, uid, [ref('stock.move_lcd17_1'),ref('stock.move_lcd17_2')], context=context)
84+-
85+ the outgoing move must be assigned
86+-
87+ !python {model: stock.move}: |
88+ move = self.browse(cr, uid, ref('stock.dest_move_lcd17'), context=context)
89+ assert move.state == 'assigned', "out move was not assigned when internal moves where processed"
90+-
91+ I create an outgoing move for 2 LCD17
92+-
93+ !record {model: stock.move, id: dest_move2_lcd17}:
94+ product_qty: 2
95+ product_id: product.product_product_7
96+ product_uom: product.product_uom_unit
97+ location_id: stock_location_components
98+ location_dest_id: stock_location_customers
99+-
100+ I create 2 moves for 1 LCD17 chained with the outgoing one
101+-
102+ !record {model: stock.move, id: move2_lcd17_1}:
103+ product_qty: 1
104+ product_id: product.product_product_7
105+ product_uom: product.product_uom_unit
106+ location_id: stock_location_stock
107+ location_dest_id: stock_location_components
108+ move_dest_id: dest_move2_lcd17
109+-
110+ !record {model: stock.move, id: move2_lcd17_2}:
111+ product_qty: 1
112+ product_id: product.product_product_7
113+ product_uom: product.product_uom_unit
114+ location_id: stock_location_stock
115+ location_dest_id: stock_location_components
116+ move_dest_id: dest_move2_lcd17
117+-
118+ I confirm all moves
119+-
120+ !python {model: stock.move}: |
121+ self.action_confirm(cr, uid, [ref('stock.dest_move2_lcd17'),ref('stock.move2_lcd17_1'),ref('stock.move2_lcd17_2')], context=context)
122+-
123+ I process the 1st move chained with the outgoing move
124+-
125+ !python {model: stock.move}: |
126+ self.action_done(cr, uid, [ref('stock.move2_lcd17_1')], context=context)
127+-
128+ the outgoing move must not be assigned
129+-
130+ !python {model: stock.move}: |
131+ move = self.browse(cr, uid, ref('stock.dest_move2_lcd17'), context=context)
132+ assert move.state != 'assigned', "out move was assigned when only 1st internal moves where processed"
133+-
134+ I process the 2nd move chained with the outgoing move
135+-
136+ !python {model: stock.move}: |
137+ self.action_done(cr, uid, [ref('stock.move2_lcd17_2')], context=context)
138+-
139+ the outgoing move must not be assigned
140+-
141+ !python {model: stock.move}: |
142+ move = self.browse(cr, uid, ref('stock.dest_move2_lcd17'), context=context)
143+ assert move.state == 'assigned', "out move was not assigned when internal moves where processed"
144+