~unifield-team/unifield-wm/us-826

« back to all changes in this revision

Viewing changes to sales_followup/sale_followup.py

  • Committer: Matthieu Dietrich
  • Date: 2011-07-22 11:27:00 UTC
  • mto: This revision was merged to the branch mainline in revision 229.
  • Revision ID: matthieu.dietrich@geneva.msf.org-20110722112700-pxnmrctduwhie98g
UF-331: [IMP] Add manufacturers text fields

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
##############################################################################
3
 
#
4
 
#    OpenERP, Open Source Management Solution
5
 
#    Copyright (C) 2011 TeMPO Consulting, MSF
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 osv, fields
23
 
from tools.translate import _
24
 
 
25
 
class sale_order_followup_test(osv.osv_memory):
26
 
    _name = 'sale.order.followup.test'
27
 
    
28
 
    _columns = {'name': fields.char(size=64, string='Name')}
29
 
    
30
 
    def create_test(self, cr, uid, ids, context={}):
31
 
        self.pool.get('product.category').create(cr, uid, {'name': 'test category'}, context=context)
32
 
        return True
33
 
    
34
 
sale_order_followup_test()
35
 
 
36
 
class sale_order_followup(osv.osv_memory):
37
 
    _name = 'sale.order.followup'
38
 
    _description = 'Sales Order Followup'
39
 
    
40
 
    def get_selection(self, cr, uid, o, field):
41
 
        """
42
 
        """
43
 
        sel = self.pool.get(o._name).fields_get(cr, uid, [field])
44
 
        res = dict(sel[field]['selection']).get(getattr(o,field),getattr(o,field))
45
 
        name = '%s,%s' % (o._name, field)
46
 
        tr_ids = self.pool.get('ir.translation').search(cr, uid, [('type', '=', 'selection'), ('name', '=', name),('src', '=', res)])
47
 
        if tr_ids:
48
 
            return self.pool.get('ir.translation').read(cr, uid, tr_ids, ['value'])[0]['value']
49
 
        else:
50
 
            return res
51
 
    
52
 
    def _get_order_state(self, cr, uid, ids, field_name, args, context={}):
53
 
        if not context:
54
 
            context = {}
55
 
            
56
 
        res = {}
57
 
            
58
 
        for follow in self.browse(cr, uid, ids, context=context):
59
 
            res[follow.id] = None
60
 
            
61
 
            if follow.order_id:
62
 
                res[follow.id] = self.get_selection(cr, uid, follow.order_id, 'state')
63
 
            
64
 
        return res
65
 
    
66
 
    _columns = {
67
 
        'order_id': fields.many2one('sale.order', string='Internal reference', readonly=True),
68
 
        'cust_ref': fields.related('order_id', 'client_order_ref', string='Customer reference', readonly=True, type='char'),
69
 
        'creation_date': fields.related('order_id', 'create_date', string='Creation date', readonly=True, type='date'),
70
 
        'state': fields.function(_get_order_state, method=True, type='char', string='Order state', readonly=True),
71
 
        'requested_date': fields.related('order_id', 'delivery_requested_date', string='Requested date', readonly=True, type='date'),
72
 
        'confirmed_date': fields.related('order_id', 'delivery_confirmed_date', string='Confirmed date', readonly=True, type='date'),
73
 
        'line_ids': fields.one2many('sale.order.line.followup', 'followup_id', string='Lines', readonly=True),
74
 
        'choose_type': fields.selection([('documents', 'Documents view'), ('progress', 'Progress view')], string='Type of view'),
75
 
    }
76
 
    
77
 
    _defaults = {
78
 
        'choose_type': lambda *a: 'progress',
79
 
    }
80
 
    
81
 
    def go_to_view(self, cr, uid, ids, context={}):
82
 
        '''
83
 
        Launches the correct view according to the user's choice
84
 
        '''
85
 
        for followup in self.browse(cr, uid, ids, context=context):
86
 
            if followup.choose_type == 'documents':
87
 
                view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sales_followup', 'sale_order_followup_document_view')[1]
88
 
            else:
89
 
                view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sales_followup', 'sale_order_followup_progress_view')[1]
90
 
            
91
 
        return {'type': 'ir.actions.act_window',
92
 
                'res_model': 'sale.order.followup',
93
 
                'res_id': followup.id,
94
 
                'view_id': [view_id],
95
 
                'view_type': 'form',
96
 
                'view_mode': 'form',
97
 
                'target': 'dummy'}
98
 
        
99
 
    def switch_documents(self, cr, uid, ids, context={}):
100
 
        '''
101
 
        Switch to documents view
102
 
        '''
103
 
        self.write(cr, uid, ids, {'choose_type': 'documents'})
104
 
        
105
 
        return self.go_to_view(cr, uid, ids, context=context)
106
 
    
107
 
    def switch_progress(self, cr, uid, ids, context={}):
108
 
        '''
109
 
        Switch to progress view
110
 
        '''
111
 
        self.write(cr, uid, ids, {'choose_type': 'progress'})
112
 
        
113
 
        return self.go_to_view(cr, uid, ids, context=context)
114
 
    
115
 
    def update_followup(self, cr, uid, ids, context={}):
116
 
        '''
117
 
        Updates data in followup view
118
 
        '''
119
 
        new_context = context.copy()
120
 
        
121
 
        # Get information of the old followup before deletion
122
 
        for followup in self.browse(cr, uid, ids, context=new_context):
123
 
            new_context['active_ids'] = [followup.order_id.id]
124
 
            new_context['view_type'] = followup.choose_type
125
 
        
126
 
        # Get the id of the new followup object
127
 
        result = self.start_order_followup(cr, uid, ids, context=new_context).get('res_id')
128
 
        if not result:
129
 
            raise osv.except_osv(_('Error'), _('No followup found ! Cannot update !'))
