Merge lp:~openerp-dev/openobject-addons/trunk-mo-disassemble into lp:openobject-addons
- trunk-mo-disassemble
- Merge into trunk
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 |
Related bugs: | |
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
OpenERP Core Team | Pending | ||
Review via email: mp+164835@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,
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', '<', 0)]" |
598 | + help="Disassemble Orders."/> |
599 | <separator/> |
600 | <filter icon="terp-gnome-cpu-frequency-applet+" string="Late" domain="['&', ('date_planned','<', 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 |