[IMP]: (Work in progress)* Changed demo references according to changes in server...
[odoo/odoo.git] / addons / mrp_operations / mrp_operations.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 from osv import fields
23 from osv import osv
24 import netsvc
25 import time
26 from datetime import datetime
27 from tools.translate import _
28
29 #----------------------------------------------------------
30 # Work Centers
31 #----------------------------------------------------------
32 # capacity_hour : capacity per hour. default: 1.0.
33 #          Eg: If 5 concurrent operations at one time: capacity = 5 (because 5 employees)
34 # unit_per_cycle : how many units are produced for one cycle
35
36 class stock_move(osv.osv):
37     _inherit = 'stock.move'
38     _columns = {
39         'move_dest_id_lines': fields.one2many('stock.move','move_dest_id', 'Children Moves')
40     }
41
42     def copy(self, cr, uid, id, default=None, context=None):
43         if default is None:
44             default = {}
45         default.update({
46             'move_dest_id_lines': [],
47         })
48         return super(stock_move, self).copy(cr, uid, id, default, context)
49
50 stock_move()
51
52 class mrp_production_workcenter_line(osv.osv):
53     def _get_date_date(self, cr, uid, ids, field_name, arg, context=None):
54         """ Finds starting date.
55         @return: Dictionary of values.
56         """
57         res={}
58         for op in self.browse(cr, uid, ids, context=context):
59             if op.date_start:
60                 res[op.id] = op.date_start[:10]
61             else:
62                 res[op.id]=False
63         return res
64
65     def _get_date_end(self, cr, uid, ids, field_name, arg, context=None):
66         """ Finds ending date.
67         @return: Dictionary of values.
68         """
69         ops = self.browse(cr, uid, ids, context=context)
70         date_and_hours_by_cal = [(op.date_planned, op.hour, op.workcenter_id.calendar_id.id) for op in ops if op.date_planned]
71
72         intervals = self.pool.get('resource.calendar').interval_get_multi(cr, uid, date_and_hours_by_cal)
73
74         res = {}
75         for op in ops:
76             res[op.id] = False
77             if op.date_planned:
78                 i = intervals.get((op.date_planned, op.hour, op.workcenter_id.calendar_id.id))
79                 if i:
80                     res[op.id] = i[-1][1].strftime('%Y-%m-%d %H:%M:%S')
81                 else:
82                     res[op.id] = op.date_planned
83         return res
84
85     _inherit = 'mrp.production.workcenter.line'
86     _order = "sequence, date_planned"
87
88     _columns = {
89        'state': fields.selection([('draft','Draft'),('cancel','Cancelled'),('pause','Pending'),('startworking', 'In Progress'),('done','Finished')],'Status', readonly=True,
90                                  help="* When a work order is created it is set in 'Draft' state.\n" \
91                                        "* When user sets work order in start mode that time it will be set in 'In Progress' state.\n" \
92                                        "* When work order is in running mode, during that time if user wants to stop or to make changes in order then can set in 'Pending' state.\n" \
93                                        "* When the user cancels the work order it will be set in 'Canceled' state.\n" \
94                                        "* When order is completely processed that time it is set in 'Finished' state."),
95        'date_start_date': fields.function(_get_date_date, string='Start Date', type='date'),
96        'date_planned': fields.datetime('Scheduled Date', select=True),
97        'date_planned_end': fields.function(_get_date_end, string='End Date', type='datetime'),
98        'date_start': fields.datetime('Start Date'),
99        'date_finished': fields.datetime('End Date'),
100        'delay': fields.float('Working Hours',help="The elapsed time between operation start and stop in this Work Center",readonly=True),
101        'production_state':fields.related('production_id','state',
102             type='selection',
103             selection=[('draft','Draft'),('picking_except', 'Picking Exception'),('confirmed','Waiting Goods'),('ready','Ready to Produce'),('in_production','In Production'),('cancel','Canceled'),('done','Done')],
104             string='Production Status', readonly=True),
105        'product':fields.related('production_id','product_id',type='many2one',relation='product.product',string='Product',
106             readonly=True),
107        'qty':fields.related('production_id','product_qty',type='float',string='Qty',readonly=True, store=True),
108        'uom':fields.related('production_id','product_uom',type='many2one',relation='product.uom',string='Unit of Measure',readonly=True),
109     }
110
111     _defaults = {
112         'state': lambda *a: 'draft',
113         'delay': lambda *a: 0.0
114     }
115
116     def modify_production_order_state(self, cr, uid, ids, action):
117         """ Modifies production order state if work order state is changed.
118         @param action: Action to perform.
119         @return: Nothing
120         """
121         wf_service = netsvc.LocalService("workflow")
122         prod_obj_pool = self.pool.get('mrp.production')
123         oper_obj = self.browse(cr, uid, ids)[0]
124         prod_obj = oper_obj.production_id
125         if action == 'start':
126                if prod_obj.state =='confirmed':
127                    prod_obj_pool.force_production(cr, uid, [prod_obj.id])
128                    wf_service.trg_validate(uid, 'mrp.production', prod_obj.id, 'button_produce', cr)
129                elif prod_obj.state =='ready':
130                    wf_service.trg_validate(uid, 'mrp.production', prod_obj.id, 'button_produce', cr)
131                elif prod_obj.state =='in_production':
132                    return
133                else:
134                    raise osv.except_osv(_('Error!'),_('Manufacturing order cannot start in state "%s"!') % (prod_obj.state,))
135         else:
136             oper_ids = self.search(cr,uid,[('production_id','=',prod_obj.id)])
137             obj = self.browse(cr,uid,oper_ids)
138             flag = True
139             for line in obj:
140                 if line.state != 'done':
141                      flag = False
142             if flag:
143                 for production in prod_obj_pool.browse(cr, uid, [prod_obj.id], context= None):
144                     if production.move_lines or production.move_created_ids:
145                         prod_obj_pool.action_produce(cr,uid, production.id, production.product_qty, 'consume_produce', context = None)
146                 wf_service.trg_validate(uid, 'mrp.production', oper_obj.production_id.id, 'button_produce_done', cr)
147         return
148
149     def write(self, cr, uid, ids, vals, context=None, update=True):
150         result = super(mrp_production_workcenter_line, self).write(cr, uid, ids, vals, context=context)
151         prod_obj = self.pool.get('mrp.production')
152         if vals.get('date_planned', False) and update:
153             for prod in self.browse(cr, uid, ids, context=context):
154                 if prod.production_id.workcenter_lines:
155                     dstart = min(vals['date_planned'], prod.production_id.workcenter_lines[0]['date_planned'])
156                     prod_obj.write(cr, uid, [prod.production_id.id], {'date_start':dstart}, context=context, mini=False)
157         return result
158
159     def action_draft(self, cr, uid, ids, context=None):
160         """ Sets state to draft.
161         @return: True
162         """
163         self.write(cr, uid, ids, {'state':'draft'})
164         self.action_draft_send_note(cr, uid, ids, context=context)
165         return True
166
167     def action_start_working(self, cr, uid, ids, context=None):
168         """ Sets state to start working and writes starting date.
169         @return: True
170         """
171         self.modify_production_order_state(cr, uid, ids, 'start')
172         self.write(cr, uid, ids, {'state':'startworking', 'date_start': time.strftime('%Y-%m-%d %H:%M:%S')})
173         self.action_start_send_note(cr, uid, ids, context=context)
174         return True
175
176     def action_done(self, cr, uid, ids, context=None):
177         """ Sets state to done, writes finish date and calculates delay.
178         @return: True
179         """
180         delay = 0.0
181         date_now = time.strftime('%Y-%m-%d %H:%M:%S')
182         obj_line = self.browse(cr, uid, ids[0])
183
184         date_start = datetime.strptime(obj_line.date_start,'%Y-%m-%d %H:%M:%S')
185         date_finished = datetime.strptime(date_now,'%Y-%m-%d %H:%M:%S')
186         delay += (date_finished-date_start).days * 24
187         delay += (date_finished-date_start).seconds / float(60*60)
188
189         self.write(cr, uid, ids, {'state':'done', 'date_finished': date_now,'delay':delay})
190         self.action_done_send_note(cr, uid, ids, context=context)
191         self.modify_production_order_state(cr,uid,ids,'done')
192         return True
193
194     def action_cancel(self, cr, uid, ids, context=None):
195         """ Sets state to cancel.
196         @return: True
197         """
198         self.write(cr, uid, ids, {'state':'cancel'})
199         self.action_cancel_send_note(cr, uid, ids, context=context)
200         return True
201
202     def action_pause(self, cr, uid, ids, context=None):
203         """ Sets state to pause.
204         @return: True
205         """
206         self.write(cr, uid, ids, {'state':'pause'})
207         self.action_pending_send_note(cr, uid, ids, context=context)
208         return True
209
210     def action_resume(self, cr, uid, ids, context=None):
211         """ Sets state to startworking.
212         @return: True
213         """
214         self.write(cr, uid, ids, {'state':'startworking'})
215         self.action_start_send_note(cr, uid, ids, context=context)
216         return True
217
218     # -------------------------------------------------------
219     # OpenChatter methods and notifications
220     # -------------------------------------------------------
221     
222     def action_draft_send_note(self, cr, uid, ids, context=None):
223         prod_obj = self.pool.get('mrp.production')
224         for workorder in self.browse(cr, uid, ids):
225             for prod in prod_obj.browse(cr, uid, [workorder.production_id]):
226                 message = _("Work order has been <b>created</b> for production order <em>%s</em>.") % (prod.id.name)
227                 self.message_append_note(cr, uid, [workorder.id], body=message, context=context)
228         return True
229
230     def action_start_send_note(self, cr, uid, ids, context=None):
231         prod_obj = self.pool.get('mrp.production')
232         for workorder in self.browse(cr, uid, ids):
233             for prod in prod_obj.browse(cr, uid, [workorder.production_id]):
234                 message = _("Work order has been <b>started</b> for production order <em>%s</em>.") % (prod.id.name)
235                 self.message_append_note(cr, uid, [workorder.id], body=message, context=context)
236         return True
237
238     def action_done_send_note(self, cr, uid, ids, context=None):
239         prod_obj = self.pool.get('mrp.production')
240         for workorder in self.browse(cr, uid, ids):
241             for prod in prod_obj.browse(cr, uid, [workorder.production_id]):
242                 message = _("Work order has been <b>done</b> for production order <em>%s</em>.") % (prod.id.name)
243                 self.message_append_note(cr, uid, [workorder.id], body=message, context=context)
244         return True
245
246     def action_pending_send_note(self, cr, uid, ids, context=None):
247         prod_obj = self.pool.get('mrp.production')
248         for workorder in self.browse(cr, uid, ids):
249             for prod in prod_obj.browse(cr, uid, [workorder.production_id]):
250                 message = _("Work order is <b>pending</b> for production order <em>%s</em>.") % (prod.id.name)
251                 self.message_append_note(cr, uid, [workorder.id], body=message, context=context)
252         return True
253
254     def action_cancel_send_note(self, cr, uid, ids, context=None):
255         prod_obj = self.pool.get('mrp.production')
256         for workorder in self.browse(cr, uid, ids):
257             for prod in prod_obj.browse(cr, uid, [workorder.production_id]):
258                 message = _("Work order has been <b>cancelled</b> for production order <em>%s</em>.") % (prod.id.name)
259                 self.message_append_note(cr, uid, [workorder.id], body=message, context=context)
260         return True
261
262 mrp_production_workcenter_line()
263
264 class mrp_production(osv.osv):
265     _inherit = 'mrp.production'
266     _columns = {
267         'allow_reorder': fields.boolean('Free Serialisation', help="Check this to be able to move independently all production orders, without moving dependent ones."),
268     }
269
270     def _production_date_end(self, cr, uid, ids, prop, unknow_none, context=None):
271         """ Calculates planned end date of production order.
272         @return: Dictionary of values
273         """
274         result = {}
275         for prod in self.browse(cr, uid, ids, context=context):
276             result[prod.id] = prod.date_planned
277             for line in prod.workcenter_lines:
278                 result[prod.id] = max(line.date_planned_end, result[prod.id])
279         return result
280
281     def action_production_end(self, cr, uid, ids):
282         """ Finishes work order if production order is done.
283         @return: Super method
284         """
285         obj = self.browse(cr, uid, ids)[0]
286         wf_service = netsvc.LocalService("workflow")
287         for workcenter_line in obj.workcenter_lines:
288             if workcenter_line.state == 'draft':
289                 wf_service.trg_validate(uid, 'mrp.production.workcenter.line', workcenter_line.id, 'button_start_working', cr)
290             wf_service.trg_validate(uid, 'mrp.production.workcenter.line', workcenter_line.id, 'button_done', cr)
291         return super(mrp_production,self).action_production_end(cr, uid, ids)
292
293     def action_in_production(self, cr, uid, ids):
294         """ Changes state to In Production and writes starting date.
295         @return: True
296         """
297         obj = self.browse(cr, uid, ids)[0]
298         workcenter_pool = self.pool.get('mrp.production.workcenter.line')
299         wf_service = netsvc.LocalService("workflow")
300         for prod in self.browse(cr, uid, ids):
301             if prod.workcenter_lines:
302                 wf_service.trg_validate(uid, 'mrp.production.workcenter.line', prod.workcenter_lines[0].id, 'button_start_working', cr)
303         return super(mrp_production,self).action_in_production(cr, uid, ids)
304     
305     def action_cancel(self, cr, uid, ids, context=None):
306         """ Cancels work order if production order is canceled.
307         @return: Super method
308         """
309         obj = self.browse(cr, uid, ids,context=context)[0]
310         wf_service = netsvc.LocalService("workflow")
311         for workcenter_line in obj.workcenter_lines:
312             wf_service.trg_validate(uid, 'mrp.production.workcenter.line', workcenter_line.id, 'button_cancel', cr)
313         return super(mrp_production,self).action_cancel(cr,uid,ids,context=context)
314
315     def _compute_planned_workcenter(self, cr, uid, ids, context=None, mini=False):
316         """ Computes planned and finished dates for work order.
317         @return: Calculated date
318         """
319         dt_end = datetime.now()
320         if context is None:
321             context = {}
322         for po in self.browse(cr, uid, ids, context=context):
323             dt_end = datetime.strptime(po.date_planned, '%Y-%m-%d %H:%M:%S')
324             if not po.date_start:
325                 self.write(cr, uid, [po.id], {
326                     'date_start': po.date_planned
327                 }, context=context, update=False)
328             old = None
329             for wci in range(len(po.workcenter_lines)):
330                 wc  = po.workcenter_lines[wci]
331                 if (old is None) or (wc.sequence>old):
332                     dt = dt_end
333                 if context.get('__last_update'):
334                     del context['__last_update']
335                 if (wc.date_planned < dt.strftime('%Y-%m-%d %H:%M:%S')) or mini:
336                     self.pool.get('mrp.production.workcenter.line').write(cr, uid, [wc.id],  {
337                         'date_planned': dt.strftime('%Y-%m-%d %H:%M:%S')
338                     }, context=context, update=False)
339                     i = self.pool.get('resource.calendar').interval_get(
340                         cr,
341                         uid,
342                         wc.workcenter_id.calendar_id and wc.workcenter_id.calendar_id.id or False,
343                         dt,
344                         wc.hour or 0.0
345                     )
346                     if i:
347                         dt_end = max(dt_end, i[-1][1])
348                 else:
349                     dt_end = datetime.strptime(wc.date_planned_end, '%Y-%m-%d %H:%M:%S')
350
351                 old = wc.sequence or 0
352             super(mrp_production, self).write(cr, uid, [po.id], {
353                 'date_finished': dt_end
354             })
355         return dt_end
356
357     def _move_pass(self, cr, uid, ids, context=None):
358         """ Calculates start date for stock moves finding interval from resource calendar.
359         @return: True
360         """
361         for po in self.browse(cr, uid, ids, context=context):
362             if po.allow_reorder:
363                 continue
364             todo = po.move_lines
365             dt = datetime.strptime(po.date_start,'%Y-%m-%d %H:%M:%S')
366             while todo:
367                 l = todo.pop(0)
368                 if l.state in ('done','cancel','draft'):
369                     continue
370                 todo += l.move_dest_id_lines
371                 if l.production_id and (l.production_id.date_finished > dt):
372                     if l.production_id.state not in ('done','cancel'):
373                         for wc in l.production_id.workcenter_lines:
374                             i = self.pool.get('resource.calendar').interval_min_get(
375                                 cr,
376                                 uid,
377                                 wc.workcenter_id.calendar_id.id or False,
378                                 dt, wc.hour or 0.0
379                             )
380                             dt = i[0][0]
381                         if l.production_id.date_start > dt.strftime('%Y-%m-%d %H:%M:%S'):
382                             self.write(cr, uid, [l.production_id.id], {'date_start':dt.strftime('%Y-%m-%d %H:%M:%S')}, mini=True)
383         return True
384
385     def _move_futur(self, cr, uid, ids, context=None):
386         """ Calculates start date for stock moves.
387         @return: True
388         """
389         for po in self.browse(cr, uid, ids, context=context):
390             if po.allow_reorder:
391                 continue
392             for line in po.move_created_ids:
393                 l = line
394                 while l.move_dest_id:
395                     l = l.move_dest_id
396                     if l.state in ('done','cancel','draft'):
397                         break
398                     if l.production_id.state in ('done','cancel'):
399                         break
400                     if l.production_id and (l.production_id.date_start < po.date_finished):
401                         self.write(cr, uid, [l.production_id.id], {'date_start': po.date_finished})
402                         break
403         return True
404
405
406     def write(self, cr, uid, ids, vals, context=None, update=True, mini=True):
407         direction = {}
408         if vals.get('date_start', False):
409             for po in self.browse(cr, uid, ids, context=context):
410                 direction[po.id] = cmp(po.date_start, vals.get('date_start', False))
411         result = super(mrp_production, self).write(cr, uid, ids, vals, context=context)
412         if (vals.get('workcenter_lines', False) or vals.get('date_start', False)) and update:
413             self._compute_planned_workcenter(cr, uid, ids, context=context, mini=mini)
414         for d in direction:
415             if direction[d] == 1:
416                 # the production order has been moved to the passed
417                 self._move_pass(cr, uid, [d], context=context)
418                 pass
419             elif direction[d] == -1:
420                 self._move_futur(cr, uid, [d], context=context)
421                 # the production order has been moved to the future
422                 pass
423         return result
424
425     def action_compute(self, cr, uid, ids, properties=[], context=None):
426         """ Computes bills of material of a product and planned date of work order.
427         @param properties: List containing dictionaries of properties.
428         @return: No. of products.
429         """
430         result = super(mrp_production, self).action_compute(cr, uid, ids, properties=properties, context=context)
431         self._compute_planned_workcenter(cr, uid, ids, context=context)
432         return result
433
434 mrp_production()
435
436 class mrp_operations_operation_code(osv.osv):
437     _name="mrp_operations.operation.code"
438     _columns={
439         'name': fields.char('Operation Name',size=64, required=True),
440         'code': fields.char('Code', size=16, required=True),
441         'start_stop': fields.selection([('start','Start'),('pause','Pause'),('resume','Resume'),('cancel','Cancelled'),('done','Done')], 'Status', required=True),
442     }
443 mrp_operations_operation_code()
444
445 class mrp_operations_operation(osv.osv):
446     _name="mrp_operations.operation"
447
448     def _order_date_search_production(self, cr, uid, ids, context=None):
449         """ Finds operations for a production order.
450         @return: List of ids
451         """
452         operation_ids = self.pool.get('mrp_operations.operation').search(cr, uid, [('production_id','=',ids[0])], context=context)
453         return operation_ids
454
455     def _get_order_date(self, cr, uid, ids, field_name, arg, context=None):
456         """ Calculates planned date for an operation.
457         @return: Dictionary of values
458         """
459         res={}
460         operation_obj = self.browse(cr, uid, ids, context=context)
461         for operation in operation_obj:
462                 res[operation.id] = operation.production_id.date_planned
463         return res
464
465     def calc_delay(self, cr, uid, vals):
466         """ Calculates delay of work order.
467         @return: Delay
468         """
469         code_lst = []
470         time_lst = []
471
472         code_ids = self.pool.get('mrp_operations.operation.code').search(cr, uid, [('id','=',vals['code_id'])])
473         code = self.pool.get('mrp_operations.operation.code').browse(cr, uid, code_ids)[0]
474
475         oper_ids = self.search(cr,uid,[('production_id','=',vals['production_id']),('workcenter_id','=',vals['workcenter_id'])])
476         oper_objs = self.browse(cr,uid,oper_ids)
477
478         for oper in oper_objs:
479             code_lst.append(oper.code_id.start_stop)
480             time_lst.append(oper.date_start)
481
482         code_lst.append(code.start_stop)
483         time_lst.append(vals['date_start'])
484         diff = 0
485         for i in range(0,len(code_lst)):
486             if code_lst[i] == 'pause' or code_lst[i] == 'done' or code_lst[i] == 'cancel':
487                 if not i: continue
488                 if code_lst[i-1] not in ('resume','start'):
489                    continue
490                 a = datetime.strptime(time_lst[i-1],'%Y-%m-%d %H:%M:%S')
491                 b = datetime.strptime(time_lst[i],'%Y-%m-%d %H:%M:%S')
492                 diff += (b-a).days * 24
493                 diff += (b-a).seconds / float(60*60)
494         return diff
495
496     def check_operation(self, cr, uid, vals):
497         """ Finds which operation is called ie. start, pause, done, cancel.
498         @param vals: Dictionary of values.
499         @return: True or False
500         """
501         code_ids=self.pool.get('mrp_operations.operation.code').search(cr,uid,[('id','=',vals['code_id'])])
502         code=self.pool.get('mrp_operations.operation.code').browse(cr,uid,code_ids)[0]
503         code_lst = []
504         oper_ids=self.search(cr,uid,[('production_id','=',vals['production_id']),('workcenter_id','=',vals['workcenter_id'])])
505         oper_objs=self.browse(cr,uid,oper_ids)
506
507         if not oper_objs:
508             if code.start_stop!='start':
509                 raise osv.except_osv(_('Sorry!'),_('Operation is not started yet !'))
510                 return False
511         else:
512             for oper in oper_objs:
513                  code_lst.append(oper.code_id.start_stop)
514             if code.start_stop=='start':
515                     if 'start' in code_lst:
516                         raise osv.except_osv(_('Sorry!'),_('Operation has already started !' 'Youcan either Pause/Finish/Cancel the operation'))
517                         return False
518             if code.start_stop=='pause':
519                     if  code_lst[len(code_lst)-1]!='resume' and code_lst[len(code_lst)-1]!='start':
520                         raise osv.except_osv(_('Error!'),_('In order to Pause the operation, it must be in the Start or Resume state!'))
521                         return False
522             if code.start_stop=='resume':
523                 if code_lst[len(code_lst)-1]!='pause':
524                    raise osv.except_osv(_('Error!'),_('In order to Resume the operation, it must be in the Pause state!'))
525                    return False
526
527             if code.start_stop=='done':
528                if code_lst[len(code_lst)-1]!='start' and code_lst[len(code_lst)-1]!='resume':
529                   raise osv.except_osv(_('Sorry!'),_('In order to Finish the operation, it must be in the Start or Resume state!'))
530                   return False
531                if 'cancel' in code_lst:
532                   raise osv.except_osv(_('Sorry!'),_('Operation is Already Cancelled!'))
533                   return False
534             if code.start_stop=='cancel':
535                if  not 'start' in code_lst :
536                    raise osv.except_osv(_('Error!'),_('There is no Operation to be cancelled!'))
537                    return False
538                if 'done' in code_lst:
539                   raise osv.except_osv(_('Error!'),_('Operation is already finished!'))
540                   return False
541         return True
542
543     def write(self, cr, uid, ids, vals, context=None):
544         oper_objs = self.browse(cr, uid, ids, context=context)[0]
545         vals['production_id']=oper_objs.production_id.id
546         vals['workcenter_id']=oper_objs.workcenter_id.id
547
548         if 'code_id' in vals:
549             self.check_operation(cr, uid, vals)
550
551         if 'date_start' in vals:
552             vals['date_start']=vals['date_start']
553             vals['code_id']=oper_objs.code_id.id
554             delay=self.calc_delay(cr, uid, vals)
555             wc_op_id=self.pool.get('mrp.production.workcenter.line').search(cr,uid,[('workcenter_id','=',vals['workcenter_id']),('production_id','=',vals['production_id'])])
556             self.pool.get('mrp.production.workcenter.line').write(cr,uid,wc_op_id,{'delay':delay})
557
558         return super(mrp_operations_operation, self).write(cr, uid, ids, vals, context=context)
559
560     def create(self, cr, uid, vals, context=None):
561         wf_service = netsvc.LocalService('workflow')
562         code_ids=self.pool.get('mrp_operations.operation.code').search(cr,uid,[('id','=',vals['code_id'])])
563         code=self.pool.get('mrp_operations.operation.code').browse(cr, uid, code_ids, context=context)[0]
564         wc_op_id=self.pool.get('mrp.production.workcenter.line').search(cr,uid,[('workcenter_id','=',vals['workcenter_id']),('production_id','=',vals['production_id'])])
565         if code.start_stop in ('start','done','pause','cancel','resume'):
566             if not wc_op_id:
567                 production_obj=self.pool.get('mrp.production').browse(cr, uid, vals['production_id'], context=context)
568                 wc_op_id.append(self.pool.get('mrp.production.workcenter.line').create(cr,uid,{'production_id':vals['production_id'],'name':production_obj.product_id.name,'workcenter_id':vals['workcenter_id']}))
569             if code.start_stop=='start':
570                 self.pool.get('mrp.production.workcenter.line').action_start_working(cr,uid,wc_op_id)
571                 wf_service.trg_validate(uid, 'mrp.production.workcenter.line', wc_op_id[0], 'button_start_working', cr)
572
573
574             if code.start_stop=='done':
575                 self.pool.get('mrp.production.workcenter.line').action_done(cr,uid,wc_op_id)
576                 wf_service.trg_validate(uid, 'mrp.production.workcenter.line', wc_op_id[0], 'button_done', cr)
577                 self.pool.get('mrp.production').write(cr,uid,vals['production_id'],{'date_finished':datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
578
579             if code.start_stop=='pause':
580                 self.pool.get('mrp.production.workcenter.line').action_pause(cr,uid,wc_op_id)
581                 wf_service.trg_validate(uid, 'mrp.production.workcenter.line', wc_op_id[0], 'button_pause', cr)
582
583             if code.start_stop=='resume':
584                 self.pool.get('mrp.production.workcenter.line').action_resume(cr,uid,wc_op_id)
585                 wf_service.trg_validate(uid, 'mrp.production.workcenter.line', wc_op_id[0], 'button_resume', cr)
586
587             if code.start_stop=='cancel':
588                 self.pool.get('mrp.production.workcenter.line').action_cancel(cr,uid,wc_op_id)
589                 wf_service.trg_validate(uid, 'mrp.production.workcenter.line', wc_op_id[0], 'button_cancel', cr)
590
591         if not self.check_operation(cr, uid, vals):
592             return
593         delay=self.calc_delay(cr, uid, vals)
594         line_vals = {}
595         line_vals['delay'] = delay
596         if vals.get('date_start',False):
597             if code.start_stop == 'done':
598                 line_vals['date_finished'] = vals['date_start']
599             elif code.start_stop == 'start':
600                 line_vals['date_start'] = vals['date_start']
601
602         self.pool.get('mrp.production.workcenter.line').write(cr, uid, wc_op_id, line_vals, context=context)
603
604         return super(mrp_operations_operation, self).create(cr, uid, vals, context=context)
605
606     def initialize_workflow_instance(self, cr, uid, context=None):
607         wf_service = netsvc.LocalService("workflow")
608         line_ids = self.pool.get('mrp.production.workcenter.line').search(cr, uid, [], context=context)
609         for line_id in line_ids:
610             wf_service.trg_create(uid, 'mrp.production.workcenter.line', line_id, cr)
611         return True
612
613     _columns={
614         'production_id':fields.many2one('mrp.production','Production',required=True),
615         'workcenter_id':fields.many2one('mrp.workcenter','Work Center',required=True),
616         'code_id':fields.many2one('mrp_operations.operation.code','Code',required=True),
617         'date_start': fields.datetime('Start Date'),
618         'date_finished': fields.datetime('End Date'),
619         'order_date': fields.function(_get_order_date,string='Order Date',type='date',store={'mrp.production':(_order_date_search_production,['date_planned'], 10)}),
620         }
621     _defaults={
622         'date_start': lambda *a:datetime.now().strftime('%Y-%m-%d %H:%M:%S')
623     }
624
625 mrp_operations_operation()
626 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
627