130
 
        else:        
131
 
            # Remove the old followup object and all his lines (on delete cascade)
132
 
            self.unlink(cr, uid, ids, context=new_context)
133
 
            
134
 
        # Returns the same view as before
135
 
        if new_context.get('view_type') == 'documents':
136
 
            return self.switch_documents(cr, uid, [result], context=new_context)
137
 
        else:
138
 
            return self.switch_progress(cr, uid, [result], context=new_context)
139
 
    
140
 
    def start_order_followup(self, cr, uid, ids, context={}):
141
 
        '''
142
 
        Creates and display a followup object
143
 
        '''
144
 
        order_obj = self.pool.get('sale.order')
145
 
        line_obj = self.pool.get('sale.order.line.followup')
146
 
        
147
 
        # openERP BUG ?
148
 
        ids = context.get('active_ids',[])
149
 
        
150
 
        if not ids:
151
 
            raise osv.except_osv(_('Error'), _('No order found !'))
152
 
        if len(ids) != 1:
153
 
            raise osv.except_osv(_('Error'), _('You should select one order to follow !'))
154
 
        
155
 
        followup_id = False
156
 
        
157
 
        for o in order_obj.browse(cr, uid, ids, context=context):
158
 
            followup_id = self.create(cr, uid, {'order_id': o.id})
159
 
            
160
 
            for line in o.order_line:
161
 
                purchase_ids = self.get_purchase_ids(cr, uid, line.id, context=context)
162
 
                purchase_line_ids = self.get_purchase_line_ids(cr, uid, line.id, purchase_ids, context=context)
163
 
                incoming_ids = self.get_incoming_ids(cr, uid, line.id, purchase_ids, context=context)
164
 
                outgoing_ids = self.get_outgoing_ids(cr, uid, line.id, context=context)
165
 
                tender_ids = self.get_tender_ids(cr, uid, line.id, context=context)
166
 
#                quotation_ids = self.get_quotation_ids(cr, uid, line.id, context=context)
167
 
                
168
 
                line_obj.create(cr, uid, {'followup_id': followup_id,
169
 
                                          'line_id': line.id,
170
 
                                          'tender_ids': [(6,0,tender_ids)],
171
 
#                                          'quotation_ids': [(6,0,quotation_ids)],
172
 
                                          'purchase_ids': [(6,0,purchase_ids)],
173
 
                                          'purchase_line_ids': [(6,0,purchase_line_ids)],
174
 
                                          'incoming_ids': [(6,0,incoming_ids)],
175
 
                                          'outgoing_ids': [(6,0,outgoing_ids)],})
176
 
                    
177
 
        view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sales_followup', 'sale_order_line_follow_choose_view')[1]
178
 
 
179
 
        return {'type': 'ir.actions.act_window',
180
 
                'res_model': 'sale.order.followup',
181
 
                'res_id': followup_id,
182
 
                'view_id': [view_id],
183
 
                'view_type': 'form',
184
 
                'view_mode': 'form',}
185
 
        
186
 
    def get_purchase_ids(self, cr, uid, line_id, context={}):
187
 
        '''
188
 
        Returns a list of purchase orders related to the sale order line
189
 
        '''
190
 
        line_obj = self.pool.get('sale.order.line')
191
 
        
192
 
        if isinstance(line_id, (int, long)):
193
 
            line_id = [line_id]
194
 
            
195
 
        purchase_ids = []
196
 
        
197
 
        for line in line_obj.browse(cr, uid, line_id, context=context):
198
 
            if line.type == 'make_to_order' and line.procurement_id:
199
 
                if line.procurement_id.purchase_id and not line.procurement_id.purchase_id.rfq_ok:
200
 
                    purchase_ids.append(line.procurement_id.purchase_id.id)
201
 
                elif line.procurement_id.tender_id and line.procurement_id.tender_id.rfq_ids:
202
 
                    for rfq in line.procurement_id.tender_id.rfq_ids:
203
 
                        if not rfq.rfq_ok:
204
 
                            purchase_ids.append(rfq.id)
205
 
        
206
 
        return purchase_ids
207
 
 
208
 
    def get_purchase_line_ids(self, cr, uid, line_id, purchase_ids, context={}):
209
 
        '''
210
 
        Returns a list of purchase order lines related to the sale order line
211
 
        '''
212
 
        po_line_obj = self.pool.get('purchase.order.line')
213
 
        line_obj = self.pool.get('sale.order.line')
214
 
        po_line_ids = []
215
 
 
216
 
        if isinstance(purchase_ids, (int, long)):
217
 
            purchase_ids = [purchase_ids]
218
 
 
219
 
        if isinstance(line_id, (int, long)):
220
 
            line_id = [line_id]
221
 
 
222
 
        for line in line_obj.browse(cr, uid, line_id, context=context):
223
 
            po_line_ids = po_line_obj.search(cr, uid, [('order_id', 'in', purchase_ids), ('product_id', '=', line.product_id.id)], context=context)
224
 
 
225
 
        return po_line_ids
226
 
    
227
 
    def get_quotation_ids(self, cr, uid, line_id, context={}):
228
 
        '''
229
 
        Returns a list of request for quotation related to the sale order line
230
 
        '''
231
 
        line_obj = self.pool.get('sale.order.line')
232
 
        
233
 
        if isinstance(line_id, (int, long)):
234
 
            line_id = [line_id]
235
 
            
236
 
        quotation_ids = []
237
 
        
238
 
        for line in line_obj.browse(cr, uid, line_id, context=context):
239
 
            if line.type == 'make_to_order' and line.procurement_id:
240
 
                if line.procurement_id.purchase_id and line.procurement_id.purchase_id.rfq_ok:
241
 
                    quotation_ids.append(line.procurement_id.purchase_id.id)
