Merge lp:~openerp-dev/openobject-addons/trunk-mo-disassemble into lp:openobject-addons

Proposed by SnippetBucket.com
Status: Needs review
Proposed branch: lp:~openerp-dev/openobject-addons/trunk-mo-disassemble
Merge into: lp:openobject-addons
Diff against target: 1089 lines (+568/-49) (has conflicts)
16 files modified
mrp/__openerp__.py (+6/-0)
mrp/mrp.py (+192/-23)
mrp/mrp_demo.xml (+1/-1)
mrp/mrp_view.xml (+57/-10)
mrp/procurement.py (+28/-0)
mrp/static/src/js/mrp.js (+38/-0)
mrp/test/disassemble_process.yml (+96/-0)
mrp/test/order_demo.yml (+0/-2)
mrp/wizard/__init__.py (+4/-0)
mrp/wizard/change_production_qty.py (+1/-1)
mrp/wizard/disassemble_qty.py (+64/-0)
mrp/wizard/disassemble_qty_view.xml (+37/-0)
mrp/wizard/mrp_product_produce.py (+18/-1)
mrp/wizard/mrp_product_produce_view.xml (+20/-8)
mrp_byproduct/mrp_byproduct.py (+4/-2)
mrp_operations/mrp_operations.py (+2/-1)
Text conflict in mrp/__openerp__.py
Text conflict in mrp/mrp.py
Text conflict in mrp/mrp_view.xml
Text conflict in mrp/procurement.py
Text conflict in mrp/wizard/__init__.py
Text conflict in mrp/wizard/mrp_product_produce.py
Text conflict in mrp/wizard/mrp_product_produce_view.xml
To merge this branch: bzr merge lp:~openerp-dev/openobject-addons/trunk-mo-disassemble
Reviewer Review Type Date Requested Status
OpenERP Core Team Pending
Review via email: mp+164835@code.launchpad.net

Description of the change

Hello,

Task: [MRP] allow disassembling through manufacturing orders

In the Manufacturing Order, allow to use negative quantities in the Product Quantity field so that the selected product is dismantled and individual components are put back into inventory, with all appropriate stock moves.

More details on pad:
http://pad.openerp.com/p/r.Vquhp9c9xsuPRWaX

Thanks,
TTA

To post a comment you must log in.
8782. By Jitendra Prajapati(OpenERP)

[MERGE]with main branch

8783. By Jitendra Prajapati(OpenERP)

[FIX/IMP]except disassamble coming from procurement.

Unmerged revisions

8783. By Jitendra Prajapati(OpenERP)

[FIX/IMP]except disassamble coming from procurement.

8782. By Jitendra Prajapati(OpenERP)

[MERGE]with main branch

8781. By Jitendra Prajapati(OpenERP)

[MERGE]with main branch

8780. By Jitendra Prajapati(OpenERP)

[MERGE]with main branch

8779. By Jitendra Prajapati(OpenERP)

[MERGE]with main branch

8778. By Jitendra Prajapati(OpenERP)

[MERGE]with main branch

8777. By Jitendra Prajapati(OpenERP)

[MERGE]with main branch

8776. By Jitendra Prajapati(OpenERP)

[MERGE]with main branch

8775. By Jitendra Prajapati(OpenERP)

[MERGE]with main branch

8774. By Jitendra Prajapati(OpenERP)

