Merge lp:~therp-nl/openobject-addons/7.0-addons_lp985525-wrong-bom-test into lp:openobject-addons/7.0

Proposed by Ronald Portier (Therp)
Status: Work in progress
Proposed branch: lp:~therp-nl/openobject-addons/7.0-addons_lp985525-wrong-bom-test
Merge into: lp:openobject-addons/7.0
Diff against target: 100 lines (+67/-20)
1 file modified
mrp/mrp.py (+67/-20)
To merge this branch: bzr merge lp:~therp-nl/openobject-addons/7.0-addons_lp985525-wrong-bom-test
Reviewer Review Type Date Requested Status
OpenERP Core Team Pending
Review via email: mp+166944@code.launchpad.net

Description of the change

Replace checks for recursion and for product not being part (directly or indirectly) of its own bom.

To post a comment you must log in.
Revision history for this message
Ronald Portier (Therp) (rportier1962) wrote :

Changed status to work in progress, because rethinking the problem the solution is not bullet proof.

My code guards against creating recursion by adding a product on a bom line that is among the ancestors (following bom_id) of that line.

But recursion can also be caused by creating a containing bill of material that has the product somewhere as a component among its descendents. I think that is the problem the original code tried (in the wrong way) to solve.

Even creating an "in between" bill of material, might cause recursion if it has some product somewhere both on the way up, or the way down.

Unmerged revisions

8690. By Ronald Portier (Therp)

[FIX] - Replace test for recursion in product or bom itself with
    code that works, is understandable, and uses ORM instead of plain SQL.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'mrp/mrp.py'
2--- mrp/mrp.py 2012-12-20 11:47:30 +0000
3+++ mrp/mrp.py 2013-06-01 17:25:55 +0000
4@@ -235,29 +235,76 @@
5 ]
6
7 def _check_recursion(self, cr, uid, ids, context=None):
8- level = 100
9- while len(ids):
10- cr.execute('select distinct bom_id from mrp_bom where id IN %s',(tuple(ids),))
11- ids = filter(None, map(lambda x:x[0], cr.fetchall()))
12- if not level:
13- return False
14- level -= 1
15+ for original_id in ids:
16+ check_id = original_id
17+ while check_id:
18+ record = self.read(
19+ cr, uid, [check_id], ['bom_id'], context=context)[0]
20+ if record and record['bom_id']:
21+ check_id = record['bom_id'][0]
22+ if check_id == original_id:
23+ return False
24+ else:
25+ check_id = False
26 return True
27
28 def _check_product(self, cr, uid, ids, context=None):
29- all_prod = []
30- boms = self.browse(cr, uid, ids, context=context)
31- def check_bom(boms):
32- res = True
33- for bom in boms:
34- if bom.product_id.id in all_prod:
35- res = res and False
36- all_prod.append(bom.product_id.id)
37- lines = bom.bom_lines
38- if lines:
39- res = res and check_bom([bom_id for bom_id in lines if bom_id not in boms])
40- return res
41- return check_bom(boms)
42+
43+ def get_record(cr, uid, mb_id, context=None):
44+ return self.read(
45+ cr, uid, [mb_id], ['bom_id', 'product_id'],
46+ context=context)[0]
47+
48+ def where_used(cr, uid, product_id, context=None):
49+ # Get all the records for which the main product is
50+ # a subproduct.
51+ product_ids = []
52+ bom_ids = self.search(
53+ cr, uid, [('product_id', '=', product_id),
54+ ('bom_id', '<>', False)],
55+ context=context)
56+ # get all the parents of these lines
57+ records = self.read(
58+ cr, uid, bom_ids, ['bom_id'],
59+ context=context)
60+ parent_ids = []
61+ for record in records:
62+ parent_id = record['bom_id'][0]
63+ if parent_id not in parent_ids:
64+ parent_ids.append(parent_id )
65+ # now read parents for the actual products
66+ records = self.read(
67+ cr, uid, parent_ids, ['product_id'],
68+ context=context)
69+ for record in records:
70+ product_id = record['product_id'][0]
71+ if not product_id in product_ids:
72+ product_ids.append(product_id)
73+ return product_ids
74+
75+ for original_id in ids:
76+ to_be_checked = []
77+ checked = []
78+
79+ record = get_record(cr, uid, original_id, context=context)
80+ # If bom record not for a line (but for main product)
81+ # no reason to check further.
82+ if not record['bom_id']:
83+ continue
84+ original_product = record['product_id'][0]
85+ record = get_record(cr, uid, record['bom_id'][0], context=context)
86+ to_be_checked.append(record['product_id'][0])
87+ while len(to_be_checked) > 0:
88+ product_id = to_be_checked.pop()
89+ if product_id == original_product:
90+ return False
91+ checked.append(product_id)
92+ maybe_check = where_used(
93+ cr, uid, product_id, context=None)
94+ for product_id in maybe_check:
95+ if product_id not in checked:
96+ to_be_checked.append(product_id)
97+ return True
98
99 _constraints = [
100 (_check_recursion, 'Error ! You cannot create recursive BoM.', ['parent_id']),