242
 
                elif line.procurement_id.tender_id and line.procurement_id.tender_id.rfq_ids:
243
 
                    for rfq in line.procurement_id.tender_id.rfq_ids:
244
 
                        if rfq.rfq_ok:
245
 
                            quotation_ids.append(rfq.id)
246
 
                
247
 
        
248
 
        return quotation_ids
249
 
        
250
 
    def get_incoming_ids(self, cr, uid, line_id, purchase_ids, context={}):
251
 
        '''
252
 
        Returns a list of incoming shipments related to the sale order line
253
 
        '''
254
 
        line_obj = self.pool.get('sale.order.line')
255
 
        purchase_obj = self.pool.get('purchase.order')
256
 
                
257
 
        if isinstance(line_id, (int, long)):
258
 
            line_id = [line_id]
259
 
            
260
 
        if isinstance(purchase_ids, (int, long)):
261
 
            purchase_ids= [purchase_ids]
262
 
            
263
 
        incoming_ids = []
264
 
        
265
 
        for line in line_obj.browse(cr, uid, line_id, context=context):
266
 
            for po in purchase_obj.browse(cr, uid, purchase_ids, context=context):
267
 
                for po_line in po.order_line:
268
 
                    if po_line.product_id.id == line.product_id.id:
269
 
                        for move in po_line.move_ids:
270
 
                            incoming_ids.append(move.id)
271
 
        
272
 
        return incoming_ids
273
 
        
274
 
    def get_outgoing_ids(self, cr, uid, line_id, context={}):
275
 
        '''
276
 
        Returns a list of outgoing deliveries related to the sale order line
277
 
        '''
278
 
        line_obj = self.pool.get('sale.order.line')
279
 
        move_obj = self.pool.get('stock.move')
280
 
                
281
 
        if isinstance(line_id, (int, long)):
282
 
            line_id = [line_id]
283
 
            
284
 
        outgoing_ids = []
285
 
 
286
 
        # Get all stock.picking associated to the sale order line
287
 
        for line in line_obj.browse(cr, uid, line_id, context=context):
288
 
            for move in line.move_ids:
289
 
                if move.id not in outgoing_ids:
290
 
                    outgoing_ids.append(move.id)
291
 
#                if move.picking_id and move.picking_id.id not in outgoing_ids:
292
 
#                    outgoing_ids.append(move.picking_id.id)
293
 
 
294
 
        return outgoing_ids
295
 
    
296
 
    def get_tender_ids(self, cr, uid, line_id, context={}):
297
 
        '''
298
 
        Returns a list of call for tender related to the sale order line
299
 
        '''
300
 
        line_obj = self.pool.get('sale.order.line')
301
 
                
302
 
        if isinstance(line_id, (int, long)):
303
 
            line_id = [line_id]
304
 
            
305
 
        tender_ids = []
306
 
 
307
 
        for line in line_obj.browse(cr, uid, line_id, context=context):
308
 
            for tender in line.tender_line_ids:
309
 
                tender_ids.append(tender.id)
310
 
        
311
 
        return tender_ids
312
 
        
313
 
    
314
 
sale_order_followup()
315
 
 
316
 
class sale_order_line_followup(osv.osv_memory):
317
 
    _name = 'sale.order.line.followup'
318
 
    _description = 'Sales Order Lines Followup'
319
 
    
320
 
    def _get_status(self, cr, uid, ids, field_name, arg, context={}):
321
 
        '''
322
 
        Get all status about the line
323
 
        '''
324
 
        move_obj = self.pool.get('stock.move')
325
 
        
326
 
        res = {}
327
 
        
328
 
        for line in self.browse(cr, uid, ids, context=context):
329
 
            res[line.id] = {'sourced_ok': 'No',
330
 
#                            'quotation_status': 'No quotation',
331
 
                            'tender_status': 'N/A',
332
 
                            'purchase_status': 'N/A',
333
 
                            'incoming_status': 'N/A',
334
 
                            'outgoing_status': 'No deliveries',
335
 
                            'product_available': 'Waiting',
336
 
                            'outgoing_nb': 0,
337
 
                            'available_qty': 0.00}
338
 
 
339
 
            # Set the available qty in stock
340
 
            res[line.id]['available_qty'] = self.pool.get('product.product').browse(cr, uid, line.line_id.product_id.id, context=context).qty_available
341
 
 
342
 
            # Define if the line is sourced or not according to the state on the SO line
343
 
            if line.line_id.state == 'draft':
344
 
                res[line.id]['sourced_ok'] = 'No'
345
 
            if line.line_id.state in ('confirmed', 'done'):
346
 
                res[line.id]['sourced_ok'] = 'Done'
347
 
            if line.line_id.state == 'cancel':
348
 
                res[line.id]['sourced_ok'] = 'Cancelled'
349
 
            if line.line_id.state == 'exception':
350
 
                res[line.id]['sourced_ok'] = 'Exception'
351
 
            
352
 
            ####################################################
353
 
            # Get information about the state of call for tender
354
 
            ####################################################
355
 
            tender_status = {'n_a': 'N/A',
356
 
                             'no_tender': 'No tender',
357
 
                             'partial': 'Partial',
358
 
                             'draft': 'Waiting',
359
 
                             'comparison': 'In Progress',
360
 
                             'done': 'Done',
361
 
                             'cancel': 'Cancelled'}
362
 
 
363
 
            if line.line_id.type == 'make_to_stock' or line.line_id.po_cft == 'po':
364
 
                res[line.id]['tender_status'] = tender_status.get('n_a', 'Error on state !')
365
 
            elif line.line_id.po_cft == 'cft' and not line.tender_ids:
366
 
                res[line.id]['tender_status'] = tender_status.get('no_tender', 'Error on state !')
367
 
            else:
368
 
                # Check if all generated tenders are in the same state
369
 
                tender_state = False