[MERGE]with lp:openobject-addons

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'idea'
2=== added directory 'idea/static'
3=== added directory 'idea/static/src'
4=== added directory 'idea/static/src/img'
5=== modified file 'mrp/__openerp__.py'
6--- mrp/__openerp__.py 2014-05-07 17:12:05 +0000
7+++ mrp/__openerp__.py 2014-05-20 10:51:47 +0000
8@@ -62,7 +62,11 @@
9 'wizard/change_production_qty_view.xml',
10 'wizard/mrp_price_view.xml',
11 'wizard/mrp_workcenter_load_view.xml',
12+<<<<<<< TREE
13 'wizard/stock_move_view.xml',
14+=======
15+ 'wizard/disassemble_qty_view.xml',
16+>>>>>>> MERGE-SOURCE
17 'mrp_view.xml',
18 'mrp_report.xml',
19 'company_view.xml',
20@@ -78,9 +82,11 @@
21 'test/order_demo.yml',
22 'test/order_process.yml',
23 'test/cancel_order.yml',
24+ 'test/disassemble_process.yml',
25 ],
26 'installable': True,
27 'application': True,
28 'auto_install': False,
29+ 'js': ['static/src/js/mrp.js',],
30 }
31 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
32
33=== modified file 'mrp/mrp.py'
34--- mrp/mrp.py 2014-05-08 11:59:17 +0000
35+++ mrp/mrp.py 2014-05-20 10:51:47 +0000
36@@ -96,6 +96,12 @@
37 value = {'costs_hour': cost.standard_price}
38 return {'value': value}
39
40+class stock_move(osv.osv):
41+ _inherit = 'stock.move'
42+ _columns = {
43+ 'disassemble': fields.boolean('Disassemble'),
44+ }
45+
46 class mrp_routing(osv.osv):
47 """
48 For specifying the routings of Work Centers.
49@@ -426,14 +432,15 @@
50 """ Return product quantity percentage """
51 result = dict.fromkeys(ids, 100)
52 for mrp_production in self.browse(cr, uid, ids, context=context):
53- if mrp_production.product_qty:
54+ if abs(mrp_production.product_qty):
55 done = 0.0
56 for move in mrp_production.move_created_ids2:
57 if not move.scrapped and move.product_id == mrp_production.product_id:
58 done += move.product_qty
59- result[mrp_production.id] = done / mrp_production.product_qty * 100
60+ result[mrp_production.id] = done / abs(mrp_production.product_qty) * 100
61 return result
62
63+<<<<<<< TREE
64 def _moves_assigned(self, cr, uid, ids, name, arg, context=None):
65 """ Test whether all the consume lines are assigned """
66 res = {}
67@@ -451,6 +458,13 @@
68 res += self.pool.get("mrp.production").search(cr, uid, [('move_lines', 'in', move.id)], context=context)
69 return res
70
71+=======
72+ def create(self, cr, uid, values, context=None):
73+ if values['product_qty'] < 0:
74+ self._description = _('Disassemble Order')
75+ return super(mrp_production, self).create(cr, uid, values, context=context)
76+
77+>>>>>>> MERGE-SOURCE
78 _columns = {
79 'name': fields.char('Reference', size=64, required=True, readonly=True, states={'draft': [('readonly', False)]}),
80 'origin': fields.char('Source Document', size=64, readonly=True, states={'draft': [('readonly', False)]},
81@@ -493,8 +507,13 @@
82 'workcenter_lines': fields.one2many('mrp.production.workcenter.line', 'production_id', 'Work Centers Utilisation',
83 readonly=True, states={'draft': [('readonly', False)]}),
84 'state': fields.selection(
85+<<<<<<< TREE
86 [('draft', 'New'), ('cancel', 'Cancelled'), ('confirmed', 'Awaiting Raw Materials'),
87 ('ready', 'Ready to Produce'), ('in_production', 'Production Started'), ('done', 'Done')],
88+=======
89+ [('draft', 'New'), ('cancel', 'Cancelled'), ('picking_except', 'Picking Exception'), ('confirmed', 'Awaiting Raw Materials'),
90+ ('ready', 'Ready to Produce'), ('in_production', 'Production Started'), ('done', 'Done')],
91+>>>>>>> MERGE-SOURCE
92 string='Status', readonly=True,
93 track_visibility='onchange',
94 help="When the production order is created the status is set to 'Draft'.\n\
95@@ -505,21 +524,35 @@
96 When the production is over, the status is set to 'Done'."),
97 'hour_total': fields.function(_production_calc, type='float', string='Total Hours', multi='workorder', store=True),
98 'cycle_total': fields.function(_production_calc, type='float', string='Total Cycles', multi='workorder', store=True),
99+<<<<<<< TREE
100 'user_id': fields.many2one('res.users', 'Responsible'),
101 'company_id': fields.many2one('res.company', 'Company', required=True),
102 'ready_production': fields.function(_moves_assigned, type='boolean', store={'stock.move': (_mrp_from_move, ['state'], 10)}),
103+=======
104+ 'user_id':fields.many2one('res.users', 'Responsible'),
105+ 'company_id': fields.many2one('res.company','Company',required=True),
106+ 'disassemble': fields.boolean('Disassemble'),
107+ 'disassemble_doc': fields.char('Disassemble Document(s)', size=64, readonly=True, help="Reference of disassembled document(s) for this Manufacturing Order."),
108+ 'qty_to_disassemble': fields.float('Remaining Quantity to Disassemble', help="Available product quantity to disassemble", digits_compute=dp.get_precision('Product Unit of Measure')),
109+>>>>>>> MERGE-SOURCE
110 }
111
112 _defaults = {
113 'priority': lambda *a: '1',
114 'state': lambda *a: 'draft',
115 'date_planned': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
116+<<<<<<< TREE
117 'product_qty': lambda *a: 1.0,
118+=======
119+ 'product_qty': 1.0,
120+>>>>>>> MERGE-SOURCE
121 'user_id': lambda self, cr, uid, c: uid,
122 'name': lambda x, y, z, c: x.pool.get('ir.sequence').get(y, z, 'mrp.production') or '/',
123 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.production', context=c),
124 'location_src_id': _src_id_default,
125- 'location_dest_id': _dest_id_default
126+ 'location_dest_id': _dest_id_default,
127+ 'disassemble': False,
128+ 'disassemble_doc': False,
129 }
130
131 _sql_constraints = [
132@@ -528,16 +561,6 @@
133
134 _order = 'priority desc, date_planned asc'
135
136- def _check_qty(self, cr, uid, ids, context=None):
137- for order in self.browse(cr, uid, ids, context=context):
138- if order.product_qty <= 0:
139- return False
140- return True
141-
142- _constraints = [
143- (_check_qty, 'Order quantity cannot be negative or zero!', ['product_qty']),
144- ]
145-
146 def unlink(self, cr, uid, ids, context=None):
147 for production in self.browse(cr, uid, ids, context=context):
148 if production.state not in ('draft', 'cancel'):
149@@ -547,17 +570,37 @@
150 def copy(self, cr, uid, id, default=None, context=None):
151 if default is None:
152 default = {}
153+ mo = self.browse(cr, uid, id, context=context)
154+ origin = False
155+ if default.get('disassemble', False):
156+ origin = mo.origin + '-' + mo.name if not mo.disassemble and mo.origin else mo.name
157 default.update({
158 'name': self.pool.get('ir.sequence').get(cr, uid, 'mrp.production'),
159- 'move_lines': [],
160- 'move_lines2': [],
161- 'move_created_ids': [],
162- 'move_created_ids2': [],
163- 'product_lines': [],
164- 'move_prod_id': False,
165+<<<<<<< TREE
166+ 'move_lines': [],
167+ 'move_lines2': [],
168+ 'move_created_ids': [],
169+ 'move_created_ids2': [],
170+ 'product_lines': [],
171+ 'move_prod_id': False,
172+=======
173+ 'move_lines': [],
174+ 'move_lines2': [],
175+ 'move_created_ids': [],
176+ 'move_created_ids2': [],
177+ 'product_lines': [],
178+ 'move_prod_id': False,
179+ 'picking_id': False,
180+ 'origin': origin,
181+>>>>>>> MERGE-SOURCE
182 })
183 return super(mrp_production, self).copy(cr, uid, id, default, context)
184
185+ def onchange_product_qty(self, cr, uid, ids, quantity, context=None):
186+ if quantity < 0:
187+ return {'value': {'disassemble': True, 'qty_to_disassemble': quantity}}
188+ return {'value': {'disassemble': False, 'qty_to_disassemble': quantity}}
189+
190 def location_id_change(self, cr, uid, ids, src, dest, context=None):
191 """ Changes destination location if source location is changed.
192 @param src: Source location id.
193@@ -645,7 +688,7 @@
194 raise osv.except_osv(_('Error!'), _("Cannot find a bill of material for this product."))
195
196 # get components and workcenter_lines from BoM structure
197- factor = uom_obj._compute_qty(cr, uid, production.product_uom.id, production.product_qty, bom_point.product_uom.id)
198+ factor = uom_obj._compute_qty(cr, uid, production.product_uom.id, abs(production.product_qty), bom_point.product_uom.id)
199 res = bom_obj._bom_explode(cr, uid, bom_point, factor / bom_point.product_qty, properties, routing_id=production.routing_id.id)
200 results = res[0] # product_lines
201 results2 = res[1] # workcenter_lines
202@@ -667,6 +710,34 @@
203 """
204 return len(self._action_compute_lines(cr, uid, ids, properties=properties, context=context))
205
206+ def action_disassemble(self, cr, uid, id, qty, context=None):
207+ """ Disassemble the production order.
208+ """
209+ mo = self.browse(cr, uid, id, context=context)
210+ values = {
211+ 'disassemble': True,
212+ 'origin': mo.name,
213+ 'routing_id': False,
214+ 'product_qty': qty,
215+ 'qty_to_disassemble': 0,
216+ }
217+ mo_id = self.copy(cr, uid, mo.id, values, context=context)
218+ if mo_id:
219+ source_doc = self.read(cr, uid, mo_id, ['name'], context=context)
220+ mo.write({'disassemble_doc': mo.disassemble_doc and mo.disassemble_doc + ", " + source_doc['name'] or source_doc['name']}, context=context)
221+ view_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mrp', 'mrp_production_form_view')
222+ view_id = view_ref and view_ref[1] or False,
223+ return {
224+ 'type': 'ir.actions.act_window',
225+ 'name': _('Disassemble Manufacturing Order'),
226+ 'res_model': 'mrp.production',
227+ 'res_id': mo_id,
228+ 'view_type': 'form',
229+ 'view_mode': 'form',
230+ 'view_id': False,
231+ 'target': 'current',
232+ }
233+
234 def action_cancel(self, cr, uid, ids, context=None):
235 """ Cancels the production order and related stock moves.
236 @return: True
237@@ -691,9 +762,11 @@
238 @return: True
239 """
240 move_obj = self.pool.get('stock.move')
241- self.write(cr, uid, ids, {'state': 'ready'})
242
243 for production in self.browse(cr, uid, ids, context=context):
244+ if production.disassemble:
245+ self.action_confirm(cr, uid, [production.id])
246+ production.write({'state': 'ready'}, context=context)
247 if not production.move_created_ids:
248 self._make_production_produce_line(cr, uid, production, context=context)
249
250@@ -841,9 +914,53 @@
251 # trigger workflow if not products to consume (eg: services)
252 self.signal_button_produce(cr, uid, [production_id])
253
254+<<<<<<< TREE
255 produced_qty = self._get_produced_qty(cr, uid, production, context=context)
256
257 main_production_move = False
258+=======
259+ produced_qty = 0
260+ for produced_product in production.move_created_ids2:
261+ if (produced_product.scrapped) or (produced_product.product_id.id != production.product_id.id):
262+ continue
263+ produced_qty += produced_product.product_qty
264+ if production_mode in ['consume','consume_produce']:
265+ consumed_data = {}
266+
267+ # Calculate already consumed qtys
268+ for consumed in production.move_lines2:
269+ if consumed.scrapped:
270+ continue
271+ if not consumed_data.get(consumed.product_id.id, False):
272+ consumed_data[consumed.product_id.id] = 0
273+ consumed_data[consumed.product_id.id] += consumed.product_qty
274+
275+ # Find product qty to be consumed and consume it
276+ for scheduled in production.product_lines:
277+
278+ # total qty of consumed product we need after this consumption
279+ total_consume = ((production_qty + produced_qty) * scheduled.product_qty / abs(production.product_qty))
280+ # qty available for consume and produce
281+ qty_avail = scheduled.product_qty - consumed_data.get(scheduled.product_id.id, 0.0)
282+
283+ if float_compare(qty_avail, 0, precision_rounding=scheduled.product_id.uom_id.rounding) <= 0:
284+ # there will be nothing to consume for this raw material
285+ continue
286+
287+ raw_product = [move for move in production.move_lines if move.product_id.id==scheduled.product_id.id]
288+ if raw_product:
289+ # qtys we have to consume
290+ qty = total_consume - consumed_data.get(scheduled.product_id.id, 0.0)
291+ if float_compare(qty, qty_avail, precision_rounding=scheduled.product_id.uom_id.rounding) == 1:
292+ # if qtys we have to consume is more than qtys available to consume
293+ prod_name = scheduled.product_id.name_get()[0][1]
294+ raise osv.except_osv(_('Warning!'), _('You are going to consume total %s quantities of "%s".\nBut you can only consume up to total %s quantities.') % (qty, prod_name, qty_avail))
295+ if float_compare(qty, 0, precision_rounding=scheduled.product_id.uom_id.rounding) <= 0:
296+ # we already have more qtys consumed than we need
297+ continue
298+ raw_product[0].action_consume(qty, raw_product[0].location_id.id, context=context)
299+
300+>>>>>>> MERGE-SOURCE
301 if production_mode == 'consume_produce':
302 # To produce remaining qty of final product
303 #vals = {'state':'confirmed'}
304@@ -861,8 +978,14 @@
305 for produce_product in production.move_created_ids:
306 produced_qty = produced_products.get(produce_product.product_id.id, 0)
307 subproduct_factor = self._get_subproduct_factor(cr, uid, production.id, produce_product.id, context=context)
308+<<<<<<< TREE
309 rest_qty = (subproduct_factor * production.product_qty) - produced_qty
310 if float_compare(rest_qty, (subproduct_factor * production_qty), precision_rounding=produce_product.product_id.uom_id.rounding) < 0:
311+=======
312+ rest_qty = (subproduct_factor * abs(production.product_qty)) - produced_qty
313+
314+ if rest_qty < (subproduct_factor * production_qty):
315+>>>>>>> MERGE-SOURCE
316 prod_name = produce_product.product_id.name_get()[0][1]
317 raise osv.except_osv(_('Warning!'), _('You are going to produce total %s quantities of "%s".\nBut you can only produce up to total %s quantities.') % ((subproduct_factor * production_qty), prod_name, rest_qty))
318 if float_compare(rest_qty, 0, precision_rounding=produce_product.product_id.uom_id.rounding) > 0:
319@@ -963,8 +1086,19 @@
320 def test_ready(self, cr, uid, ids):
321 res = False
322 for production in self.browse(cr, uid, ids):
323+<<<<<<< TREE
324 if production.ready_production:
325 res = True
326+=======
327+ boms = self._action_compute_lines(cr, uid, [production.id])
328+ res = False
329+ for bom in boms:
330+ product = self.pool.get('product.product').browse(cr, uid, bom['product_id'])
331+ if production.disassemble:
332+ res = False
333+ elif product.type in ('product', 'consu'):
334+ res = True
335+>>>>>>> MERGE-SOURCE
336 return res
337
338
339@@ -976,13 +1110,21 @@
340 'name': production.name,
341 'date': production.date_planned,
342 'product_id': production.product_id.id,
343+<<<<<<< TREE
344+=======
345+ 'product_qty': abs(production.product_qty),
346+>>>>>>> MERGE-SOURCE
347 'product_uom': production.product_uom.id,
348 'product_uom_qty': production.product_qty,
349 'product_uos_qty': production.product_uos and production.product_uos_qty or False,
350 'product_uos': production.product_uos and production.product_uos.id or False,
351- 'location_id': source_location_id,
352- 'location_dest_id': destination_location_id,
353+ 'location_id': production.disassemble and destination_location_id or source_location_id ,
354+ 'location_dest_id': production.disassemble and source_location_id or destination_location_id,
355 'move_dest_id': production.move_prod_id.id,
356+<<<<<<< TREE
357+=======
358+ 'state': production.disassemble and 'assigned' or 'waiting', # for products to produce
359+>>>>>>> MERGE-SOURCE
360 'company_id': production.company_id.id,
361 'production_id': production.id,
362 'origin': production.name,
363@@ -1019,6 +1161,7 @@
364 move_id = stock_move.create(cr, uid, {
365 'name': production.name,
366 'date': production.date_planned,
367+<<<<<<< TREE
368 'product_id': product.id,
369 'product_uom_qty': qty,
370 'product_uom': uom_id,
371@@ -1026,12 +1169,27 @@
372 'product_uos': uos_id or False,
373 'location_id': source_location_id,
374 'location_dest_id': destination_location_id,
375+=======
376+ 'product_id': production_line.product_id.id,
377+ 'product_qty': production_line.product_qty,
378+ 'product_uom': production_line.product_uom.id,
379+ 'product_uos_qty': production_line.product_uos and production_line.product_uos_qty or False,
380+ 'product_uos': production_line.product_uos and production_line.product_uos.id or False,
381+ 'location_id': production.disassemble and destination_location_id or source_location_id,
382+ 'location_dest_id': production.disassemble and source_location_id or destination_location_id,
383+ 'move_dest_id': parent_move_id,
384+ 'state': production.disassemble and 'assigned' or 'waiting', # for products to consume
385+>>>>>>> MERGE-SOURCE
386 'company_id': production.company_id.id,
387+<<<<<<< TREE
388 'procure_method': self._get_raw_material_procure_method(cr, uid, product, context=context),
389 'raw_material_production_id': production.id,
390 #this saves us a browse in create()
391 'price_unit': product.standard_price,
392 'origin': production.name,
393+=======
394+ 'disassemble': production.disassemble
395+>>>>>>> MERGE-SOURCE
396 })
397 return move_id
398
399@@ -1045,9 +1203,20 @@
400 uncompute_ids = filter(lambda x: x, [not x.product_lines and x.id or False for x in self.browse(cr, uid, ids, context=context)])
401 self.action_compute(cr, uid, uncompute_ids, context=context)
402 for production in self.browse(cr, uid, ids, context=context):
403+<<<<<<< TREE
404 self._make_production_produce_line(cr, uid, production, context=context)
405
406 stock_moves = []
407+=======
408+ if not production.disassemble:
409+ shipment_id = self._make_production_internal_shipment(cr, uid, production, context=context)
410+ produce_move_id = self._make_production_produce_line(cr, uid, production, context=context)
411+
412+ # Take routing location as a Source Location.
413+ source_location_id = production.location_src_id.id
414+ if production.bom_id.routing_id and production.bom_id.routing_id.location_id:
415+ source_location_id = production.bom_id.routing_id.location_id.id
416+>>>>>>> MERGE-SOURCE
417 for line in production.product_lines:
418 stock_move_id = self._make_production_consume_line(cr, uid, line, context=context)
419 if stock_move_id:
420
421=== modified file 'mrp/mrp_demo.xml'
422--- mrp/mrp_demo.xml 2014-01-20 14:42:43 +0000
423+++ mrp/mrp_demo.xml 2014-05-20 10:51:47 +0000
424@@ -38,7 +38,6 @@
425
426 <record id="mrp_workcenter_0" model="mrp.workcenter">
427 <field name="name">Assembly workshop</field>
428- <field name="calendar_id" ref="resource.timesheet_group1"/>
429 <field name="capacity_per_cycle">5</field>
430 <field name="time_cycle">1</field>
431 <field name="time_start">0.1</field>
432@@ -661,6 +660,7 @@
433 <record id="mrp_production_2" model="mrp.production">
434 <field name="product_id" ref="product.product_product_27"/>
435 <field name="product_uom" ref="product.product_uom_unit"/>
436+ <field name="product_qty">5</field>
437 <field name="location_src_id" ref="stock.stock_location_stock"/>
438 <field name="location_dest_id" ref="stock.stock_location_output"/>
439 <field name="bom_id" ref="mrp.mrp_bom_7"/>
440
441=== modified file 'mrp/mrp_view.xml'
442--- mrp/mrp_view.xml 2014-05-07 18:29:17 +0000
443+++ mrp/mrp_view.xml 2014-05-20 10:51:47 +0000
444@@ -671,32 +671,57 @@
445 <field name="arch" type="xml">
446 <form string="Manufacturing Orders" version="7.0">
447 <header>
448+<<<<<<< TREE
449 <button name="button_confirm" states="draft" string="Confirm Production" class="oe_highlight"/>
450 <button name="%(act_mrp_product_produce)d" states="ready,in_production" string="Produce" type="action" class="oe_highlight"/>
451 <button name="action_assign" states="confirmed,picking_except" string="Check Availability" type="object" class="oe_highlight"/>
452 <button name="force_production" states="confirmed" string="Force Reservation" type="object"/>
453+=======
454+ <button name="button_confirm" string="Confirm Production" class="oe_highlight" attrs="{'invisible':['|',('disassemble', '=', True),('state','!=','draft')]}"/>
455+ <button name="button_confirm" string="Confirm Dismantling" class="oe_highlight" attrs="{'invisible':['|',('disassemble', '=', False),('state','!=','draft')]}"/>
456+ <button name="%(act_mrp_product_produce)d" string="Produce" type="action" class="oe_highlight" attrs="{'invisible':['|',('disassemble', '=', True),('state','not in',('ready', 'in_production'))]}"/>
457+ <button name="%(act_mrp_disassemble_product)d" string="Disassemble" type="action" class="oe_highlight" attrs="{'invisible':['|',('disassemble', '=', False),('state','not in',('confirmed', 'ready', 'in_production'))]}"/>
458+ <button name="force_production" states="confirmed" string="Force Reservation" type="object" class="oe_highlight"/>
459+ <button name="force_production" states="picking_except" string="Force Reservation" type="object"/>
460+>>>>>>> MERGE-SOURCE
461 <button name="button_produce" states="ready" string="Mark as Started"/>
462+<<<<<<< TREE
463 <button name="button_cancel" states="draft,ready,in_production" string="Cancel Production"/>
464 <button name="action_cancel" type="object" states="confirmed" string="Cancel Production"/>
465 <field name="state" widget="statusbar" statusbar_visible="draft,ready,in_production,done" statusbar_colors='{"confirmed":"blue"}'/>
466+=======
467+ <button name="button_recreate" states="picking_except" string="Recreate Picking"/>
468+ <button name="button_cancel" string="Cancel Production" attrs="{'invisible':['|',('disassemble', '=', True),('state','not in',('draft','ready','in_production','picking_except'))]}"/>
469+ <button name="button_cancel" string="Cancel Dismantling" attrs="{'invisible':['|',('disassemble', '=', False),('state','not in',('draft','ready','in_production','picking_except'))]}"/>
470+ <button name="action_cancel" type="object" string="Cancel Production" attrs="{'invisible':['|',('disassemble', '=', True),('state','!=','confirmed')]}"/>
471+ <button name="action_cancel" type="object" string="Cancel Dismantling" attrs="{'invisible':['|',('disassemble', '=', False), ('state','!=','confirmed')]}"/>
472+ <button name="%(action_change_disassemble_qty)d" type="action" string="Disassemble" attrs="{'invisible':['|','|', ('state','!=','done'), ('qty_to_disassemble','=',0)]}"/>
473+ <field name="state" widget="statusbar" statusbar_visible="draft,ready,in_production,done" statusbar_colors='{"picking_except":"red","confirmed":"blue"}'/>
474+>>>>>>> MERGE-SOURCE
475 </header>
476 <sheet>
477 <div class="oe_title">
478- <h1>Manufacturing Order <field name="name" class="oe_inline"/></h1>
479+ <h1><label string="Disassemble" attrs="{'invisible':[('disassemble','=',False)]}" class="oe_inline"/>
480+ Manufacturing Order <field name="name" class="oe_inline"/></h1>
481 </div>
482 <group>
483 <group>
484+<<<<<<< TREE
485 <field name="product_id" on_change="product_id_change(product_id, product_qty)" domain="[('bom_ids','!=',False),('bom_ids.bom_id','=',False),('bom_ids.type','!=','phantom')]" class="oe_inline" context='{"default_type": "product"}'/>
486+=======
487+ <field name="product_id" on_change="product_id_change(product_id, product_qty)" domain="[('bom_ids','!=',False),('bom_ids.bom_id','=',False)]" class="oe_inline" context='{"default_supply_method":"produce", "default_type": "product"}'/>
488+ <field name="qty_to_disassemble" invisible="1"/>
489+>>>>>>> MERGE-SOURCE
490 <label for="product_qty"/>
491 <div>
492- <field name="product_qty" class="oe_inline" on_change="product_id_change(product_id, product_qty)"/>
493+ <field name="product_qty" widget="mrp_product_qty" class="oe_inline" on_change="onchange_product_qty(product_qty)"/>
494 <field name="product_uom" groups="product.group_uom" class="oe_inline"/>
495 <button type="action"
496 name="%(mrp.action_change_production_qty)d"
497 string="Update" states="confirmed" class="oe_edit_only oe_link"/>
498 </div>
499- <label for="product_uos_qty" groups="product.group_uos"/>
500- <div groups="product.group_uos">
501+ <label for="product_uos_qty" attrs="{'invisible':[('disassemble', '=', True)]}" groups="product.group_uos"/>
502+ <div attrs="{'invisible':[('disassemble', '=', True)]}" groups="product.group_uos">
503 <field name="product_uos_qty" class="oe_inline"/>
504 <label string="-" attrs="{'invisible':[('product_uos','=',False)]}" class="oe_inline"/>
505 <field name="product_uos" class="oe_inline"/>
506@@ -715,16 +740,21 @@
507 </group>
508 </group>
509 <notebook>
510- <page string="Consumed Products">
511+ <page string="Consumed Products" name="consumed_products">
512 <group>
513- <group string="Products to Consume">
514+ <group string="Products to Consume" name="group_to_consume">
515 <field name="move_lines" nolabel="1" options="{'reload_on_button': true}">
516 <tree colors="blue:state == 'draft';black:state in ('ready','assigned','in_production');gray:state in ('cancel','done');red:state in ('confirmed','waiting')" string="Products to Consume">
517 <field name="product_id"/>
518 <field name="product_qty" string="Quantity"/>
519 <field name="product_uom" string="Unit of Measure" groups="product.group_uom"/>
520 <field name="state" invisible="1"/>
521+<<<<<<< TREE
522 <button name="%(mrp.move_consume)d"
523+=======
524+ <field name="disassemble" invisible="1"/>
525+ <button name="%(stock.move_consume)d"
526+>>>>>>> MERGE-SOURCE
527 string="Consume Products" type="action"
528 icon="gtk-go-forward" context="{'consume': True}"
529 states="assigned"
530@@ -732,11 +762,11 @@
531 <button name="%(stock.move_scrap)d"
532 string="Scrap Products" type="action"
533 icon="terp-gtk-jump-to-ltr" context="{'scrap': True}"
534- states="draft,waiting,confirmed,assigned"/>
535+ attrs="{'invisible':['|',('disassemble', '=', True),('state', '=', 'cancel')]}"/>
536 </tree>
537 </field>
538 </group>
539- <group string="Consumed Products">
540+ <group string="Consumed Products" name="group_consumed">
541 <field name="move_lines2" nolabel="1" options="{'reload_on_button': true}">
542 <tree colors="red:scrapped==True;blue:state == 'draft';black:state in ('confirmed','ready','in_production');gray:state == 'cancel' " string="Consumed Products" editable="bottom">
543 <field name="product_id" readonly="1"/>
544@@ -745,12 +775,17 @@
545 <field name="product_uom" readonly="1" string="Unit of Measure" groups="product.group_uom"/>
546 <field name="state" invisible="1"/>
547 <field name="scrapped" invisible="1"/>
548+ <field name="disassemble" invisible="1"/>
549+ <button name="%(stock.move_scrap)d"
550+ string="Scrap Products" type="action"
551+ icon="terp-gtk-jump-to-ltr" context="{'scrap': True}"
552+ attrs="{'invisible':['|',('disassemble', '=', False),('state', '=', 'cancel')]}"/>
553 </tree>
554 </field>
555 </group>
556 </group>
557 </page>
558- <page string="Finished Products">
559+ <page string="Finished Products" attrs="{'invisible':[('disassemble', '=', True)]}">
560 <group>
561 <group string="Products to Produce">
562 <field name="move_created_ids" nolabel="1" options="{'reload_on_button': true}">
563@@ -804,7 +839,7 @@
564 </tree>
565 </field>
566 </page>
567- <page string="Scheduled Products">
568+ <page string="Scheduled Products" attrs="{'invisible':[('disassemble', '=', True)]}">
569 <button name="action_compute" states="draft"
570 string="Compute Data" type="object"
571 icon="terp-stock_format-scientific"/>
572@@ -816,10 +851,18 @@
573 <field name="priority"/>
574 <field name="date_start" invisible="1"/>
575 <field name="date_finished" invisible="1"/>
576+ <field name="disassemble" invisible="1"/>
577+ <field name="disassemble_doc" attrs="{'invisible':[('disassemble_doc', '=', False)]}"/>
578 </group>
579 <group>
580+<<<<<<< TREE
581 <field name="company_id" groups="base.group_multi_company" widget="selection" />
582 <field name="move_prod_id" groups="stock.group_locations"/>
583+=======
584+ <field name="company_id" groups="base.group_multi_company" widget="selection"/>
585+ <field name="picking_id" attrs="{'invisible':[('disassemble', '=', True)]}"/>
586+ <field name="move_prod_id" attrs="{'invisible':[('disassemble', '=', True)]}" groups="stock.group_locations"/>
587+>>>>>>> MERGE-SOURCE
588 </group>
589 </group>
590 </page>
591@@ -845,6 +888,10 @@
592 help="Manufacturing Orders which are ready to start production."/>
593 <filter icon="terp-check" string="In Production" name="inprogress" domain="[('state','=','in_production')]"
594 help="Manufacturing Orders which are currently in production."/>
595+ <filter string="Manufacturing" name="manufacturing" domain="[('disassemble', '=', False)]"
596+ help="Manufacturing Orders."/>
597+ <filter string="Disassemble" name="disassemble" domain="[('product_qty', '&lt;', 0)]"
598+ help="Disassemble Orders."/>
599 <separator/>
600 <filter icon="terp-gnome-cpu-frequency-applet+" string="Late" domain="['&amp;', ('date_planned','&lt;', current_date), ('state', 'in', ('draft', 'confirmed', 'ready'))]"
601 help="Production started late"/>
602
603=== modified file 'mrp/procurement.py'
604--- mrp/procurement.py 2014-05-07 17:14:31 +0000
605+++ mrp/procurement.py 2014-05-20 10:51:47 +0000
606@@ -77,6 +77,7 @@
607 bom_obj = self.pool.get('mrp.bom')
608 procurement_obj = self.pool.get('procurement.order')
609 for procurement in procurement_obj.browse(cr, uid, ids, context=context):
610+<<<<<<< TREE
611 if self.check_bom_exists(cr, uid, [procurement.id], context=context):
612 if procurement.bom_id:
613 bom_id = procurement.bom_id.id
614@@ -116,6 +117,33 @@
615 else:
616 res[procurement.id] = False
617 self.message_post(cr, uid, [procurement.id], body=_("No BoM exists for this product!"), context=context)
618+=======
619+ res_id = procurement.move_id.id
620+ newdate = datetime.strptime(procurement.date_planned, '%Y-%m-%d %H:%M:%S') - relativedelta(days=procurement.product_id.produce_delay or 0.0)
621+ newdate = newdate - relativedelta(days=company.manufacturing_lead)
622+ produce_id = production_obj.create(cr, uid, {
623+ 'origin': procurement.origin,
624+ 'product_id': procurement.product_id.id,
625+ 'product_qty': procurement.product_qty,
626+ 'qty_to_disassemble': procurement.product_qty,
627+ 'product_uom': procurement.product_uom.id,
628+ 'product_uos_qty': procurement.product_uos and procurement.product_uos_qty or False,
629+ 'product_uos': procurement.product_uos and procurement.product_uos.id or False,
630+ 'location_src_id': procurement.location_id.id,
631+ 'location_dest_id': procurement.location_id.id,
632+ 'bom_id': procurement.bom_id and procurement.bom_id.id or False,
633+ 'date_planned': newdate.strftime('%Y-%m-%d %H:%M:%S'),
634+ 'move_prod_id': res_id,
635+ 'company_id': procurement.company_id.id,
636+ })
637+
638+ res[procurement.id] = produce_id
639+ self.write(cr, uid, [procurement.id], {'state': 'running', 'production_id': produce_id})
640+ bom_result = production_obj.action_compute(cr, uid,
641+ [produce_id], properties=[x.id for x in procurement.property_ids])
642+ production_obj.signal_button_confirm(cr, uid, [produce_id])
643+ self.production_order_create_note(cr, uid, ids, context=context)
644+>>>>>>> MERGE-SOURCE
645 return res
646
647 def production_order_create_note(self, cr, uid, procurement, context=None):
648
649=== added directory 'mrp/static/src/js'
650=== added file 'mrp/static/src/js/mrp.js'
651--- mrp/static/src/js/mrp.js 1970-01-01 00:00:00 +0000
652+++ mrp/static/src/js/mrp.js 2014-05-20 10:51:47 +0000
653@@ -0,0 +1,38 @@
654+openerp.mrp = function(instance) {
655+var _t = instance.web._t;
656+
657+instance.web.form.mrp_product_qty = instance.web.form.FieldFloat.extend({
658+ init: function(field_manager, node) {
659+ var self = this;
660+ this._super(field_manager, node);
661+ this.on("change:value", this, function() {
662+ self.update_label(this.get('value'));
663+ });
664+ var $page = this.view.$el.find("a[name='consumed_products']");
665+ var $label_product = this.view.$el.find("div[name='group_to_consume']");
666+ var $label_consumed = this.view.$el.find("div[name='group_consumed']");
667+ this.label_list = [$page, $label_product, $label_consumed];
668+ this.label_value = [[_t("Consumed Products"),_t("Disassembled Products")],
669+ [_t("Products to Consume"),_t("Products to Disassemble")],
670+ [_t("Consumed Products"),_t("Disassembled Products")]];
671+ },
672+ update_label:function(value){
673+ var self = this
674+ _.map(self.label_list, function(ele, i){
675+ value > 0 ? ele.text(self.label_value[i][0]) : ele.text(self.label_value[i][1])
676+ });
677+ },
678+ parse_value: function(val, def) {
679+ if (this.widget) this.widget='float'
680+ return instance.web.parse_value(val, this, def);
681+ },
682+ format_value: function(val, def) {
683+ if (this.widget) this.widget='float'
684+ return instance.web.format_value(val, this, def);
685+ },
686+
687+});
688+instance.web.form.widgets = instance.web.form.widgets.extend({
689+'mrp_product_qty': 'instance.web.form.mrp_product_qty',
690+});
691+}
692
693=== added file 'mrp/test/disassemble_process.yml'
694--- mrp/test/disassemble_process.yml 1970-01-01 00:00:00 +0000
695+++ mrp/test/disassemble_process.yml 2014-05-20 10:51:47 +0000
696@@ -0,0 +1,96 @@
697+-
698+ MRP user can doing all process related to Production Order, so let's check data with giving the access rights of user.
699+-
700+ !context
701+ uid: 'res_users_mrp_user'
702+-
703+ In order to test Disassemble feature in OpenERP we will create a Production order with negative quantity for PC Assemble SC349
704+-
705+ !record {model: mrp.production, id: mrp_production_test2}:
706+ product_id: product.product_product_4
707+ product_qty: -5.0
708+ location_src_id: stock.stock_location_14
709+ location_dest_id: stock.stock_location_output
710+ bom_id: mrp_bom_24
711+ routing_id: mrp.mrp_routing_1
712+-
713+ I compute the production order.
714+-
715+ !python {model: mrp.production}: |
716+ order = self.browse(cr, uid, ref("mrp_production_test2"), context=context)
717+ order.action_compute(context=context)
718+-
719+ I check production lines after compute.
720+-
721+ !python {model: mrp.production}: |
722+ order = self.browse(cr, uid, ref("mrp_production_test2"), context=context)
723+ assert len(order.product_lines) == 5, "Production lines are not generated proper."
724+-
725+ Now I check workcenter lines.
726+-
727+ !python {model: mrp.production}: |
728+ from openerp.tools import float_compare
729+ def assert_equals(value1, value2, msg, float_compare=float_compare):
730+ assert float_compare(value1, value2, precision_digits=2) == 0, msg
731+ order = self.browse(cr, uid, ref("mrp_production_test2"), context=context)
732+ assert len(order.workcenter_lines), "Workcenter lines are not generated proper."
733+-
734+ I confirm the Production Order.
735+-
736+ !workflow {model: mrp.production, action: button_confirm, ref: mrp_production_test2}
737+-
738+ I check details of Produce Move of Production Order to trace Final Product.
739+-
740+ !python {model: mrp.production}: |
741+ order = self.browse(cr, uid, ref("mrp_production_test2"))
742+ assert order.state == 'ready', "Production order should be ready."
743+ assert order.move_created_ids, "Trace Record is not created for Final Product."
744+ move = order.move_created_ids[0]
745+ source_location_id = order.product_id.property_stock_production.id
746+ assert move.date == order.date_planned, "Planned date is not correspond."
747+ assert move.product_id.id == order.product_id.id, "Product is not correspond."
748+ assert move.product_uom.id == order.product_uom.id, "UOM is not correspond."
749+ assert move.product_qty == abs(order.product_qty), "Qty is not correspond."
750+ assert move.product_uos_qty == order.product_uos and order.product_uos_qty or abs(order.product_qty), "UOS qty is not correspond."
751+ if order.product_uos:
752+ assert move.product_uos.id == order.product_uos.id, "UOS is not correspond."
753+ assert move.location_id.id == order.location_dest_id.id, "Source Location is not correspond."
754+ assert move.location_dest_id.id == source_location_id, "Destination Location is not correspond."
755+ routing_loc = None
756+ if order.bom_id.routing_id and order.bom_id.routing_id.location_id:
757+ routing_loc = order.bom_id.routing_id.location_id.id
758+ date_planned = order.date_planned
759+ for move_line in order.move_lines:
760+ for order_line in order.product_lines:
761+ if move_line.product_id.type not in ('product', 'consu'):
762+ continue
763+ if move_line.product_id.id == order_line.product_id.id:
764+ assert move_line.date == date_planned, "Planned date is not correspond in 'To consume line'."
765+ assert move_line.product_qty == order_line.product_qty, "Qty is not correspond in 'To consume line'."
766+ assert move_line.product_uom.id == order_line.product_uom.id, "UOM is not correspond in 'To consume line'."
767+ assert move_line.product_uos_qty == order_line.product_uos and order_line.product_uos_qty or order_line.product_qty, "UOS qty is not correspond in 'To consume line'."
768+ if order_line.product_uos:
769+ assert move_line.product_uos.id == order_line.product_uos.id, "UOS is not correspond in 'To consume line'."
770+ assert move_line.location_id.id == source_location_id, "Source location is not correspond in 'To consume line'."
771+ assert move_line.location_dest_id.id == routing_loc or order.location_src_id.id, "Destination Location is not correspond in 'To consume line'."
772+-
773+ I check that production order in ready state.
774+-
775+ !python {model: mrp.production}: |
776+ order = self.browse(cr, uid, ref("mrp_production_test2"))
777+ assert order.state == 'ready', 'Production order should be in Ready State.'
778+
779+-
780+ I check that production order in production state after start production.
781+-
782+ !python {model: mrp.production}: |
783+ order = self.browse(cr, uid, ref("mrp_production_test2"))
784+ context.update({'active_id': ref('mrp_production_test2')})
785+ assert self.action_produce(cr, uid, order.id , 5.0, 'consume_produce'), 'Can not do action produce.'
786+
787+-
788+ I check that production order in Done state.
789+-
790+ !python {model: mrp.production}: |
791+ order = self.browse(cr, uid, ref("mrp_production_test2"))
792+ assert order.state == 'done', 'Production order should be in Done State.'
793
794=== modified file 'mrp/test/order_demo.yml'
795--- mrp/test/order_demo.yml 2014-04-04 16:17:13 +0000
796+++ mrp/test/order_demo.yml 2014-05-20 10:51:47 +0000
797@@ -13,5 +13,3 @@
798 location_dest_id: stock.stock_location_output
799 bom_id: mrp_bom_24
800 routing_id: mrp.mrp_routing_1
801-
802-
803
804=== modified file 'mrp/wizard/__init__.py'
805--- mrp/wizard/__init__.py 2014-01-28 15:18:34 +0000
806+++ mrp/wizard/__init__.py 2014-05-20 10:51:47 +0000
807@@ -23,7 +23,11 @@
808 import mrp_price
809 import mrp_workcenter_load
810 import change_production_qty
811+<<<<<<< TREE
812 import stock_move
813+=======
814+import disassemble_qty
815+>>>>>>> MERGE-SOURCE
816 #import mrp_change_standard_price
817
818 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
819
820=== modified file 'mrp/wizard/change_production_qty.py'
821--- mrp/wizard/change_production_qty.py 2013-11-29 16:56:44 +0000
822+++ mrp/wizard/change_production_qty.py 2014-05-20 10:51:47 +0000
823@@ -71,7 +71,7 @@
824 move_obj = self.pool.get('stock.move')
825 for wiz_qty in self.browse(cr, uid, ids, context=context):
826 prod = prod_obj.browse(cr, uid, record_id, context=context)
827- prod_obj.write(cr, uid, [prod.id], {'product_qty': wiz_qty.product_qty})
828+ prod_obj.write(cr, uid, [prod.id], {'product_qty': wiz_qty.product_qty, 'qty_to_disassemble': wiz_qty.product_qty, 'disassemble': wiz_qty.product_qty < 0 and True or False}, context=context)
829 prod_obj.action_compute(cr, uid, [prod.id])
830
831 for move in prod.move_lines:
832
833=== added file 'mrp/wizard/disassemble_qty.py'
834--- mrp/wizard/disassemble_qty.py 1970-01-01 00:00:00 +0000
835+++ mrp/wizard/disassemble_qty.py 2014-05-20 10:51:47 +0000
836@@ -0,0 +1,64 @@
837+# -*- coding: utf-8 -*-
838+##############################################################################
839+#
840+# OpenERP, Open Source Management Solution
841+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
842+#
843+# This program is free software: you can redistribute it and/or modify
844+# it under the terms of the GNU Affero General Public License as
845+# published by the Free Software Foundation, either version 3 of the
846+# License, or (at your option) any later version.
847+#
848+# This program is distributed in the hope that it will be useful,
849+# but WITHOUT ANY WARRANTY; without even the implied warranty of
850+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
851+# GNU Affero General Public License for more details.
852+#
853+# You should have received a copy of the GNU Affero General Public License
854+# along with this program. If not, see <http://www.gnu.org/licenses/>.
855+#
856+##############################################################################
857+
858+from openerp.osv import fields, osv
859+from openerp.tools.translate import _
860+import openerp.addons.decimal_precision as dp
861+
862+class change_disassemble_qty(osv.osv_memory):
863+ _name = 'change.disassemble.qty'
864+ _description = 'Change Quantity for Disassemble Products'
865+
866+ _columns = {
867+ 'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
868+ }
869+
870+ def default_get(self, cr, uid, fields, context=None):
871+ """ To get default values for the object.
872+ @param self: The object pointer.
873+ @param cr: A database cursor
874+ @param uid: ID of the user currently logged in
875+ @param fields: List of fields for which we want default values
876+ @param context: A standard dictionary
877+ @return: A dictionary which of fields with values.
878+ """
879+ if context is None:
880+ context = {}
881+ res = super(change_disassemble_qty, self).default_get(cr, uid, fields, context=context)
882+ prod = self.pool.get('mrp.production').browse(cr, uid, context.get('active_id'), context=context)
883+ if 'product_qty' in fields:
884+ res.update({'product_qty': prod.qty_to_disassemble * -1 })
885+ return res
886+
887+ def change_disassemble_qty(self, cr, uid, ids, context=None):
888+ mrp_production_obj = self.pool.get('mrp.production')
889+ mrp_id = context.get('active_id', False)
890+ qty = self.browse(cr, uid, ids[0], context=context).product_qty
891+ if qty >= 0:
892+ raise osv.except_osv(_('Warning!'), _('Quantity must be negative to disassemble.'))
893+ mrp_record = mrp_production_obj.browse(cr, uid, mrp_id, context=context)
894+ mrp_record.write({'qty_to_disassemble': mrp_record.qty_to_disassemble - abs(qty)}, context=context)
895+ if mrp_record.qty_to_disassemble < abs(qty) :
896+ raise osv.except_osv(_('Warning!'), _('You are going to disassemble total %s quantities of "%s".\nBut you can only disassemble up to total %s quantities.') % (abs(qty), mrp_record.product_id.name, mrp_record.qty_to_disassemble))
897+ return mrp_production_obj.action_disassemble(cr, uid, mrp_id, qty, context=context)
898+
899+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
900+
901
902=== added file 'mrp/wizard/disassemble_qty_view.xml'
903--- mrp/wizard/disassemble_qty_view.xml 1970-01-01 00:00:00 +0000
904+++ mrp/wizard/disassemble_qty_view.xml 2014-05-20 10:51:47 +0000
905@@ -0,0 +1,37 @@
906+<?xml version="1.0" encoding="utf-8"?>
907+<openerp>
908+ <data>
909+
910+ <!-- Change Product Quantity for Disassemble Order-->
911+
912+ <record id="view_change_disassemble_qty_wizard" model="ir.ui.view">
913+ <field name="name">Change Disassemble Order Qty</field>
914+ <field name="model">change.disassemble.qty</field>
915+ <field name="arch" type="xml">
916+ <form string="Change Disassemble Order Quantity in Negative" version="7.0">
917+ <newline/>
918+ <group col="7">
919+ <field name="product_qty" />
920+ <label string="Use negative quantity for disassemble order" colspan="5" class="oe_left oe_grey" style="margin-top: 5px;"/>
921+ </group>
922+ <footer>
923+ <button name="change_disassemble_qty" string="Create"
924+ colspan="1" type="object" class="oe_highlight" />
925+ or
926+ <button string="Cancel" class="oe_link" special="cancel" />
927+ </footer>
928+ </form>
929+ </field>
930+ </record>
931+
932+ <record id="action_change_disassemble_qty" model="ir.actions.act_window">
933+ <field name="name">Select Quantity to Disassemble</field>
934+ <field name="type">ir.actions.act_window</field>
935+ <field name="res_model">change.disassemble.qty</field>
936+ <field name="view_type">form</field>
937+ <field name="view_mode">form</field>
938+ <field name="target">new</field>
939+ </record>
940+
941+ </data>
942+</openerp>
943
944=== modified file 'mrp/wizard/mrp_product_produce.py'
945--- mrp/wizard/mrp_product_produce.py 2014-05-07 16:59:51 +0000
946+++ mrp/wizard/mrp_product_produce.py 2014-05-20 10:51:47 +0000
947@@ -20,7 +20,9 @@
948 ##############################################################################
949
950 from openerp.osv import fields, osv
951+from lxml import etree
952 import openerp.addons.decimal_precision as dp
953+from openerp.tools.translate import _
954
955
956 class mrp_product_produce_line(osv.osv_memory):
957@@ -52,6 +54,7 @@
958 'track_production': fields.boolean('Track production'),
959 }
960
961+<<<<<<< TREE
962 def on_change_qty(self, cr, uid, ids, product_qty, consume_lines, context=None):
963 """
964 When changing the quantity of products to be produced it will
965@@ -72,6 +75,20 @@
966 return {'value': {'consume_lines': new_consume_lines}}
967
968
969+=======
970+ def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
971+ if context is None:
972+ context = {}
973+ res = super(mrp_product_produce,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
974+ doc = etree.XML(res['arch'])
975+ mrp_production = self.pool.get('mrp.production').browse(cr, uid, context.get('active_id'), context=context)
976+ if view_type == 'form' and mrp_production.disassemble == True:
977+ for node in doc.xpath("//group[@name='produce']"):
978+ node.set('string',_('Product to Disassemble'))
979+ res['arch'] = etree.tostring(doc)
980+ return res
981+
982+>>>>>>> MERGE-SOURCE
983 def _get_product_qty(self, cr, uid, context=None):
984 """ To obtain product quantity
985 @param self: The object pointer.
986@@ -89,7 +106,7 @@
987 if move.product_id == prod.product_id:
988 if not move.scrapped:
989 done += move.product_qty
990- return (prod.product_qty - done) or prod.product_qty
991+ return (abs(prod.product_qty) - done) or abs(prod.product_qty)
992
993 def _get_product_id(self, cr, uid, context=None):
994 """ To obtain product id
995
996=== modified file 'mrp/wizard/mrp_product_produce_view.xml'
997--- mrp/wizard/mrp_product_produce_view.xml 2014-02-05 10:43:24 +0000
998+++ mrp/wizard/mrp_product_produce_view.xml 2014-05-20 10:51:47 +0000
999@@ -1,17 +1,17 @@
1000 <?xml version="1.0" encoding="utf-8"?>
1001 <openerp>
1002 <data>
1003-
1004-
1005+
1006 <!-- Produce -->
1007-
1008+
1009 <record id="view_mrp_product_produce_wizard" model="ir.ui.view">
1010 <field name="name">MRP Product Produce</field>
1011 <field name="model">mrp.product.produce</field>
1012 <field name="arch" type="xml">
1013 <form string="Produce" version="7.0">
1014- <group string="Produce">
1015+ <group name="produce" string="Produce">
1016 <field name="mode"/>
1017+<<<<<<< TREE
1018 <field name="product_qty" colspan="2" on_change="on_change_qty(product_qty, consume_lines, context)"/>
1019 <field name="product_id" invisible="1"/>
1020 <field name="track_production" invisible="1"/>
1021@@ -31,6 +31,10 @@
1022 </tree>
1023 </field>
1024 </group>
1025+=======
1026+ <field name="product_qty" colspan="2"/>
1027+ </group>
1028+>>>>>>> MERGE-SOURCE
1029 <footer>
1030 <button name="do_produce" type="object" string="Confirm" class="oe_highlight"/>
1031 or
1032@@ -47,8 +51,16 @@
1033 <field name="view_type">form</field>
1034 <field name="view_mode">form</field>
1035 <field name="target">new</field>
1036- </record>
1037-
1038-
1039+ </record>
1040+
1041+ <record id="act_mrp_disassemble_product" model="ir.actions.act_window">
1042+ <field name="name">Disassemble</field>
1043+ <field name="type">ir.actions.act_window</field>
1044+ <field name="res_model">mrp.product.produce</field>
1045+ <field name="view_type">form</field>
1046+ <field name="view_mode">form</field>
1047+ <field name="target">new</field>
1048+ </record>
1049+
1050 </data>
1051-</openerp>
1052+</openerp>
1053
1054=== modified file 'mrp_byproduct/mrp_byproduct.py'
1055--- mrp_byproduct/mrp_byproduct.py 2014-04-22 12:16:25 +0000
1056+++ mrp_byproduct/mrp_byproduct.py 2014-05-20 10:51:47 +0000
1057@@ -102,6 +102,8 @@
1058 qty1 *= product_uom_factor / (production.bom_id.product_qty or 1.0)
1059 if production.product_uos_qty:
1060 qty2 *= product_uos_factor / (production.bom_id.product_uos_qty or 1.0)
1061+ if production.disassemble:
1062+ continue
1063 data = {
1064 'name': 'PROD:'+production.name,
1065 'date': production.date_planned,
1066@@ -110,8 +112,8 @@
1067 'product_uom': sub_product.product_uom.id,
1068 'product_uos_qty': qty2,
1069 'product_uos': production.product_uos and production.product_uos.id or False,
1070- 'location_id': source,
1071- 'location_dest_id': production.location_dest_id.id,
1072+ 'location_id': source if not production.disassemble else production.location_dest_id.id,
1073+ 'location_dest_id': production.location_dest_id.id if not production.disassemble else source,
1074 'move_dest_id': production.move_prod_id.id,
1075 'state': 'waiting',
1076 'production_id': production.id
1077
1078=== modified file 'mrp_operations/mrp_operations.py'
1079--- mrp_operations/mrp_operations.py 2014-04-23 09:48:07 +0000
1080+++ mrp_operations/mrp_operations.py 2014-05-20 10:51:47 +0000
1081@@ -139,7 +139,8 @@
1082 if flag:
1083 for production in prod_obj_pool.browse(cr, uid, [prod_obj.id], context= None):
1084 if production.move_lines or production.move_created_ids:
1085- prod_obj_pool.action_produce(cr,uid, production.id, production.product_qty, 'consume_produce', context = None)
1086+ product_qty = production.disassemble and abs(production.product_qty) or production.product_qty
1087+ prod_obj_pool.action_produce(cr,uid, production.id, product_qty, 'consume_produce', context = None)
1088 prod_obj_pool.signal_button_produce_done(cr, uid, [oper_obj.production_id.id])
1089 return
1090

Subscribers

People subscribed via source and target branches

to all changes: