Merge lp:~openerp-dev/openobject-addons/trunk-mo-disassemble-jpr into lp:openobject-addons
- trunk-mo-disassemble-jpr
- Merge into trunk
Status: | Needs review |
---|---|
Proposed branch: | lp:~openerp-dev/openobject-addons/trunk-mo-disassemble-jpr |
Merge into: | lp:openobject-addons |
Diff against target: |
800 lines (+387/-41) 15 files modified
mrp/__openerp__.py (+2/-1) mrp/mrp.py (+70/-12) mrp/mrp_demo.xml (+1/-0) mrp/mrp_view.xml (+46/-18) mrp/procurement.py (+1/-0) mrp/static/src/js/mrp.js (+38/-0) mrp/test/disassemble_process.yml (+96/-0) mrp/wizard/__init__.py (+1/-0) mrp/wizard/change_production_qty.py (+1/-1) mrp/wizard/disassemble_qty.py (+63/-0) mrp/wizard/disassemble_qty_view.xml (+36/-0) mrp/wizard/mrp_product_produce.py (+14/-2) mrp/wizard/mrp_product_produce_view.xml (+12/-4) mrp_byproduct/mrp_byproduct.py (+4/-2) mrp_operations/mrp_operations.py (+2/-1) |
To merge this branch: | bzr merge lp:~openerp-dev/openobject-addons/trunk-mo-disassemble-jpr |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
OpenERP Core Team | Pending | ||
Review via email: mp+219795@code.launchpad.net |
Commit message
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://
Thanks
- 9477. By Jitendra Prajapati(OpenERP)
-
[IMP]update module description.
create mo with positve and negative qty only.
Try to duplicate MO coping origin but not work for disassembling.
Change the License years as per current License policies.
Unmerged revisions
- 9477. By Jitendra Prajapati(OpenERP)
-
[IMP]update module description.
create mo with positve and negative qty only.
Try to duplicate MO coping origin but not work for disassembling.
Change the License years as per current License policies. - 9476. By Jitendra Prajapati(OpenERP)
-
[FIX/IMP]except disassamble coming from procurement.
- 9475. By Jitendra Prajapati(OpenERP)
-
[IMP]update wizard label,when disassamble time.
change the source and destination location of mrp_production when product_qty field < 0 - 9474. By Jitendra Prajapati(OpenERP)
-
[IMP]improve code
- 9473. By Jitendra Prajapati(OpenERP)
-
[ADD]add a new yml file and create disassemble process test case
- 9472. By Jitendra Prajapati(OpenERP)
-
[IMP]apply widget on product_qty field. change the label in mrp form view, when product disassemble time.
- 9471. By Jitendra Prajapati(OpenERP)
-
[MERGE]sync with lp:openobject-addons
- 9470. By RGA(OpenERP)
-
[IMP] add widget update_label that change label of page and separator on change value of quantity field
- 9469. By Jitendra Prajapati(OpenERP)
-
[FIX]runbot test
- 9468. By Jitendra Prajapati(OpenERP)
-
[IMP] mrp: filter 'Manufacturing'
/'Disassemble' in search view.
Preview Diff
1 | === modified file 'mrp/__openerp__.py' |
2 | --- mrp/__openerp__.py 2014-05-07 17:12:05 +0000 |
3 | +++ mrp/__openerp__.py 2014-05-23 07:38:59 +0000 |
4 | @@ -34,7 +34,7 @@ |
5 | Manage the Manufacturing process in OpenERP |
6 | =========================================== |
7 | |
8 | -The manufacturing module allows you to cover planning, ordering, stocks and the manufacturing or assembly of products from raw materials and components. It handles the consumption and production of products according to a bill of materials and the necessary operations on machinery, tools or human resources according to routings. |
9 | +The manufacturing module allows you to cover planning, ordering, stocks and the manufacturing or assembly and disassembling of products from raw materials and components. It handles the consumption and production of products according to a bill of materials and the necessary operations on machinery, tools or human resources according to routings. |
10 | |
11 | It supports complete integration and planification of stockable goods, consumables or services. Services are completely integrated with the rest of the software. For instance, you can set up a sub-contracting service in a bill of materials to automatically purchase on order the assembly of your production. |
12 | |
13 | @@ -63,6 +63,7 @@ |
14 | 'wizard/mrp_price_view.xml', |
15 | 'wizard/mrp_workcenter_load_view.xml', |
16 | 'wizard/stock_move_view.xml', |
17 | + 'wizard/disassemble_qty_view.xml', |
18 | 'mrp_view.xml', |
19 | 'mrp_report.xml', |
20 | 'company_view.xml', |
21 | |
22 | === modified file 'mrp/mrp.py' |
23 | --- mrp/mrp.py 2014-05-08 11:59:17 +0000 |
24 | +++ mrp/mrp.py 2014-05-23 07:38:59 +0000 |
25 | @@ -96,6 +96,12 @@ |
26 | value = {'costs_hour': cost.standard_price} |
27 | return {'value': value} |
28 | |
29 | +class stock_move(osv.osv): |
30 | + _inherit = 'stock.move' |
31 | + _columns = { |
32 | + 'disassemble': fields.boolean('Disassemble'), |
33 | + } |
34 | + |
35 | class mrp_routing(osv.osv): |
36 | """ |
37 | For specifying the routings of Work Centers. |
38 | @@ -426,14 +432,19 @@ |
39 | """ Return product quantity percentage """ |
40 | result = dict.fromkeys(ids, 100) |
41 | for mrp_production in self.browse(cr, uid, ids, context=context): |
42 | - if mrp_production.product_qty: |
43 | + if abs(mrp_production.product_qty): |
44 | done = 0.0 |
45 | for move in mrp_production.move_created_ids2: |
46 | if not move.scrapped and move.product_id == mrp_production.product_id: |
47 | done += move.product_qty |
48 | - result[mrp_production.id] = done / mrp_production.product_qty * 100 |
49 | + result[mrp_production.id] = done / abs(mrp_production.product_qty) * 100 |
50 | return result |
51 | |
52 | + def create(self, cr, uid, values, context=None): |
53 | + if values['product_qty'] < 0: |
54 | + self._description = _('Disassemble Order') |
55 | + return super(mrp_production, self).create(cr, uid, values, context=context) |
56 | + |
57 | def _moves_assigned(self, cr, uid, ids, name, arg, context=None): |
58 | """ Test whether all the consume lines are assigned """ |
59 | res = {} |
60 | @@ -505,6 +516,9 @@ |
61 | When the production is over, the status is set to 'Done'."), |
62 | 'hour_total': fields.function(_production_calc, type='float', string='Total Hours', multi='workorder', store=True), |
63 | 'cycle_total': fields.function(_production_calc, type='float', string='Total Cycles', multi='workorder', store=True), |
64 | + 'disassemble': fields.boolean('Disassemble'), |
65 | + 'disassemble_doc': fields.char('Disassemble Document(s)', size=64, readonly=True, help="Reference of disassembled document(s) for this Manufacturing Order."), |
66 | + 'qty_to_disassemble': fields.float('Remaining Quantity to Disassemble', help="Available product quantity to disassemble", digits_compute=dp.get_precision('Product Unit of Measure')), |
67 | 'user_id': fields.many2one('res.users', 'Responsible'), |
68 | 'company_id': fields.many2one('res.company', 'Company', required=True), |
69 | 'ready_production': fields.function(_moves_assigned, type='boolean', store={'stock.move': (_mrp_from_move, ['state'], 10)}), |
70 | @@ -519,7 +533,9 @@ |
71 | 'name': lambda x, y, z, c: x.pool.get('ir.sequence').get(y, z, 'mrp.production') or '/', |
72 | 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.production', context=c), |
73 | 'location_src_id': _src_id_default, |
74 | - 'location_dest_id': _dest_id_default |
75 | + 'location_dest_id': _dest_id_default, |
76 | + 'disassemble': False, |
77 | + 'disassemble_doc': False |
78 | } |
79 | |
80 | _sql_constraints = [ |
81 | @@ -530,12 +546,12 @@ |
82 | |
83 | def _check_qty(self, cr, uid, ids, context=None): |
84 | for order in self.browse(cr, uid, ids, context=context): |
85 | - if order.product_qty <= 0: |
86 | + if order.product_qty == 0: |
87 | return False |
88 | return True |
89 | |
90 | _constraints = [ |
91 | - (_check_qty, 'Order quantity cannot be negative or zero!', ['product_qty']), |
92 | + (_check_qty, 'Order quantity cannot be zero!', ['product_qty']), |
93 | ] |
94 | |
95 | def unlink(self, cr, uid, ids, context=None): |
96 | @@ -547,6 +563,12 @@ |
97 | def copy(self, cr, uid, id, default=None, context=None): |
98 | if default is None: |
99 | default = {} |
100 | + mo = self.browse(cr, uid, id, context=context) |
101 | + origin = False |
102 | + if default.get('disassemble'): |
103 | + origin = mo.origin + '-' + mo.name if not mo.disassemble and mo.origin else mo.name |
104 | + if not mo.disassemble: |
105 | + origin = mo.origin |
106 | default.update({ |
107 | 'name': self.pool.get('ir.sequence').get(cr, uid, 'mrp.production'), |
108 | 'move_lines': [], |
109 | @@ -555,6 +577,7 @@ |
110 | 'move_created_ids2': [], |
111 | 'product_lines': [], |
112 | 'move_prod_id': False, |
113 | + 'origin': origin, |
114 | }) |
115 | return super(mrp_production, self).copy(cr, uid, id, default, context) |
116 | |
117 | @@ -570,6 +593,11 @@ |
118 | return {'value': {'location_dest_id': src}} |
119 | return {} |
120 | |
121 | + def onchange_product_qty(self, cr, uid, ids, quantity, context=None): |
122 | + if quantity < 0: |
123 | + return {'value': {'disassemble': True, 'qty_to_disassemble': 0.0}} |
124 | + return {'value': {'disassemble': False, 'qty_to_disassemble': quantity}} |
125 | + |
126 | def product_id_change(self, cr, uid, ids, product_id, product_qty=0, context=None): |
127 | """ Finds UoM of changed product. |
128 | @param product_id: Id of changed product. |
129 | @@ -645,7 +673,7 @@ |
130 | raise osv.except_osv(_('Error!'), _("Cannot find a bill of material for this product.")) |
131 | |
132 | # get components and workcenter_lines from BoM structure |
133 | - factor = uom_obj._compute_qty(cr, uid, production.product_uom.id, production.product_qty, bom_point.product_uom.id) |
134 | + factor = uom_obj._compute_qty(cr, uid, production.product_uom.id, abs(production.product_qty), bom_point.product_uom.id) |
135 | res = bom_obj._bom_explode(cr, uid, bom_point, factor / bom_point.product_qty, properties, routing_id=production.routing_id.id) |
136 | results = res[0] # product_lines |
137 | results2 = res[1] # workcenter_lines |
138 | @@ -667,6 +695,34 @@ |
139 | """ |
140 | return len(self._action_compute_lines(cr, uid, ids, properties=properties, context=context)) |
141 | |
142 | + def action_disassemble(self, cr, uid, id, qty, context=None): |
143 | + """ Disassemble the production order. |
144 | + """ |
145 | + mo = self.browse(cr, uid, id, context=context) |
146 | + values = { |
147 | + 'disassemble': True, |
148 | + 'origin': mo.name, |
149 | + 'routing_id': False, |
150 | + 'product_qty': qty, |
151 | + 'qty_to_disassemble': 0, |
152 | + } |
153 | + mo_id = self.copy(cr, uid, mo.id, values, context=context) |
154 | + if mo_id: |
155 | + source_doc = self.read(cr, uid, mo_id, ['name'], context=context) |
156 | + mo.write({'disassemble_doc': mo.disassemble_doc and mo.disassemble_doc + ", " + source_doc['name'] or source_doc['name']}, context=context) |
157 | + view_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mrp', 'mrp_production_form_view') |
158 | + view_id = view_ref and view_ref[1] or False, |
159 | + return { |
160 | + 'type': 'ir.actions.act_window', |
161 | + 'name': _('Disassemble Manufacturing Order'), |
162 | + 'res_model': 'mrp.production', |
163 | + 'res_id': mo_id, |
164 | + 'view_type': 'form', |
165 | + 'view_mode': 'form', |
166 | + 'view_id': False, |
167 | + 'target': 'current', |
168 | + } |
169 | + |
170 | def action_cancel(self, cr, uid, ids, context=None): |
171 | """ Cancels the production order and related stock moves. |
172 | @return: True |
173 | @@ -788,7 +844,7 @@ |
174 | dicts[scheduled.product_id.id] = {} |
175 | |
176 | # total qty of consumed product we need after this consumption |
177 | - total_consume = ((product_qty + produced_qty) * scheduled.product_qty / production.product_qty) |
178 | + total_consume = ((product_qty + produced_qty) * scheduled.product_qty / abs(production.product_qty)) |
179 | qty = total_consume - consumed_qty |
180 | |
181 | # Search for quants related to this related move |
182 | @@ -861,7 +917,7 @@ |
183 | for produce_product in production.move_created_ids: |
184 | produced_qty = produced_products.get(produce_product.product_id.id, 0) |
185 | subproduct_factor = self._get_subproduct_factor(cr, uid, production.id, produce_product.id, context=context) |
186 | - rest_qty = (subproduct_factor * production.product_qty) - produced_qty |
187 | + rest_qty = (subproduct_factor * abs(production.product_qty)) - produced_qty |
188 | if float_compare(rest_qty, (subproduct_factor * production_qty), precision_rounding=produce_product.product_id.uom_id.rounding) < 0: |
189 | prod_name = produce_product.product_id.name_get()[0][1] |
190 | 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)) |
191 | @@ -977,15 +1033,16 @@ |
192 | 'date': production.date_planned, |
193 | 'product_id': production.product_id.id, |
194 | 'product_uom': production.product_uom.id, |
195 | - 'product_uom_qty': production.product_qty, |
196 | + 'product_uom_qty': abs(production.product_qty), |
197 | 'product_uos_qty': production.product_uos and production.product_uos_qty or False, |
198 | 'product_uos': production.product_uos and production.product_uos.id or False, |
199 | - 'location_id': source_location_id, |
200 | - 'location_dest_id': destination_location_id, |
201 | + 'location_id': production.disassemble and destination_location_id or source_location_id, |
202 | + 'location_dest_id': production.disassemble and source_location_id or destination_location_id, |
203 | 'move_dest_id': production.move_prod_id.id, |
204 | 'company_id': production.company_id.id, |
205 | 'production_id': production.id, |
206 | 'origin': production.name, |
207 | + 'state': production.disassemble and 'assigned' or 'waiting', # for products to produce |
208 | } |
209 | move_id = stock_move.create(cr, uid, data, context=context) |
210 | #a phantom bom cannot be used in mrp order so it's ok to assume the list returned by action_confirm |
211 | @@ -1020,7 +1077,7 @@ |
212 | 'name': production.name, |
213 | 'date': production.date_planned, |
214 | 'product_id': product.id, |
215 | - 'product_uom_qty': qty, |
216 | + 'product_uom_qty': abs(qty), |
217 | 'product_uom': uom_id, |
218 | 'product_uos_qty': uos_id and uos_qty or False, |
219 | 'product_uos': uos_id or False, |
220 | @@ -1032,6 +1089,7 @@ |
221 | #this saves us a browse in create() |
222 | 'price_unit': product.standard_price, |
223 | 'origin': production.name, |
224 | + 'disassemble': production.disassemble, |
225 | }) |
226 | return move_id |
227 | |
228 | |
229 | === modified file 'mrp/mrp_demo.xml' |
230 | --- mrp/mrp_demo.xml 2014-01-20 14:42:43 +0000 |
231 | +++ mrp/mrp_demo.xml 2014-05-23 07:38:59 +0000 |
232 | @@ -661,6 +661,7 @@ |
233 | <record id="mrp_production_2" model="mrp.production"> |
234 | <field name="product_id" ref="product.product_product_27"/> |
235 | <field name="product_uom" ref="product.product_uom_unit"/> |
236 | + <field name="product_qty">5</field> |
237 | <field name="location_src_id" ref="stock.stock_location_stock"/> |
238 | <field name="location_dest_id" ref="stock.stock_location_output"/> |
239 | <field name="bom_id" ref="mrp.mrp_bom_7"/> |
240 | |
241 | === modified file 'mrp/mrp_view.xml' |
242 | --- mrp/mrp_view.xml 2014-05-07 18:29:17 +0000 |
243 | +++ mrp/mrp_view.xml 2014-05-23 07:38:59 +0000 |
244 | @@ -665,38 +665,54 @@ |
245 | </field> |
246 | </record> |
247 | |
248 | + <template id="mrp_backend" name="mrp assets" inherit_id="web.assets_backend"> |
249 | + <xpath expr="." position="inside"> |
250 | + <script type="text/javascript" src="/mrp/static/src/js/mrp.js"></script> |
251 | + </xpath> |
252 | + </template> |
253 | + |
254 | <record id="mrp_production_form_view" model="ir.ui.view"> |
255 | <field name="name">mrp.production.form</field> |
256 | <field name="model">mrp.production</field> |
257 | <field name="arch" type="xml"> |
258 | <form string="Manufacturing Orders" version="7.0"> |
259 | <header> |
260 | - <button name="button_confirm" states="draft" string="Confirm Production" class="oe_highlight"/> |
261 | - <button name="%(act_mrp_product_produce)d" states="ready,in_production" string="Produce" type="action" class="oe_highlight"/> |
262 | - <button name="action_assign" states="confirmed,picking_except" string="Check Availability" type="object" class="oe_highlight"/> |
263 | - <button name="force_production" states="confirmed" string="Force Reservation" type="object"/> |
264 | + <button name="button_confirm" string="Confirm Production" class="oe_highlight" attrs="{'invisible':['|',('disassemble', '=', True),('state','!=','draft')]}"/> |
265 | + <button name="button_confirm" string="Confirm Dismantling" class="oe_highlight" attrs="{'invisible':['|',('disassemble', '=', False),('state','!=','draft')]}"/> |
266 | + <button name="%(act_mrp_product_produce)d" string="Produce" type="action" class="oe_highlight" attrs="{'invisible':['|',('disassemble', '=', True),('state','not in',('ready', 'in_production'))]}"/> |
267 | + <button name="%(act_mrp_disassemble_product)d" string="Disassemble" type="action" class="oe_highlight" attrs="{'invisible':['|',('disassemble', '=', False),('state','not in',('ready', 'in_production'))]}"/> |
268 | + <button name="action_assign" string="Check Availability" type="object" class="oe_highlight" attrs="{'invisible':['|',('disassemble', '=', True),('state','not in',('confirmed','picking_except'))]}"/> |
269 | + <button name="force_production" states="confirmed" string="Force Reservation" type="object" class="oe_highlight"/> |
270 | <button name="button_produce" states="ready" string="Mark as Started"/> |
271 | - <button name="button_cancel" states="draft,ready,in_production" string="Cancel Production"/> |
272 | - <button name="action_cancel" type="object" states="confirmed" string="Cancel Production"/> |
273 | + <button name="button_recreate" states="picking_except" string="Recreate Picking"/> |
274 | + <button name="button_cancel" string="Cancel Production" attrs="{'invisible':['|',('disassemble', '=', True),('state','not in',('draft','ready','in_production','picking_except'))]}"/> |
275 | + <button name="button_cancel" string="Cancel Dismantling" attrs="{'invisible':['|',('disassemble', '=', False),('state','not in',('draft','ready','in_production','picking_except'))]}"/> |
276 | + <button name="action_cancel" type="object" string="Cancel Production" attrs="{'invisible':['|',('disassemble', '=', True),('state','!=','confirmed')]}"/> |
277 | + <button name="action_cancel" type="object" string="Cancel Dismantling" attrs="{'invisible':['|',('disassemble', '=', False),('state','!=','confirmed')]}"/> |
278 | + <button name="%(action_change_disassemble_qty)d" type="action" string="Disassemble" attrs="{'invisible':['|','|', ('state','!=','done'), ('qty_to_disassemble','=',0)]}"/> |
279 | <field name="state" widget="statusbar" statusbar_visible="draft,ready,in_production,done" statusbar_colors='{"confirmed":"blue"}'/> |
280 | </header> |
281 | <sheet> |
282 | <div class="oe_title"> |
283 | - <h1>Manufacturing Order <field name="name" class="oe_inline"/></h1> |
284 | + <h1> |
285 | + <label string="Disassemble" attrs="{'invisible':[('disassemble','=',False)]}" class="oe_inline"/>Manufacturing Order |
286 | + <field name="name" class="oe_inline"/> |
287 | + </h1> |
288 | </div> |
289 | <group> |
290 | <group> |
291 | <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"}'/> |
292 | + <field name="qty_to_disassemble" invisible="1"/> |
293 | <label for="product_qty"/> |
294 | <div> |
295 | - <field name="product_qty" class="oe_inline" on_change="product_id_change(product_id, product_qty)"/> |
296 | + <field name="product_qty" widget="mrp_product_qty" class="oe_inline" on_change="onchange_product_qty(product_qty)"/> |
297 | <field name="product_uom" groups="product.group_uom" class="oe_inline"/> |
298 | <button type="action" |
299 | name="%(mrp.action_change_production_qty)d" |
300 | string="Update" states="confirmed" class="oe_edit_only oe_link"/> |
301 | </div> |
302 | - <label for="product_uos_qty" groups="product.group_uos"/> |
303 | - <div groups="product.group_uos"> |
304 | + <label for="product_uos_qty" attrs="{'invisible':[('disassemble', '=', True)]}" groups="product.group_uos"/> |
305 | + <div attrs="{'invisible':[('disassemble', '=', True)]}" groups="product.group_uos"> |
306 | <field name="product_uos_qty" class="oe_inline"/> |
307 | <label string="-" attrs="{'invisible':[('product_uos','=',False)]}" class="oe_inline"/> |
308 | <field name="product_uos" class="oe_inline"/> |
309 | @@ -715,15 +731,16 @@ |
310 | </group> |
311 | </group> |
312 | <notebook> |
313 | - <page string="Consumed Products"> |
314 | + <page string="Consumed Products" name="consumed_products"> |
315 | <group> |
316 | - <group string="Products to Consume"> |
317 | + <group string="Products to Consume" name="group_to_consume"> |
318 | <field name="move_lines" nolabel="1" options="{'reload_on_button': true}"> |
319 | <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"> |
320 | <field name="product_id"/> |
321 | <field name="product_qty" string="Quantity"/> |
322 | <field name="product_uom" string="Unit of Measure" groups="product.group_uom"/> |
323 | <field name="state" invisible="1"/> |
324 | + <field name="disassemble" invisible="1"/> |
325 | <button name="%(mrp.move_consume)d" |
326 | string="Consume Products" type="action" |
327 | icon="gtk-go-forward" context="{'consume': True}" |
328 | @@ -732,11 +749,11 @@ |
329 | <button name="%(stock.move_scrap)d" |
330 | string="Scrap Products" type="action" |
331 | icon="terp-gtk-jump-to-ltr" context="{'scrap': True}" |
332 | - states="draft,waiting,confirmed,assigned"/> |
333 | + attrs="{'invisible':['|',('disassemble', '=', True),('state', '=', 'cancel')]}"/> |
334 | </tree> |
335 | </field> |
336 | </group> |
337 | - <group string="Consumed Products"> |
338 | + <group string="Consumed Products" name="group_consumed"> |
339 | <field name="move_lines2" nolabel="1" options="{'reload_on_button': true}"> |
340 | <tree colors="red:scrapped==True;blue:state == 'draft';black:state in ('confirmed','ready','in_production');gray:state == 'cancel' " string="Consumed Products" editable="bottom"> |
341 | <field name="product_id" readonly="1"/> |
342 | @@ -745,12 +762,17 @@ |
343 | <field name="product_uom" readonly="1" string="Unit of Measure" groups="product.group_uom"/> |
344 | <field name="state" invisible="1"/> |
345 | <field name="scrapped" invisible="1"/> |
346 | + <field name="disassemble" invisible="1"/> |
347 | + <button name="%(stock.move_scrap)d" |
348 | + string="Scrap Products" type="action" |
349 | + icon="terp-gtk-jump-to-ltr" context="{'scrap': True}" |
350 | + attrs="{'invisible':['|',('disassemble', '=', False),('state', '=', 'cancel')]}"/> |
351 | </tree> |
352 | </field> |
353 | </group> |
354 | </group> |
355 | </page> |
356 | - <page string="Finished Products"> |
357 | + <page string="Finished Products" attrs="{'invisible':[('disassemble', '=', True)]}"> |
358 | <group> |
359 | <group string="Products to Produce"> |
360 | <field name="move_created_ids" nolabel="1" options="{'reload_on_button': true}"> |
361 | @@ -804,7 +826,7 @@ |
362 | </tree> |
363 | </field> |
364 | </page> |
365 | - <page string="Scheduled Products"> |
366 | + <page string="Scheduled Products" attrs="{'invisible':[('disassemble', '=', True)]}"> |
367 | <button name="action_compute" states="draft" |
368 | string="Compute Data" type="object" |
369 | icon="terp-stock_format-scientific"/> |
370 | @@ -816,10 +838,12 @@ |
371 | <field name="priority"/> |
372 | <field name="date_start" invisible="1"/> |
373 | <field name="date_finished" invisible="1"/> |
374 | + <field name="disassemble" invisible="1"/> |
375 | + <field name="disassemble_doc" attrs="{'invisible':[('disassemble_doc', '=', False)]}"/> |
376 | </group> |
377 | <group> |
378 | - <field name="company_id" groups="base.group_multi_company" widget="selection" /> |
379 | - <field name="move_prod_id" groups="stock.group_locations"/> |
380 | + <field name="company_id" groups="base.group_multi_company" widget="selection"/> |
381 | + <field name="move_prod_id" groups="stock.group_locations" attrs="{'invisible':[('disassemble', '=', True)]}"/> |
382 | </group> |
383 | </group> |
384 | </page> |
385 | @@ -845,6 +869,10 @@ |
386 | help="Manufacturing Orders which are ready to start production."/> |
387 | <filter icon="terp-check" string="In Production" name="inprogress" domain="[('state','=','in_production')]" |
388 | help="Manufacturing Orders which are currently in production."/> |
389 | + <filter string="Manufacturing" name="manufacturing" domain="[('disassemble', '=', False)]" |
390 | + help="Manufacturing Orders."/> |
391 | + <filter string="Disassemble" name="disassemble" domain="[('product_qty', '<', 0)]" |
392 | + help="Disassemble Orders."/> |
393 | <separator/> |
394 | <filter icon="terp-gnome-cpu-frequency-applet+" string="Late" domain="['&', ('date_planned','<', current_date), ('state', 'in', ('draft', 'confirmed', 'ready'))]" |
395 | help="Production started late"/> |
396 | |
397 | === modified file 'mrp/procurement.py' |
398 | --- mrp/procurement.py 2014-05-07 17:14:31 +0000 |
399 | +++ mrp/procurement.py 2014-05-23 07:38:59 +0000 |
400 | @@ -95,6 +95,7 @@ |
401 | 'origin': procurement.origin, |
402 | 'product_id': procurement.product_id.id, |
403 | 'product_qty': procurement.product_qty, |
404 | + 'qty_to_disassemble': procurement.product_qty, |
405 | 'product_uom': procurement.product_uom.id, |
406 | 'product_uos_qty': procurement.product_uos and procurement.product_uos_qty or False, |
407 | 'product_uos': procurement.product_uos and procurement.product_uos.id or False, |
408 | |
409 | === added directory 'mrp/static/src/js' |
410 | === added file 'mrp/static/src/js/mrp.js' |
411 | --- mrp/static/src/js/mrp.js 1970-01-01 00:00:00 +0000 |
412 | +++ mrp/static/src/js/mrp.js 2014-05-23 07:38:59 +0000 |
413 | @@ -0,0 +1,38 @@ |
414 | +openerp.mrp = function(instance) { |
415 | +var _t = instance.web._t; |
416 | + |
417 | +instance.web.form.mrp_product_qty = instance.web.form.FieldFloat.extend({ |
418 | + init: function(field_manager, node) { |
419 | + var self = this; |
420 | + this._super(field_manager, node); |
421 | + this.on("change:value", this, function() { |
422 | + self.update_label(this.get('value')); |
423 | + }); |
424 | + var $page = this.view.$el.find("a[name='consumed_products']"); |
425 | + var $label_product = this.view.$el.find("div[name='group_to_consume']"); |
426 | + var $label_consumed = this.view.$el.find("div[name='group_consumed']"); |
427 | + this.label_list = [$page, $label_product, $label_consumed]; |
428 | + this.label_value = [[_t("Consumed Products"),_t("Disassembled Products")], |
429 | + [_t("Products to Consume"),_t("Products to Disassemble")], |
430 | + [_t("Consumed Products"),_t("Disassembled Products")]]; |
431 | + }, |
432 | + update_label:function(value){ |
433 | + var self = this |
434 | + _.map(self.label_list, function(ele, i){ |
435 | + value >= 0 ? ele.text(self.label_value[i][0]) : ele.text(self.label_value[i][1]) |
436 | + }); |
437 | + }, |
438 | + parse_value: function(val, def) { |
439 | + if (this.widget) this.widget='float' |
440 | + return instance.web.parse_value(val, this, def); |
441 | + }, |
442 | + format_value: function(val, def) { |
443 | + if (this.widget) this.widget='float' |
444 | + return instance.web.format_value(val, this, def); |
445 | + }, |
446 | + |
447 | +}); |
448 | +instance.web.form.widgets = instance.web.form.widgets.extend({ |
449 | +'mrp_product_qty': 'instance.web.form.mrp_product_qty', |
450 | +}); |
451 | +} |
452 | \ No newline at end of file |
453 | |
454 | === added file 'mrp/test/disassemble_process.yml' |
455 | --- mrp/test/disassemble_process.yml 1970-01-01 00:00:00 +0000 |
456 | +++ mrp/test/disassemble_process.yml 2014-05-23 07:38:59 +0000 |
457 | @@ -0,0 +1,96 @@ |
458 | +- |
459 | + MRP user can doing all process related to Production Order, so let's check data with giving the access rights of user. |
460 | +- |
461 | + !context |
462 | + uid: 'res_users_mrp_user' |
463 | +- |
464 | + In order to test Disassemble feature in OpenERP we will create a Production order with negative quantity for PC Assemble SC349 |
465 | +- |
466 | + !record {model: mrp.production, id: mrp_production_test2}: |
467 | + product_id: product.product_product_4 |
468 | + product_qty: -5.0 |
469 | + location_src_id: stock.stock_location_14 |
470 | + location_dest_id: stock.stock_location_output |
471 | + bom_id: mrp_bom_24 |
472 | + routing_id: mrp.mrp_routing_1 |
473 | +- |
474 | + I compute the production order. |
475 | +- |
476 | + !python {model: mrp.production}: | |
477 | + order = self.browse(cr, uid, ref("mrp_production_test2"), context=context) |
478 | + order.action_compute(context=context) |
479 | +- |
480 | + I check production lines after compute. |
481 | +- |
482 | + !python {model: mrp.production}: | |
483 | + order = self.browse(cr, uid, ref("mrp_production_test2"), context=context) |
484 | + assert len(order.product_lines) == 5, "Production lines are not generated proper." |
485 | +- |
486 | + Now I check workcenter lines. |
487 | +- |
488 | + !python {model: mrp.production}: | |
489 | + from openerp.tools import float_compare |
490 | + def assert_equals(value1, value2, msg, float_compare=float_compare): |
491 | + assert float_compare(value1, value2, precision_digits=2) == 0, msg |
492 | + order = self.browse(cr, uid, ref("mrp_production_test2"), context=context) |
493 | + assert len(order.workcenter_lines), "Workcenter lines are not generated proper." |
494 | +- |
495 | + I confirm the Production Order. |
496 | +- |
497 | + !workflow {model: mrp.production, action: button_confirm, ref: mrp_production_test2} |
498 | +- |
499 | + I check details of Produce Move of Production Order to trace Final Product. |
500 | +- |
501 | + !python {model: mrp.production}: | |
502 | + order = self.browse(cr, uid, ref("mrp_production_test2")) |
503 | + assert order.state == 'ready', "Production order should be ready." |
504 | + assert order.move_created_ids, "Trace Record is not created for Final Product." |
505 | + move = order.move_created_ids[0] |
506 | + source_location_id = order.product_id.property_stock_production.id |
507 | + assert move.date == order.date_planned, "Planned date is not correspond." |
508 | + assert move.product_id.id == order.product_id.id, "Product is not correspond." |
509 | + assert move.product_uom.id == order.product_uom.id, "UOM is not correspond." |
510 | + assert move.product_qty == abs(order.product_qty), "Qty is not correspond." |
511 | + assert move.product_uos_qty == order.product_uos and order.product_uos_qty or abs(order.product_qty), "UOS qty is not correspond." |
512 | + if order.product_uos: |
513 | + assert move.product_uos.id == order.product_uos.id, "UOS is not correspond." |
514 | + assert move.location_id.id == order.location_dest_id.id, "Source Location is not correspond." |
515 | + assert move.location_dest_id.id == source_location_id, "Destination Location is not correspond." |
516 | + routing_loc = None |
517 | + if order.bom_id.routing_id and order.bom_id.routing_id.location_id: |
518 | + routing_loc = order.bom_id.routing_id.location_id.id |
519 | + date_planned = order.date_planned |
520 | + for move_line in order.move_lines: |
521 | + for order_line in order.product_lines: |
522 | + if move_line.product_id.type not in ('product', 'consu'): |
523 | + continue |
524 | + if move_line.product_id.id == order_line.product_id.id: |
525 | + assert move_line.date == date_planned, "Planned date is not correspond in 'To consume line'." |
526 | + assert move_line.product_qty == order_line.product_qty, "Qty is not correspond in 'To consume line'." |
527 | + assert move_line.product_uom.id == order_line.product_uom.id, "UOM is not correspond in 'To consume line'." |
528 | + 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'." |
529 | + if order_line.product_uos: |
530 | + assert move_line.product_uos.id == order_line.product_uos.id, "UOS is not correspond in 'To consume line'." |
531 | + assert move_line.location_id.id == source_location_id, "Source location is not correspond in 'To consume line'." |
532 | + assert move_line.location_dest_id.id == routing_loc or order.location_src_id.id, "Destination Location is not correspond in 'To consume line'." |
533 | +- |
534 | + I check that production order in ready state. |
535 | +- |
536 | + !python {model: mrp.production}: | |
537 | + order = self.browse(cr, uid, ref("mrp_production_test2")) |
538 | + assert order.state == 'ready', 'Production order should be in Ready State.' |
539 | + |
540 | +- |
541 | + I check that production order in production state after start production. |
542 | +- |
543 | + !python {model: mrp.production}: | |
544 | + order = self.browse(cr, uid, ref("mrp_production_test2")) |
545 | + context.update({'active_id': ref('mrp_production_test2')}) |
546 | + assert self.action_produce(cr, uid, order.id , 5.0, 'consume_produce'), 'Can not do action produce.' |
547 | + |
548 | +- |
549 | + I check that production order in Done state. |
550 | +- |
551 | + !python {model: mrp.production}: | |
552 | + order = self.browse(cr, uid, ref("mrp_production_test2")) |
553 | + assert order.state == 'done', 'Production order should be in Done State.' |
554 | |
555 | === modified file 'mrp/wizard/__init__.py' |
556 | --- mrp/wizard/__init__.py 2014-01-28 15:18:34 +0000 |
557 | +++ mrp/wizard/__init__.py 2014-05-23 07:38:59 +0000 |
558 | @@ -24,6 +24,7 @@ |
559 | import mrp_workcenter_load |
560 | import change_production_qty |
561 | import stock_move |
562 | +import disassemble_qty |
563 | #import mrp_change_standard_price |
564 | |
565 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
566 | |
567 | === modified file 'mrp/wizard/change_production_qty.py' |
568 | --- mrp/wizard/change_production_qty.py 2013-11-29 16:56:44 +0000 |
569 | +++ mrp/wizard/change_production_qty.py 2014-05-23 07:38:59 +0000 |
570 | @@ -71,7 +71,7 @@ |
571 | move_obj = self.pool.get('stock.move') |
572 | for wiz_qty in self.browse(cr, uid, ids, context=context): |
573 | prod = prod_obj.browse(cr, uid, record_id, context=context) |
574 | - prod_obj.write(cr, uid, [prod.id], {'product_qty': wiz_qty.product_qty}) |
575 | + 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) |
576 | prod_obj.action_compute(cr, uid, [prod.id]) |
577 | |
578 | for move in prod.move_lines: |
579 | |
580 | === added file 'mrp/wizard/disassemble_qty.py' |
581 | --- mrp/wizard/disassemble_qty.py 1970-01-01 00:00:00 +0000 |
582 | +++ mrp/wizard/disassemble_qty.py 2014-05-23 07:38:59 +0000 |
583 | @@ -0,0 +1,63 @@ |
584 | +# -*- coding: utf-8 -*- |
585 | +############################################################################## |
586 | +# |
587 | +# OpenERP, Open Source Management Solution |
588 | +# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). |
589 | +# |
590 | +# This program is free software: you can redistribute it and/or modify |
591 | +# it under the terms of the GNU Affero General Public License as |
592 | +# published by the Free Software Foundation, either version 3 of the |
593 | +# License, or (at your option) any later version. |
594 | +# |
595 | +# This program is distributed in the hope that it will be useful, |
596 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
597 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
598 | +# GNU Affero General Public License for more details. |
599 | +# |
600 | +# You should have received a copy of the GNU Affero General Public License |
601 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
602 | +# |
603 | +############################################################################## |
604 | + |
605 | +from openerp.osv import fields, osv |
606 | +from openerp.tools.translate import _ |
607 | +import openerp.addons.decimal_precision as dp |
608 | + |
609 | +class change_disassemble_qty(osv.osv_memory): |
610 | + _name = 'change.disassemble.qty' |
611 | + _description = 'Change Quantity for Disassemble Products' |
612 | + |
613 | + _columns = { |
614 | + 'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True), |
615 | + } |
616 | + |
617 | + def default_get(self, cr, uid, fields, context=None): |
618 | + """ To get default values for the object. |
619 | + @param self: The object pointer. |
620 | + @param cr: A database cursor |
621 | + @param uid: ID of the user currently logged in |
622 | + @param fields: List of fields for which we want default values |
623 | + @param context: A standard dictionary |
624 | + @return: A dictionary which of fields with values. |
625 | + """ |
626 | + if context is None: |
627 | + context = {} |
628 | + res = super(change_disassemble_qty, self).default_get(cr, uid, fields, context=context) |
629 | + prod = self.pool.get('mrp.production').browse(cr, uid, context.get('active_id'), context=context) |
630 | + if 'product_qty' in fields: |
631 | + res.update({'product_qty': prod.qty_to_disassemble * -1 }) |
632 | + return res |
633 | + |
634 | + def change_disassemble_qty(self, cr, uid, ids, context=None): |
635 | + mrp_production_obj = self.pool.get('mrp.production') |
636 | + mrp_id = context.get('active_id', False) |
637 | + qty = self.browse(cr, uid, ids[0], context=context).product_qty |
638 | + if qty >= 0: |
639 | + raise osv.except_osv(_('Warning!'), _('Quantity must be negative to disassemble.')) |
640 | + mrp_record = mrp_production_obj.browse(cr, uid, mrp_id, context=context) |
641 | + mrp_record.write({'qty_to_disassemble': mrp_record.qty_to_disassemble - abs(qty)}, context=context) |
642 | + if mrp_record.qty_to_disassemble < abs(qty) : |
643 | + 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)) |
644 | + return mrp_production_obj.action_disassemble(cr, uid, mrp_id, qty, context=context) |
645 | + |
646 | +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
647 | \ No newline at end of file |
648 | |
649 | === added file 'mrp/wizard/disassemble_qty_view.xml' |
650 | --- mrp/wizard/disassemble_qty_view.xml 1970-01-01 00:00:00 +0000 |
651 | +++ mrp/wizard/disassemble_qty_view.xml 2014-05-23 07:38:59 +0000 |
652 | @@ -0,0 +1,36 @@ |
653 | +<?xml version="1.0" encoding="utf-8"?> |
654 | +<openerp> |
655 | + <data> |
656 | + |
657 | + <!-- Change Product Quantity for Disassemble Order--> |
658 | + <record id="view_change_disassemble_qty_wizard" model="ir.ui.view"> |
659 | + <field name="name">Change Disassemble Order Qty</field> |
660 | + <field name="model">change.disassemble.qty</field> |
661 | + <field name="arch" type="xml"> |
662 | + <form string="Change Disassemble Order Quantity in Negative" version="7.0"> |
663 | + <newline/> |
664 | + <group col="7"> |
665 | + <field name="product_qty" /> |
666 | + <label string="Use negative quantity for disassemble order" colspan="5" class="oe_left oe_grey" style="margin-top: 5px;"/> |
667 | + </group> |
668 | + <footer> |
669 | + <button name="change_disassemble_qty" string="Create" |
670 | + colspan="1" type="object" class="oe_highlight" /> |
671 | + or |
672 | + <button string="Cancel" class="oe_link" special="cancel" /> |
673 | + </footer> |
674 | + </form> |
675 | + </field> |
676 | + </record> |
677 | + |
678 | + <record id="action_change_disassemble_qty" model="ir.actions.act_window"> |
679 | + <field name="name">Select Quantity to Disassemble</field> |
680 | + <field name="type">ir.actions.act_window</field> |
681 | + <field name="res_model">change.disassemble.qty</field> |
682 | + <field name="view_type">form</field> |
683 | + <field name="view_mode">form</field> |
684 | + <field name="target">new</field> |
685 | + </record> |
686 | + |
687 | + </data> |
688 | +</openerp> |
689 | \ No newline at end of file |
690 | |
691 | === modified file 'mrp/wizard/mrp_product_produce.py' |
692 | --- mrp/wizard/mrp_product_produce.py 2014-05-07 16:59:51 +0000 |
693 | +++ mrp/wizard/mrp_product_produce.py 2014-05-23 07:38:59 +0000 |
694 | @@ -21,7 +21,8 @@ |
695 | |
696 | from openerp.osv import fields, osv |
697 | import openerp.addons.decimal_precision as dp |
698 | - |
699 | +from lxml import etree |
700 | +from openerp.tools.translate import _ |
701 | |
702 | class mrp_product_produce_line(osv.osv_memory): |
703 | _name="mrp.product.produce.line" |
704 | @@ -71,6 +72,17 @@ |
705 | new_consume_lines.append([0, False, consume]) |
706 | return {'value': {'consume_lines': new_consume_lines}} |
707 | |
708 | + def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): |
709 | + if context is None: |
710 | + context = {} |
711 | + 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) |
712 | + doc = etree.XML(res['arch']) |
713 | + mrp_production = self.pool.get('mrp.production').browse(cr, uid, context.get('active_id'), context=context) |
714 | + if view_type == 'form' and mrp_production.disassemble == True: |
715 | + for node in doc.xpath("//group[@name='produce']"): |
716 | + node.set('string',_('Product to Disassemble')) |
717 | + res['arch'] = etree.tostring(doc) |
718 | + return res |
719 | |
720 | def _get_product_qty(self, cr, uid, context=None): |
721 | """ To obtain product quantity |
722 | @@ -89,7 +101,7 @@ |
723 | if move.product_id == prod.product_id: |
724 | if not move.scrapped: |
725 | done += move.product_qty |
726 | - return (prod.product_qty - done) or prod.product_qty |
727 | + return (abs(prod.product_qty) - done) or abs(prod.product_qty) |
728 | |
729 | def _get_product_id(self, cr, uid, context=None): |
730 | """ To obtain product id |
731 | |
732 | === modified file 'mrp/wizard/mrp_product_produce_view.xml' |
733 | --- mrp/wizard/mrp_product_produce_view.xml 2014-02-05 10:43:24 +0000 |
734 | +++ mrp/wizard/mrp_product_produce_view.xml 2014-05-23 07:38:59 +0000 |
735 | @@ -10,7 +10,7 @@ |
736 | <field name="model">mrp.product.produce</field> |
737 | <field name="arch" type="xml"> |
738 | <form string="Produce" version="7.0"> |
739 | - <group string="Produce"> |
740 | + <group name="produce" string="Produce"> |
741 | <field name="mode"/> |
742 | <field name="product_qty" colspan="2" on_change="on_change_qty(product_qty, consume_lines, context)"/> |
743 | <field name="product_id" invisible="1"/> |
744 | @@ -47,8 +47,16 @@ |
745 | <field name="view_type">form</field> |
746 | <field name="view_mode">form</field> |
747 | <field name="target">new</field> |
748 | - </record> |
749 | - |
750 | - |
751 | + </record> |
752 | + |
753 | + <record id="act_mrp_disassemble_product" model="ir.actions.act_window"> |
754 | + <field name="name">Disassemble</field> |
755 | + <field name="type">ir.actions.act_window</field> |
756 | + <field name="res_model">mrp.product.produce</field> |
757 | + <field name="view_type">form</field> |
758 | + <field name="view_mode">form</field> |
759 | + <field name="target">new</field> |
760 | + </record> |
761 | + |
762 | </data> |
763 | </openerp> |
764 | |
765 | === modified file 'mrp_byproduct/mrp_byproduct.py' |
766 | --- mrp_byproduct/mrp_byproduct.py 2014-04-22 12:16:25 +0000 |
767 | +++ mrp_byproduct/mrp_byproduct.py 2014-05-23 07:38:59 +0000 |
768 | @@ -102,6 +102,8 @@ |
769 | qty1 *= product_uom_factor / (production.bom_id.product_qty or 1.0) |
770 | if production.product_uos_qty: |
771 | qty2 *= product_uos_factor / (production.bom_id.product_uos_qty or 1.0) |
772 | + if production.disassemble: |
773 | + continue |
774 | data = { |
775 | 'name': 'PROD:'+production.name, |
776 | 'date': production.date_planned, |
777 | @@ -110,8 +112,8 @@ |
778 | 'product_uom': sub_product.product_uom.id, |
779 | 'product_uos_qty': qty2, |
780 | 'product_uos': production.product_uos and production.product_uos.id or False, |
781 | - 'location_id': source, |
782 | - 'location_dest_id': production.location_dest_id.id, |
783 | + 'location_id': source if not production.disassemble else production.location_dest_id.id, |
784 | + 'location_dest_id': production.location_dest_id.id if not production.disassemble else source, |
785 | 'move_dest_id': production.move_prod_id.id, |
786 | 'state': 'waiting', |
787 | 'production_id': production.id |
788 | |
789 | === modified file 'mrp_operations/mrp_operations.py' |
790 | --- mrp_operations/mrp_operations.py 2014-04-23 09:48:07 +0000 |
791 | +++ mrp_operations/mrp_operations.py 2014-05-23 07:38:59 +0000 |
792 | @@ -139,7 +139,8 @@ |
793 | if flag: |
794 | for production in prod_obj_pool.browse(cr, uid, [prod_obj.id], context= None): |
795 | if production.move_lines or production.move_created_ids: |
796 | - prod_obj_pool.action_produce(cr,uid, production.id, production.product_qty, 'consume_produce', context = None) |
797 | + product_qty = production.disassemble and abs(production.product_qty) or production.product_qty |
798 | + prod_obj_pool.action_produce(cr,uid, production.id, product_qty, 'consume_produce', context = None) |
799 | prod_obj_pool.signal_button_produce_done(cr, uid, [oper_obj.production_id.id]) |
800 | return |
801 |