370
 
                for tender in line.tender_ids:
371
 
                    if not tender_state:
372
 
                        tender_state = tender.state
373
 
                    if tender_state != tender.state:
374
 
                        tender_state = 'partial'
375
 
 
376
 
                res[line.id]['tender_status'] = tender_status.get(tender_state, 'Error on state !')
377
 
 
378
 
            ####################################################
379
 
            # Get information about the state of purchase orders
380
 
            ####################################################
381
 
            purchase_status = {'n_a': 'N/A',
382
 
                               'no_order': 'No order',
383
 
                               'partial': 'Partial',
384
 
                               'draft': 'Draft',
385
 
                               'confirmed': 'Confirmed',
386
 
                               'wait': 'Confirmed',
387
 
                               'approved': 'Approved',
388
 
                               'done': 'Done',
389
 
                               'cancel': 'Cancelled',
390
 
                               'except_picking': 'Exception',
391
 
                               'except_invoice': 'Exception',}
392
 
 
393
 
            if line.line_id.type == 'make_to_stock':
394
 
                res[line.id]['purchase_status'] = purchase_status.get('n_a', 'Error on state !')
395
 
            elif not line.purchase_ids:
396
 
                res[line.id]['purchase_status'] = purchase_status.get('no_order', 'Error on state !')
397
 
            else:
398
 
                # Check if all generated PO are in the same state
399
 
                purchase_state = False
400
 
                for order in line.purchase_ids:
401
 
                    if not purchase_state:
402
 
                        purchase_state = order.state
403
 
                    if purchase_state != order.state:
404
 
                        purchase_state = 'partial'
405
 
 
406
 
                res[line.id]['purchase_status'] = purchase_status.get(purchase_state, 'Error on state !')
407
 
 
408
 
            ###########################################################
409
 
            # Get information about the state of all incoming shipments
410
 
            ###########################################################
411
 
            incoming_status = {'n_a': 'N/A',
412
 
                               'no_incoming': 'No shipment',
413
 
                               'partial': 'Partial',
414
 
                               'draft': 'Waiting',
415
 
                               'confirmed': 'Waiting',
416
 
                               'assigned': 'Available',
417
 
                               'done': 'Done',
418
 
                               'cancel': 'Cancelled'}
419
 
 
420
 
            if line.line_id.type == 'make_to_stock':
421
 
                res[line.id]['incoming_status'] = incoming_status.get('n_a', 'Error on state !')
422
 
            elif not line.incoming_ids:
423
 
                res[line.id]['incoming_status'] = incoming_status.get('no_incoming', 'Error on state !')
424
 
            else:
425
 
                shipment_state = False
426
 
                for shipment in line.incoming_ids:
427
 
                    if not shipment_state:
428
 
                        shipment_state = shipment.state
429
 
                    if shipment_state != shipment.state:
430
 
                        shipment_state = 'partial'
431
 
 
432
 
                res[line.id]['incoming_status'] = incoming_status.get(shipment_state, 'Error on state !')
433
 
 
434
 
            #######################################################################
435
 
            # Get information about the step and the state of all outgoing delivery
436
 
            #######################################################################
437
 
            out_status = {'no_out': 'No deliveries',
438
 
                          'partial': 'Partial',
439
 
                          'draft': 'Waiting',
440
 
                          'confirmed': 'Waiting',
441
 
                          'assigned': 'Available',
442
 
                          'done': 'Done',
443
 
                          'picked': 'Picked',
444
 
                          'packed': 'Packed',
445
 
                          'shipped': 'Shipped',
446
 
                          'cancel': 'Cancelled',}
447
 
 
448
 
            if not line.outgoing_ids:
449
 
                res[line.id]['outgoing_status'] = out_status.get('no_out', 'Error on state !')
450
 
                res[line.id]['outgoing_nb'] = '0'
451
 
            else:
452
 
                # Get the first stock.picking
453
 
                first_out = False
454
 
                moves_first_out = []
455
 
                moves_first_out_ids = []
456
 
                for out in line.outgoing_ids:
457
 
                    if out.picking_id and not out.picking_id.previous_step_id:
458
 
                        first_out = out.picking_id
459
 
                        moves_first_out.append(out)
460
 
                        moves_first_out_ids.append(out.id)
461
 
 
462
 
                # Check the flow type of the first picking (full or quick)
463
 
                if first_out.subtype == 'standard':
464
 
                    res[line.id]['outgoing_nb'] = len(moves_first_out)
465
 
                    out_state = False
466
 
                    for out in moves_first_out:
467
 
                        if not out_state:
468
 
                            out_state = out.state
469
 
                        if out.state != out_state:
470
 
                            out_state = 'partial'
471
 
 
472
 
                    res[line.id]['outgoing_status'] = out_status.get(out_state, 'Error on state !')
473
 
                    res[line.id]['product_available'] = out_status.get(out_state, 'Error on state !')
474
 
                else:
475
 
                    # Full mode
476
 
                    # Begin from the first out moves
477
 
                    ppl_ids = []
478
 
                    out_step = {'general': {'moves': [], 'state': False},
479
 
                                'picking': {'moves': [], 'state': False},
480
 
                                'packing': {'moves': [], 'state': False},
481
 
                                'dispatch': {'moves': [], 'state': False},
482
 
                                'distrib': {'moves': [], 'state': False},
483
 
                                'customer': {'moves': [], 'state': False},}
484
 
                    
485
 
                    # Sort outgoing moves by type (picking, packing, dispatch, distrib, sending)
486
 
                    for out in line.outgoing_ids:
487
 
                        if not out.backmove_id:
488
 
                            out_step['general']['moves'].append(out)
489
 
                        elif out.backmove_id.id in moves_first_out_ids and out.picking_id and out.picking_id.subtype == 'picking':
490
 
                            out_step['picking']['moves'].append(out)
491
 
                        elif out.backmove_id.id in moves_first_out_ids and out.picking_id and out.picking_id.subtype == 'ppl':
492
 
                            out_step['packing']['moves'].append(out)
493
 
                            ppl_ids.append(out.picking_id.id)
494
 
                        elif out.backmove_id.id in moves_first_out_ids and out.picking_id and out.picking_id.subtype == 'packing':
495
 
                            if out.location_dest_id.usage == 'customer':
496
 
                                out_step['customer']['moves'].append(out)
497
 
                            elif out.location_dest_id.id == out.picking_id.warehouse_id.lot_distribution_id.id:
498
 
                                out_step['distrib']['moves'].append(out)
499
 
                            elif out.location_dest_id.id == out.picking_id.warehouse_id.lot_dispatch_id.id:
500
 
                                out_step['dispatch']['moves'].append(out)
501
 
                            elif out.location_id.id == out.picking_id.warehouse_id.lot_dispatch_id.id:
502
 
                                out_step['dispatch']['moves'].append(out)
503
 
                            elif out.location_id.id == out.picking_id.warehouse_id.lot_distribution_id.id:
504
 
                                out_step['distrib']['moves'].append(out)
505
 
                                
506
 
                    nb_out = len(out_step['picking']['moves'])
507
 
                    
508
 
                    nb_return_pack = 0
509
 
                    nb_return_pack2 = 0
510
 
                    for pack in out_step['packing']['moves']:
511
 
                        if pack.state == 'done' and pack.product_qty == 0.00:
512
 
                            nb_return_pack += 1
513
 
                        
514
 
                    nb_return_dist = 0    
515
 
                    for cust in out_step['customer']['moves']:
516
 
                        if cust.state == 'done' and cust.product_qty == 0.00:
517
 
                            nb_return_dist += 1
518
 
                    
519
 
                    # Set the state for the step 'customer'
520
 
                    ret_iter = 0
521
 
                    for cust in out_step['customer']['moves']:
522
 
                        if cust.state == 'done' and ret_iter != nb_return_dist:
523
 
                            ret_iter += 1
524
 
                            continue
525
 
                        if not out_step['customer']['state']:
526
 
                            out_step['customer']['state'] = cust.state
527
 
                        if out_step['customer']['state'] != cust.state:
528
 
                            out_step['customer']['state'] = 'partial'
529
 
                            
530
 
                    # Set the state for the step 'distrib'
531
 
                    ret_iter = 0
532
 
                    for dist in out_step['distrib']['moves']:
533
 
                        if dist.state == 'cancel' or dist.product_qty == 0.00:
534
 
                            continue
535
 
                        if dist.state == 'done' and ret_iter != nb_return_dist:
536
 
                            ret_iter += 1
537
 
                            continue
538
 
                        if not out_step['distrib']['state']:
539
 
                            out_step['distrib']['state'] = dist.state
540
 
                        if out_step['distrib']['state'] != dist.state:
541
 
                            out_step['distrib']['state'] = 'partial'
542
 
                            
543
 
                    # Set the state for the step 'dispatch'    
544
 
                    for disp in out_step['dispatch']['moves']:
545
 
                        if disp.location_id.id == disp.picking_id.warehouse_id.lot_dispatch_id.id:
546
 
                            nb_return_pack2 += 1
547
 
                            nb_out -= 1
548
 
                        if not out_step['dispatch']['state']:
549
 
                            out_step['dispatch']['state'] = disp.state
550
 
                        if out_step['dispatch']['state'] != disp.state:
551
 
                            out_step['dispatch']['state'] = 'partial'
552
 
                    
553
 
                    # Set the state for the step 'packing'
554
 
                    ret_iter = 0
555
 
                    for pack in out_step['packing']['moves']:
556
 
                        if pack.product_qty == 0.00 or pack.location_dest_id.id != pack.picking_id.warehouse_id.lot_dispatch_id.id:
557
 
                            continue
558
 
                        if pack.state == 'done' and ret_iter != (nb_return_pack + nb_return_pack2):
559
 
                            ret_iter += 1
560
 
                            continue
561
 
                        if not out_step['packing']['state']:
562
 
                            out_step['packing']['state'] = pack.state
563
 
                        if out_step['packing']['state'] != pack.state:
564
 
                            out_step['packing']['state'] = 'partial'
565
 
                            
566
 
                    # Set the state for the step 'picking'
567
 
                    ret_iter = 0  
568
 
                    ret_iter2 = 0
569
 
                    for pick in out_step['picking']['moves']:
570
 
                        if pick.state == 'cancel':
571
 
                            nb_out -= 1
572
 
                            continue
573
 
                        if pick.state == 'done' and ret_iter != nb_return_pack:
574
 
                            nb_out -= 1
575
 
                            ret_iter += 1
576
 
                            continue
577
 
                        elif pick.state == 'done' and ret_iter2 != nb_return_pack2:
578
 
                            ret_iter2 += 1
579
 
#                            nb_out -= 1
580
 
                            continue
581
 
                        if not out_step['picking']['state']:
582
 
                            out_step['picking']['state'] = pick.state
583
 
                        if out_step['picking']['state'] != pick.state:
584
 
                            out_step['picking']['state'] = 'partial'          
585
 
                                        
586
 
                    # Increase the nb of out if there are products in general picking ticket
587
 
                    total_line = 0.00
588
 
                    for general in out_step['general']['moves']:
589
 
                        if general.product_qty != 0.00 and general.state != 'cancel':
590
 
                            total_line += general.product_qty
591
 
                            nb_out += 1
592
 
                            out_step['general']['state'] = general.state
593
 
                    
594
 
                    # If all products should be processed from the main picking ticket or if the main picking ticket is done
595
 
                    if total_line == line.line_id.product_uom_qty:
596
 
                        res[line.id]['product_available'] = out_status.get(out_step['general']['state'], 'Error on state !')
597
 
                        res[line.id]['outgoing_status'] = out_status.get(out_step['general']['state'], 'Error on state !')
598
 
                    elif out_step['customer']['state'] == 'done':
599
 
                        res[line.id]['product_available'] = out_status.get('done', 'Error on state !')
600
 
                        res[line.id]['outgoing_status'] = out_status.get('done', 'Error on state !')
601
 
                    elif total_line < line.line_id.product_uom_qty and out_step['general']['state']:
602
 
                        res[line.id]['product_available'] = out_status.get('partial', 'Error on state !')
603
 
                        res[line.id]['outgoing_status'] = out_status.get('partial', 'Error on state !')
604
 
                    else:
605
 
                        # If not all products are sent to the supplier
606
 
                        if out_step['customer']['state'] and out_step['customer']['state'] == 'partial':
607
 
                            res[line.id]['outgoing_status'] = out_status.get('partial', 'Error on state !')
608
 
                            res[line.id]['product_available'] = out_status.get('done', 'Error on state !')
609
 
                        # If all products are waiting to send to customer
610
 
                        elif out_step['customer']['state'] and out_step['customer']['state'] == 'assigned':
611
 
                            res[line.id]['outgoing_status'] = out_status.get('shipped', 'Error on state !')
612
 
                            res[line.id]['product_available'] = out_status.get('done', 'Error on state !')
613
 
                        
614
 
                        # If all products are not in distribution
615
 
                        if out_step['distrib']['state'] and out_step['distrib']['state'] == 'partial':
616
 
                            res[line.id]['outgoing_status'] = out_status.get('partial', 'Error on state !')
617
 
                        elif out_step['distrib']['state'] and out_step['distrib']['state'] == 'assigned':
618
 
                            res[line.id]['outgoing_status'] = out_status.get('packed', 'Error on state !')
619
 
                            res[line.id]['product_available'] = out_status.get('done', 'Error on state !')
620
 
                            
621
 
                        # If all products are not in dispatch zone
622
 
                        if out_step['dispatch']['state'] == 'partial':
623
 
                            res[line.id]['outgoing_status'] = out_status.get('partial', 'Error on state !')
624
 
                        
625
 
                        # If all products are not picked
626
 
                        if out_step['picking']['state'] == 'partial' or out_step['packing']['state'] == 'partial':
627
 
                            res[line.id]['outgoing_status'] = out_status.get('partial', 'Error on state !')
628
 
                            res[line.id]['product_available'] = out_status.get(out_step['picking']['state'], 'Error on state !')
629
 
                        elif out_step['picking']['state'] == 'assigned':
630
 
                            res[line.id]['outgoing_status'] = out_status.get('assigned', 'Error on state !')
631
 
                            res[line.id]['product_available'] = out_status.get('assigned', 'Error on state !')
632
 
                        elif out_step['picking']['state'] == 'done' and out_step['packing']['state'] == 'assigned':
633
 
                            res[line.id]['outgoing_status'] = out_status.get('picked', 'Error on state !')
634
 
                            res[line.id]['product_available'] = out_status.get('done', 'Error on state !')
635
 
                        
636
 
                    # Set the number of the outgoing deliveries
637
 
                    res[line.id]['outgoing_nb'] = '%s' %nb_out
638
 
 
639
 
        return res
640
 
    
641
 
    _columns = {
642
 
        'followup_id': fields.many2one('sale.order.followup', string='Sale Order Followup', required=True, on_delete='cascade'),
643
 
        'line_id': fields.many2one('sale.order.line', string='Order line', required=True, readonly=True),
644
 
        'procure_method': fields.related('line_id', 'type', type='selection', selection=[('make_to_stock','From stock'), ('make_to_order','On order')], readonly=True, string='Proc. Method'),
645
 
        'po_cft': fields.related('line_id', 'po_cft', type='selection', selection=[('po','PO'), ('cft','CFT')], readonly=True, string='PO/CFT'),
646
 
        'line_number': fields.related('line_id', 'line_number', string='Order line', readonly=True, type='integer'),
647
 
        'product_id': fields.related('line_id', 'product_id', string='Product reference', readondy=True, 
648
 
                                     type='many2one', relation='product.product'),
649
 
        'qty_ordered': fields.related('line_id', 'product_uom_qty', string='Ordered qty', readonly=True),
650
 
        'uom_id': fields.related('line_id', 'product_uom', type='many2one', relation='product.uom', string='UoM', readonly=True),
651
 
        'sourced_ok': fields.function(_get_status, method=True, string='Sourced', type='char', 
652
 
                                   readonly=True, multi='status'),
653
 
        'tender_ids': fields.many2many('tender', 'call_tender_follow_rel',
654
 
                                       'follow_line_id', 'tender_id', string='Tender'),
655
 
        'tender_status': fields.function(_get_status, method=True, string='Tender', type='char',
656
 
                                         readonly=True, multi='status'),
657
 
#        'quotation_ids': fields.many2many('purchase.order', 'quotation_follow_rel', 'follow_line_id',
658
 
#                                          'quotation_id', string='Requests for Quotation', readonly=True),
659
 
#        'quotation_status': fields.function(_get_status, method=True, string='Request for Quotation',
660
 
#                                            type='char', readonly=True, multi='status'),
661
 
        'purchase_ids': fields.many2many('purchase.order', 'purchase_follow_rel', 'follow_line_id', 
662
 
                                         'purchase_id', string='Purchase Orders', readonly=True),
663
 
        'purchase_line_ids': fields.many2many('purchase.order.line', 'purchase_line_follow_rel', 'follow_line_id',
664
 
                                              'purchase_line_id', string='Purchase Orders', readonly=True),
665
 
        'purchase_status': fields.function(_get_status, method=True, string='Purchase Order',
666
 
                                            type='char', readonly=True, multi='status'),
667
 
        'incoming_ids': fields.many2many('stock.move', 'incoming_follow_rel', 'follow_line_id', 
668
 
                                         'incoming_id', string='Incoming Shipment', readonly=True),
669
 
        'incoming_status': fields.function(_get_status, method=True, string='Incoming Shipment',
670
 
                                            type='char', readonly=True, multi='status'),
671
 
        'product_available': fields.function(_get_status, method=True, string='Product available',
672
 
                                             type='char', readonly=True, multi='status'),
673
 
        'available_qty': fields.function(_get_status, method=True, string='Product available',
674
 
                                            type='float', readonly=True, multi='status'),
675
 
        'outgoing_ids': fields.many2many('stock.move', 'outgoing_follow_rel', 'outgoing_id', 
676
 
                                         'follow_line_id', string='Outgoing Deliveries', readonly=True),
677
 
        'outgoing_status': fields.function(_get_status, method=True, string='Outgoing delivery',
678
 
                                            type='char', readonly=True, multi='status'),
679
 
        'outgoing_nb': fields.function(_get_status, method=True, string='Outgoing delivery',
680
 
                                            type='char', readonly=True, multi='status'),
681
 
    }
682
 
    
683
 
sale_order_line_followup()
684
 
 
685
 
 
686
 
class sale_order_followup_from_menu(osv.osv_memory):
687
 
    _name = 'sale.order.followup.from.menu'
688
 
    _description = 'Sale order followup menu entry'
689
 
    
690
 
    _columns = {
691
 
        'order_id': fields.many2one('sale.order', string='Internal reference', required=True),
692
 
        'cust_order_id': fields.many2one('sale.order', string='Customer reference', required=True),
693
 
    }
694
 
    
695
 
    def go_to_followup(self, cr, uid, ids, context={}):
696
 
        new_context = context.copy()
697
 
        new_ids = []
698
 
        for menu in self.browse(cr, uid, ids, context=context):
699
 
            new_ids.append(menu.order_id and menu.order_id.id or menu.cust_order_id.id)
700
 
            
701
 
        new_context['active_ids'] = new_ids
702
 
        
703
 
        return self.pool.get('sale.order.followup').start_order_followup(cr, uid, ids, context=new_context)
704
 
 
705
 
    def change_order_id(self, cr, uid, ids, order_id, cust_order_id, type='order_id'):
706
 
        res = {}
707
 
 
708
 
        if type == 'cust_order_id' and cust_order_id:
709
 
            res.update({'order_id': False})
710
 
        elif order_id:
711
 
            res.update({'cust_order_id': False})
712
 
 
713
 
        return {'value': res}
714
 
 
715
 
            
716
 
sale_order_followup_from_menu()
717
 
 
718
 
 
719
 
class tender_line(osv.osv):
720
 
    _name = 'tender.line'
721
 
    _inherit = 'tender.line'
722
 
    
723
 
    def go_to_tender_info(self, cr, uid, ids, context={}):
724
 
        '''
725
 
        Return the form of the object
726
 
        '''
727
 
        view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'tender_flow', 'tender_form')[1]
728
 
        tender_id = self.pool.get('tender.line').browse(cr, uid, ids[0], context=context).tender_id.id
729
 
        return {'type': 'ir.actions.act_window',
730
 
                'res_model': 'tender',
731
 
                'view_type': 'form',
732
 
                'view_mode': 'form',
733
 
                'view_id': [view_id],
734
 
                'res_id': tender_id,}
735
 
    
736
 
tender_line()
737
 
 
738
 
 
739
 
class purchase_order(osv.osv):
740
 
    _name = 'purchase.order'
741
 
    _inherit = 'purchase.order'
742
 
    
743
 
    def go_to_po_info(self, cr, uid, ids, context={}):
744
 
        '''
745
 
        Return the form of the object
746
 
        '''
747
 
        view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'purchase', 'purchase_order_form')[1]
748
 
        po_id = self.pool.get('purchase.order.line').browse(cr, uid, ids, context=context).order_id.id
749
 
        return {'type': 'ir.actions.act_window',
750
 
                'res_model': 'purchase.order',
751
 
                'view_type': 'form',
752
 
                'view_mode': 'form',
753
 
                'view_id': [view_id],
754
 
                'res_id': po_id,}
755
 
    
756
 
purchase_order()
757
 
 
758
 
 
759
 
class request_for_quotation(osv.osv):
760
 
    _name = 'purchase.order'
761
 
    _inherit = 'purchase.order'
762
 
    
763
 
    def go_to_rfq_info(self, cr, uid, ids, context={}):
764
 
        '''
765
 
        Return the form of the object
766
 
        '''
767
 
        view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'purchase', 'purchase_order_form')[1]
768
 
        return {'type': 'ir.actions.act_window',
769
 
                'res_model': 'purchase.order',
770
 
                'view_type': 'form',
771
 
                'view_mode': 'form',
772
 
                'view_id': [view_id],
773
 
                'res_id': ids[0],}
774
 
    
775
 
request_for_quotation()
776
 
 
777
 
 
778
 
class stock_move(osv.osv):
779
 
    _name = 'stock.move'
780
 
    _inherit = 'stock.move'
781
 
 
782
 
    def _get_view_id(self, cr, uid, ids, context={}):
783
 
        '''
784
 
        Returns the good view id
785
 
        '''
786
 
        if isinstance(ids, (int,long)):
787
 
            ids = [ids]
788
 
 
789
 
        obj_data = self.pool.get('ir.model.data')
790
 
 
791
 
        pick = self.pool.get('stock.move').browse(cr, uid, ids, context=context)[0].picking_id
792
 
 
793
 
        view_list = {'out': ('stock', 'view_picking_out_form'),
794
 
                     'in': ('stock', 'view_picking_in_form'),
795
 
                     'internal': ('stock', 'view_picking_form'),
796
 
                     'picking': ('msf_outgoing', 'view_picking_ticket_form'),
797
 
                     'ppl': ('msf_outgoing', 'view_ppl_form'),
798
 
                     'packing': ('msf_outgoing', 'view_packing_form')
799
 
                     }
800
 
        if pick.type == 'out':
801
 
            module, view = view_list.get(pick.subtype,('msf_outgoing', 'view_picking_ticket_form'))
802
 
            try:
803
 
                return obj_data.get_object_reference(cr, uid, module, view)[1], pick.id
804
 
            except ValueError, e:
805
 
                pass
806
 
        
807
 
        module, view = view_list.get(pick.type,('stock', 'view_picking_form'))
808
 
 
809
 
        return self.pool.get('ir.model.data').get_object_reference(cr, uid, module, view)[1], pick.id
810
 
    
811
 
    def go_to_incoming_info(self, cr, uid, ids, context={}):
812
 
        '''
813
 
        Return the form of the object
814
 
        '''
815
 
        view_id = self._get_view_id(cr, uid, ids, context=context)
816
 
        return {'type': 'ir.actions.act_window',
817
 
                'res_model': 'stock.picking',
818
 
                'view_type': 'form',
819
 
                'view_mode': 'form',
820
 
                'view_id': [view_id[0]],
821
 
                'res_id': view_id[1],}
822
 
        
823
 
    def go_to_outgoing_info(self, cr, uid, ids, context={}):
824
 
        '''
825
 
        Return the form of the object
826
 
        '''
827
 
        view_id = self._get_view_id(cr, uid, ids, context=context)
828
 
        return {'type': 'ir.actions.act_window',
829
 
                'res_model': 'stock.picking',
830
 
                'view_type': 'form',
831
 
                'view_mode': 'form',
832
 
                'view_id': [view_id[0]],
833
 
                'res_id': view_id[1],}
834
 
    
835
 
stock_move()
836
 
 
837
 
 
838
 
class purchase_order_line(osv.osv):
839
 
    _name = 'purchase.order.line'
840
 
    _inherit = 'purchase.order.line'
841
 
 
842
 
    STATE_SELECTION = [
843
 
                       ('draft', 'Draft'),
844
 
                       ('wait', 'Waiting'),
845
 
                       ('confirmed', 'Waiting Approval'),
846
 
                       ('approved', 'Approved'),
847
 
                       ('except_picking', 'Shipping Exception'),
848
 
                       ('except_invoice', 'Invoice Exception'),
849
 
                       ('done', 'Done'),
850
 
                       ('cancel', 'Cancelled'),
851
 
                       ('rfq_sent', 'RfQ Sent'),
852
 
                       ('rfq_updated', 'RfQ Updated'),
853
 
    ]
854
 
 
855
 
    ORDER_TYPE = [('regular', 'Regular'), ('donation_exp', 'Donation before expiry'), 
856
 
                                        ('donation_st', 'Standard donation'), ('loan', 'Loan'), 
857
 
                                        ('in_kind', 'In Kind Donation'), ('purchase_list', 'Purchase List'),
858
 
                                        ('direct', 'Direct Purchase Order')]
859
 
 
860
 
    _columns = {
861
 
        'order_type': fields.related('order_id', 'order_type', type='selection', selection=ORDER_TYPE, readonly=True),
862
 
        'po_state': fields.related('order_id', 'state', type='selection', selection=STATE_SELECTION, readonly=True),
863
 
    }
864
 
 
865
 
    def go_to_po_info(self, cr, uid, ids, context={}):
866
 
        '''
867
 
        Return the form of the object
868
 
        '''
869
 
        view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'purchase', 'purchase_order_form')[1]
870
 
        if isinstance(ids, (int,long)):
871
 
            ids = [ids]
872
 
        po_id = self.pool.get('purchase.order.line').browse(cr, uid, ids, context=context)[0].order_id.id
873
 
        return {'type': 'ir.actions.act_window',
874
 
                'res_model': 'purchase.order',
875
 
                'view_type': 'form',
876
 
                'view_mode': 'form',
877
 
                'view_id': [view_id],
878
 
                'res_id': po_id,}
879
 
 
880
 
purchase_order_line()
881
 
 
882
 
 
883
 
class sale_order(osv.osv):
884
 
    _name = 'sale.order'
885
 
    _inherit = 'sale.order'
886
 
 
887
 
    def name_search(self, cr, uid, name='', args=None, operator='ilike', context={}, limit=80):
888
 
        '''
889
 
        Search all SO by internal or customer reference
890
 
        '''
891
 
        if context.get('from_followup'):
892
 
            ids = []
893
 
            if name and len(name) > 1:
894
 
                ids.extend(self.search(cr, uid, [('client_order_ref', operator, name)], context=context))
895
 
 
896
 
            return self.name_get(cr, uid, ids, context=context)
897
 
        else:
898
 
            return super(sale_order, self).name_search(cr, uid, name, args, operator, context, limit)
899
 
 
900
 
    def name_get(self, cr, uid, ids, context={}):
901
 
        '''
902
 
        If the method is called from followup wizard, set the customer ref in brackets
903
 
        '''
904
 
        if context.get('from_followup'):
905
 
            res = []
906
 
            for r in self.browse(cr, uid, ids, context=context):
907
 
                if r.client_order_ref:
908
 
                    res.append((r.id, '%s' % r.client_order_ref))
909
 
                else:
910
 
                    res.append((r.id, '%s' % r.name))
911
 
            return res
912
 
        else:
913
 
            return super(sale_order, self).name_get(cr, uid, ids, context=context)
914
 
 
915
 
sale_order()