~credativ/openobject-addons/elico-7.0-fixes

« back to all changes in this revision

Viewing changes to delivery_plan/sale.py

  • Committer:
  • Date: 2013-09-09 09:06:00 UTC
  • mfrom: (3.2.1 elico-7.0)
  • Revision ID: elicoidal@hotmail.com-20130909090600-ca5wvn9l8npx01w5
[ADD] several new modules

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) 2010-2013 Elico Corp. All Rights Reserved.
 
6
#    Author: Yannick Gouin <yannick.gouin@elico-corp.com>
 
7
#
 
8
#    This program is free software: you can redistribute it and/or modify
 
9
#    it under the terms of the GNU Affero General Public License as
 
10
#    published by the Free Software Foundation, either version 3 of the
 
11
#    License, or (at your option) any later version.
 
12
#
 
13
#    This program is distributed in the hope that it will be useful,
 
14
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
#    GNU Affero General Public License for more details.
 
17
#
 
18
#    You should have received a copy of the GNU Affero General Public License
 
19
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
#
 
21
##############################################################################
 
22
 
 
23
from osv import osv, fields
 
24
from datetime import datetime
 
25
from dateutil.relativedelta import relativedelta
 
26
import pytz
 
27
from openerp import netsvc
 
28
from openerp.tools.translate import _
 
29
 
 
30
 
 
31
class sale_order(osv.osv):
 
32
    _inherit = "sale.order"
 
33
    
 
34
    def _get_dts_id(self, cr, uid, ids, fields, args, context=None):
 
35
        result = {}
 
36
        for so in self.browse(cr, uid, ids, context=context):
 
37
            result[so.id] = so.pts_id and so.pts_id.dts_id and so.pts_id.dts_id.id or False
 
38
        return result
 
39
        
 
40
    def _so_to_update_after_dts_change(self, cr, uid, ids, fields=None, arg=None, context=None):
 
41
        if type(ids) != type([]):
 
42
            ids = [ids]
 
43
        return self.pool.get('sale.order').search(cr, uid, [('pts_id', 'in', ids)]) or []
 
44
    
 
45
    _store_dts_id = {
 
46
        'sale.order': (lambda self, cr, uid, ids, context: ids, ['pts_id'], 10),
 
47
        'delivery.time': (_so_to_update_after_dts_change, ['dts_id'], 10),
 
48
    }    
 
49
    
 
50
    _columns = {
 
51
        'pts_id':   fields.many2one('delivery.time', 'Preparation Time', domain=[('type', '=', 'pts')]),
 
52
        'dts_id':  fields.function(_get_dts_id, method=True, type='many2one', relation='delivery.time', string='Delivery Time', store=_store_dts_id, readonly=True, domain=[('type', '=', 'dts')]),
 
53
        'batch_id': fields.many2one('picking.batch', 'Picking Batch', change_default=True),
 
54
        'start_date': fields.datetime('Delivery Start Date'),
 
55
        'end_date': fields.datetime(' Delivery End Date'),
 
56
        # 'so_payment_method': fields.char('Payment Method', size=32),
 
57
    }
 
58
 
 
59
    
 
60
    def action_cancel_order_with_moves_not_delivered(self, cr, uid, ids, context=None):
 
61
        wf_service = netsvc.LocalService("workflow")
 
62
        if context is None:
 
63
            context = {}
 
64
        sale_order_line_obj = self.pool.get('sale.order.line')
 
65
        drl_obj = self.pool.get('delivery.route.line')
 
66
        #proc_obj = self.pool.get('procurement.order')
 
67
        for sale in self.browse(cr, uid, ids, context=context):
 
68
            try:
 
69
                for pick in sale.picking_ids:
 
70
                    for mov in pick.move_lines:
 
71
                        if mov.state not in ('done','cancel'):
 
72
                            mov.write({'state':'cancel'})
 
73
                                    
 
74
                for pick in sale.picking_ids:
 
75
                    if pick.state != 'cancel':
 
76
                        #wf_service.trg_validate(uid, 'stock.picking', pick.id, 'button_cancel', cr)
 
77
                        pick.write({'state':'cancel'})
 
78
                for inv in sale.invoice_ids:
 
79
                    wf_service.trg_validate(uid, 'account.invoice', inv.id, 'invoice_cancel', cr)
 
80
                for line in sale.order_line:
 
81
                    if line.procurement_id:
 
82
                        wf_service.trg_validate(uid, 'procurement.order', line.procurement_id.id, 'button_check', cr)
 
83
                
 
84
                #cancel delivery route line
 
85
                drl_ids = drl_obj.search(cr, uid, [('sale_order_id','=',sale.id),('state','!=','cancel')])
 
86
                drl_obj.action_cancel(cr,uid,drl_ids,context=context)
 
87
                
 
88
                order_ref = context.get('order_ref',False)
 
89
                self.write(cr, uid, [sale.id], {'state':'shipping_except','client_order_ref':order_ref})
 
90
                cr.commit()
 
91
            except:
 
92
                _logger.info('==== #LY action_cancel_order_with_moves_not_delivered fail %s===='%(sale.id))
 
93
        return True
 
94
    
 
95
    def action_cancel_order_with_moves(self, cr, uid, ids, context=None):
 
96
        wf_service = netsvc.LocalService("workflow")
 
97
        if context is None:
 
98
            context = {}
 
99
        sale_order_line_obj = self.pool.get('sale.order.line')
 
100
        drl_obj = self.pool.get('delivery.route.line')
 
101
        #proc_obj = self.pool.get('procurement.order')
 
102
        for sale in self.browse(cr, uid, ids, context=context):
 
103
            try:
 
104
                if sale.state == 'done':
 
105
                        return False
 
106
                for pick in sale.picking_ids:
 
107
                    if pick.state == 'done':
 
108
                        return False
 
109
                    for mov in pick.move_lines:
 
110
                        if mov.state == 'done':
 
111
                            return False
 
112
                for inv in sale.invoice_ids:
 
113
                    if inv.state == 'paid':
 
114
                        return False
 
115
                    
 
116
                for pick in sale.picking_ids:
 
117
                    wf_service.trg_validate(uid, 'stock.picking', pick.id, 'button_cancel', cr)
 
118
                for pick in sale.picking_ids:
 
119
                    if pick.state != 'cancel':
 
120
                        wf_service.trg_validate(uid, 'stock.picking', pick.id, 'button_cancel', cr)
 
121
                                    
 
122
                for inv in sale.invoice_ids:
 
123
                    wf_service.trg_validate(uid, 'account.invoice', inv.id, 'invoice_cancel', cr)
 
124
                for line in sale.order_line:
 
125
                    if line.procurement_id:
 
126
                        wf_service.trg_validate(uid, 'procurement.order', line.procurement_id.id, 'button_check', cr)
 
127
                
 
128
                #cancel delivery route line
 
129
                drl_ids = drl_obj.search(cr, uid, [('sale_order_id','=',sale.id),('state','!=','cancel')])
 
130
                drl_obj.action_cancel(cr,uid,drl_ids,context=context)
 
131
                
 
132
                sale_order_line_obj.write(cr, uid, [l.id for l in  sale.order_line], {'state': 'cancel'})
 
133
                self.write(cr, uid, [sale.id], {'state': 'cancel'})
 
134
                cr.commit()
 
135
            except:
 
136
                _logger.info('==== #LY action_cancel_order_with_moves fail %s===='%(sale.id))
 
137
        return True
 
138
 
 
139
    def _prepare_order_line_procurement(self, cr, uid, order, pt_id, line, move_id, date_planned, context=None):
 
140
        return {
 
141
            'name': line.name,
 
142
            'origin': order.name,
 
143
            'date_planned': date_planned,
 
144
            'product_id': line.product_id.id,
 
145
            'product_qty': line.product_uom_qty,
 
146
            'product_uom': line.product_uom.id,
 
147
            'product_uos_qty': (line.product_uos and line.product_uos_qty) or line.product_uom_qty,
 
148
            'product_uos': (line.product_uos and line.product_uos.id) or line.product_uom.id,
 
149
            'location_id': order.shop_id.warehouse_id.lot_stock_id.id,
 
150
            'procure_method': line.type,
 
151
            'move_id': move_id,
 
152
            'company_id': order.company_id.id,
 
153
            'note': line.name,
 
154
            'pts_id': pt_id,
 
155
        }
 
156
    
 
157
    def _prepare_order_line_move_fc(self, cr, uid, order, line, picking_id, pt_id, date_planned, context=None):
 
158
        location_id = order.shop_id.warehouse_id.lot_stock_id.id
 
159
        output_id = order.shop_id.warehouse_id.lot_output_id.id
 
160
        return {
 
161
            'name': line.name,
 
162
            'picking_id': picking_id,
 
163
            'product_id': line.product_id.id,
 
164
            'date': date_planned,
 
165
            'date_expected': date_planned,
 
166
            'product_qty': line.product_uom_qty,
 
167
            'product_uom': line.product_uom.id,
 
168
            'product_uos_qty': (line.product_uos and line.product_uos_qty) or line.product_uom_qty,
 
169
            'product_uos': (line.product_uos and line.product_uos.id) or line.product_uom.id,
 
170
            'product_packaging': line.product_packaging.id,
 
171
            'partner_id': line.address_allotment_id.id or order.partner_shipping_id.id,
 
172
            'location_id': location_id,
 
173
            'location_dest_id': output_id,
 
174
            'sale_line_id': line.id,
 
175
            'tracking_id': False,
 
176
            'state': 'draft',
 
177
            'company_id': order.company_id.id,
 
178
            'price_unit': line.product_id.standard_price or 0.0,
 
179
            'pts_id':pt_id,
 
180
        }
 
181
    
 
182
    def _prepare_order_picking(self, cr, uid, order, dt_id, pt_id, context=None):
 
183
        # SHOULD USE ir_sequence.next_by_code() or ir_sequence.next_by_id()
 
184
        pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.out')
 
185
        return {
 
186
            'name': pick_name,
 
187
            'origin': order.name,
 
188
            'date': order.date_order,
 
189
            'type': 'out',
 
190
            'state': 'auto',
 
191
            'move_type': order.picking_policy,
 
192
            'sale_id': order.id,
 
193
            'partner_id': order.partner_shipping_id.id,
 
194
            'note': order.note,
 
195
            'invoice_state': (order.order_policy == 'picking' and '2binvoiced') or 'none',
 
196
            'company_id': order.company_id.id,
 
197
            'dts_id':dt_id,
 
198
            'pts_id':pt_id,
 
199
        }
 
200
    
 
201
    def _prepare_pts_dts(self, cr, uid, order, context=None):
 
202
        if not context:
 
203
            context = {}
 
204
        tz = pytz.timezone('Asia/Shanghai')
 
205
        tz2 = pytz.timezone('America/Anchorage')
 
206
        delivery_time_obj = self.pool.get('delivery.time')
 
207
        time_slot_obj = self.pool.get('delivery.time.slot')
 
208
        val={}
 
209
        
 
210
        dts = False
 
211
        pts = False
 
212
        pt_id = False
 
213
        dt_id = False
 
214
        min_date = False
 
215
        slot_id = False
 
216
        now = datetime.now()
 
217
        address = order.partner_shipping_id or order.partner_id or False
 
218
        
 
219
        if order.start_date:
 
220
            min_date = order.start_date
 
221
            dts = order.start_date
 
222
            dts = datetime.strptime(dts, '%Y-%m-%d %H:%M:%S')            
 
223
            dts = pytz.utc.localize(dts).astimezone(tz)
 
224
        if order.date_order:
 
225
            pts = datetime.strptime(order.date_order, '%Y-%m-%d')
 
226
            pts = pytz.utc.localize(pts).astimezone(tz)
 
227
        if not pts:
 
228
            pts = dts
 
229
        
 
230
        if dts:
 
231
            start_date = datetime.strftime(dts, '%Y-%m-%d')
 
232
            from_time = datetime.strftime(dts, '%H:%M')
 
233
            #LY remove the shanghai restrict out
 
234
            # if address and (not address.city or address.city.lower() in ['shanghai']):
 
235
            #     from_time = datetime.strftime(dts, '%H:%M')
 
236
            # else:  # eg: in Nanjing
 
237
            #     from_time = '09:30'
 
238
            name = datetime.strftime(dts, '%y%m%d')
 
239
            name_pts = name
 
240
            start_date_pts = start_date
 
241
            
 
242
            slot_ids = time_slot_obj.search(cr, uid, [('max_time', '>=', from_time), ('type', '=', 'dts')], order='max_time')
 
243
            if slot_ids:
 
244
                slot = time_slot_obj.browse(cr, uid, slot_ids[0])
 
245
                name += slot.name
 
246
                end_date = start_date + ' ' + slot.end_time
 
247
                start_date += ' ' + slot.start_time 
 
248
                start_date = datetime.strptime(start_date, '%Y-%m-%d %H:%M')
 
249
                start_date = pytz.utc.localize(start_date).astimezone(tz2)
 
250
                start_date = datetime.strftime(start_date, '%Y-%m-%d %H:%M')
 
251
                end_date = datetime.strptime(end_date, '%Y-%m-%d %H:%M')
 
252
                end_date = pytz.utc.localize(end_date).astimezone(tz2)
 
253
                end_date = datetime.strftime(end_date, '%Y-%m-%d %H:%M')
 
254
                
 
255
                dt_ids = delivery_time_obj.search(cr, uid, [('name', '=', name), ('type', '=', 'dts'), ('slot_id', '=', slot.id)])
 
256
                if dt_ids:
 
257
                    dt_id = dt_ids[0]
 
258
                else:
 
259
                    dt_id = delivery_time_obj.create(cr, uid, {
 
260
                        'name':name,
 
261
                        'start_date': start_date,
 
262
                        'end_date': end_date,
 
263
                        'active': True,
 
264
                        'type': 'dts',
 
265
                        'slot_id': slot.id,
 
266
                        }, context)
 
267
                    cr.commit()
 
268
        
 
269
        if pts and dt_id:
 
270
            date_pts = datetime.strftime(pts, '%Y-%m-%d')
 
271
            from_time = datetime.strftime(pts, '%H:%M')
 
272
            if date_pts < datetime.strftime(dts, '%Y-%m-%d'):
 
273
                date_pts = datetime.strftime(dts, '%Y-%m-%d')
 
274
                from_time = '00:00'
 
275
            
 
276
            pts_slot_ids = time_slot_obj.search(cr, uid, [('max_time', '>=', from_time), ('type', '=', 'pts'), ('dts_id', '=', slot.id)], order='max_time')
 
277
            if pts_slot_ids:
 
278
                pts_slot = time_slot_obj.browse(cr, uid, pts_slot_ids[0])
 
279
                name_pts += pts_slot.name
 
280
                end_date_pts = start_date_pts + ' ' + pts_slot.end_time
 
281
                start_date_pts += ' ' + pts_slot.start_time
 
282
                start_date_pts = datetime.strptime(start_date_pts, '%Y-%m-%d %H:%M')
 
283
                start_date_pts = pytz.utc.localize(start_date_pts).astimezone(tz2)
 
284
                start_date_pts = datetime.strftime(start_date_pts, '%Y-%m-%d %H:%M')
 
285
                end_date_pts = datetime.strptime(end_date_pts, '%Y-%m-%d %H:%M')
 
286
                end_date_pts = pytz.utc.localize(end_date_pts).astimezone(tz2)
 
287
                end_date_pts = datetime.strftime(end_date_pts, '%Y-%m-%d %H:%M')
 
288
            
 
289
                pt_ids = delivery_time_obj.search(cr, uid, [('name', '=', name_pts), ('type', '=', 'pts'), ('slot_id', '=', pts_slot.id)])
 
290
                if pt_ids:
 
291
                    pt_id = pt_ids[0]
 
292
                else:
 
293
                    pt_id = delivery_time_obj.create(cr, uid, {
 
294
                        'name':name_pts,
 
295
                        'start_date': start_date_pts,
 
296
                        'end_date': end_date_pts,
 
297
                        'active': True,
 
298
                        'type': 'pts',
 
299
                        'slot_id': pts_slot.id,
 
300
                        'dts_id': dt_id,
 
301
                        }, context)
 
302
                    cr.commit()
 
303
                val['pts_id'] = pt_id
 
304
        if val:
 
305
            order.write(val)
 
306
        return min_date, dt_id, pt_id
 
307
    
 
308
    
 
309
    def _create_pickings_and_procurements(self, cr, uid, order, order_lines, picking_id=False, context=None):
 
310
        """Create the required procurements to supply sales order lines, also connecting
 
311
        the procurements to appropriate stock moves in order to bring the goods to the
 
312
        sales order's requested location.
 
313
 
 
314
        If ``picking_id`` is provided, the stock moves will be added to it, otherwise
 
315
        a standard outgoing picking will be created to wrap the stock moves, as returned
 
316
        by :meth:`~._prepare_order_picking`.
 
317
 
 
318
        Modules that wish to customize the procurements or partition the stock moves over
 
319
        multiple stock pickings may override this method and call ``super()`` with
 
320
        different subsets of ``order_lines`` and/or preset ``picking_id`` values.
 
321
 
 
322
        :param browse_record order: sales order to which the order lines belong
 
323
        :param list(browse_record) order_lines: sales order line records to procure
 
324
        :param int picking_id: optional ID of a stock picking to which the created stock moves
 
325
                               will be added. A new picking will be created if ommitted.
 
326
        :return: True
 
327
        """
 
328
        val = {}
 
329
        move_obj = self.pool.get('stock.move')
 
330
        picking_obj = self.pool.get('stock.picking')
 
331
        procurement_obj = self.pool.get('procurement.order')
 
332
        proc_ids = []
 
333
        
 
334
        min_date, dt_id, pt_id = self._prepare_pts_dts(cr, uid, order)
 
335
        # min_date, dt_id, pt_id = order.date_order, order.dts_id.id, order.pts_id.id
 
336
        print '<<<<<<<<<<<<<<  %s, %s, %s' % (min_date, dt_id, pt_id)
 
337
        
 
338
        for line in order_lines:
 
339
            if line.state == 'done':
 
340
                continue
 
341
 
 
342
            date_planned = min_date or self._get_date_planned(cr, uid, order, line, order.date_order, context=context)
 
343
 
 
344
            if line.product_id:
 
345
                if line.product_id.type in ('product', 'consu'):
 
346
                    if not picking_id:
 
347
                        picking_id = picking_obj.create(cr, uid, self._prepare_order_picking(cr, uid, order, dt_id, pt_id, context=context))
 
348
                    move_id = move_obj.create(cr, uid, self._prepare_order_line_move_fc(cr, uid, order, line, picking_id, pt_id, date_planned, context=context))
 
349
                else:
 
350
                    # a service has no stock move
 
351
                    move_id = False
 
352
 
 
353
                proc_id = procurement_obj.create(cr, uid, self._prepare_order_line_procurement(cr, uid, order, pt_id, line, move_id, date_planned, context=context))
 
354
                proc_ids.append(proc_id)
 
355
                line.write({'procurement_id': proc_id})
 
356
                self.ship_recreate(cr, uid, order, line, move_id, proc_id)
 
357
 
 
358
        wf_service = netsvc.LocalService("workflow")
 
359
        if picking_id:
 
360
            wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr)
 
361
        for proc_id in proc_ids:
 
362
            wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr)
 
363
        
 
364
        if order.state == 'shipping_except':
 
365
            val['state'] = 'progress'
 
366
            val['shipped'] = False
 
367
 
 
368
            if (order.order_policy == 'manual'):
 
369
                for line in order.order_line:
 
370
                    if (not line.invoiced) and (line.state not in ('cancel', 'draft')):
 
371
                        val['state'] = 'manual'
 
372
                        break
 
373
        order.write(val)
 
374
        return True
 
375
 
 
376
sale_order()
 
377
 
 
378
 
 
379
class picking_batch(osv.osv):
 
380
    _name = "picking.batch"
 
381
    _columns = {
 
382
        'name': fields.char('Name', size=32, translate=True),
 
383
        'picking_ids': fields.one2many('stock.picking', 'batch_id', 'Contains'),
 
384
        'active': fields.boolean('Active'),
 
385
    }
 
386
 
 
387
    _defaults = {
 
388
        'active': True,
 
389
    }
 
390
 
 
391
picking_batch()
 
392
 
 
393
 
 
394
class stock_picking(osv.osv):
 
395
    _name = "stock.picking"
 
396
    _inherit = "stock.picking"
 
397
    _columns = {
 
398
        'batch_id': fields.many2one('picking.batch', 'Picking Batch', change_default=True),
 
399
        'route_line_id': fields.one2many('delivery.route.line', 'picking_id', 'Delivery Time'),
 
400
        'so_payment_method': fields.char('Payment Method', size=32),
 
401
    }
 
402
        
 
403
    def write(self, cr, uid, ids, vals, context=None):
 
404
        context = context or {}
 
405
        pts_id = False
 
406
        if type(ids) != type([]):
 
407
            ids = [ids]
 
408
        if 'dts_id' in vals:
 
409
            move_pool = self.pool.get('stock.move')
 
410
            proc_pool = self.pool.get('procurement.order')
 
411
            pts_pool = self.pool.get('delivery.time')
 
412
            drl_pool = self.pool.get('delivery.route.line')
 
413
            
 
414
            if 'pts_id' not in vals:
 
415
                pts_id = pts_pool.search(cr, uid, [('active', '=', True), ('type', '=', 'pts'), ('dts_id', '=', vals['dts_id'])], order='start_date DESC')
 
416
                if pts_id:
 
417
                    pts_id = pts_id[0]
 
418
                    vals.update({'pts_id':pts_id})
 
419
            else:
 
420
                pts_id = vals['pts_id']
 
421
            
 
422
            if pts_id:
 
423
                move_ids = move_pool.search(cr, uid, [('state', 'not in', ['cancel', 'done']), ('picking_id', 'in', ids)])
 
424
                if move_ids:
 
425
                    move_pool.write(cr, uid, move_ids, {'pts_id':pts_id})
 
426
                    proc_ids = proc_pool.search(cr, uid, [('state', 'not in', ['cancel', 'done']), ('move_id', 'in', move_ids)])
 
427
                    if proc_ids:
 
428
                        proc_pool.write(cr, uid, proc_ids, {'pts_id':pts_id})
 
429
            
 
430
#            Actually DONE in fields.function
 
431
#            route_lines = drl_pool.search(cr, uid, [('state', 'in', ['draft']), ('picking_id', 'in', ids)])
 
432
#            if route_lines:
 
433
#                drl_pool.write(cr, uid, route_lines, {'dts_id': vals['dts_id']})
 
434
        return super(stock_picking, self).write(cr, uid, ids, vals, context=context)
 
435
    
 
436
    
 
437
    def pts_id_change(self, cr, uid, ids, pts_id, context=None):
 
438
        res = {}
 
439
        context = context or {}
 
440
        if type(ids) != type([]):
 
441
            ids = [ids]
 
442
        move_pool = self.pool.get('stock.move')
 
443
        proc_pool = self.pool.get('procurement.order')
 
444
        pts_pool = self.pool.get('delivery.time')
 
445
        drl_pool = self.pool.get('delivery.route.line')
 
446
        
 
447
        if pts_id:
 
448
            pts = pts_pool.browse(cr, uid, [pts_id])[0]
 
449
            #self.write(cr, uid, ids, {'dts_id':pts and pts.dts_id and pts.dts_id.id or False})
 
450
            res['dts_id'] = pts and pts.dts_id and pts.dts_id.id or False
 
451
        
 
452
        move_ids = move_pool.search(cr, uid, [('state', 'not in', ['cancel', 'done']), ('picking_id', 'in', ids)])
 
453
        if move_ids:
 
454
            vals = {'pts_id':pts_id}
 
455
            if pts_id:
 
456
                vals.update({'date_expected':pts.dts_id.start_date})
 
457
            move_pool.write(cr, uid, move_ids, vals)
 
458
            proc_ids = proc_pool.search(cr, uid, [('state', 'not in', ['cancel', 'done']), ('move_id', 'in', move_ids)])
 
459
            if proc_ids:
 
460
                proc_pool.write(cr, uid, proc_ids, {'pts_id':pts_id})
 
461
        
 
462
#        Actually DONE in fields.function
 
463
#        route_lines = drl_pool.search(cr, uid, [('state', 'in', ['draft']), ('picking_id', 'in', ids)])
 
464
#        if route_lines and 'dts_id' in res:
 
465
#            drl_pool.write(cr, uid, route_lines, {'dts_id': res['dts_id']})
 
466
        return {'value': res}
 
467
 
 
468
 
 
469
    def create(self, cr, uid, data, context=None):
 
470
        """
 
471
        create route line 
 
472
        """
 
473
        if not data.get('pts_id', False) and data.get('origin', False):
 
474
            so_obj = self.pool.get('sale.order')
 
475
            so_ids = so_obj.search(cr, uid, [('name', '=', data.get('origin'))]) or []
 
476
            for so in so_obj.browse(cr, uid, so_ids):
 
477
                data.update({'pts_id':so.pts_id and so.pts_id.id or False, 'dts_id':so.pts_id and so.pts_id.dts_id and so.pts_id.dts_id.id or False})
 
478
        
 
479
        return_type = data.get('return', 'none')
 
480
        sp = super(stock_picking, self).create(cr, uid, data, context=context)
 
481
        if data.get('type', 'internal') in ['in','out'] and data.get('pts_id', False) and return_type not in ['customer', 'supplier']:
 
482
            self.pool.get('delivery.route.line').create(cr, uid, {'picking_id':sp, })
 
483
        return sp
 
484
 
 
485
stock_picking()
 
486
 
 
487
 
 
488
class stock_picking_out(osv.osv):
 
489
    _inherit = "stock.picking.out"
 
490
    _columns = {
 
491
        'batch_id': fields.many2one('picking.batch', 'Picking Batch', change_default=True),
 
492
        'route_line_id': fields.one2many('delivery.route.line', 'picking_id', 'Delivery Time'),
 
493
        'so_payment_method': fields.char('Payment Method', size=32),
 
494
    }
 
495
        
 
496
    def write(self, cr, uid, ids, vals, context=None):
 
497
        context = context or {}
 
498
        pts_id = False
 
499
        if type(ids) != type([]):
 
500
            ids = [ids]
 
501
        if 'dts_id' in vals:
 
502
            move_pool = self.pool.get('stock.move')
 
503
            proc_pool = self.pool.get('procurement.order')
 
504
            pts_pool = self.pool.get('delivery.time')
 
505
            drl_pool = self.pool.get('delivery.route.line')
 
506
            
 
507
            if 'pts_id' not in vals:
 
508
                pts_id = pts_pool.search(cr, uid, [('active', '=', True), ('type', '=', 'pts'), ('dts_id', '=', vals['dts_id'])], order='start_date DESC')
 
509
                if pts_id:
 
510
                    pts_id = pts_id[0]
 
511
                    vals.update({'pts_id':pts_id})
 
512
            else:
 
513
                pts_id = vals['pts_id']
 
514
            
 
515
            if pts_id:
 
516
                move_ids = move_pool.search(cr, uid, [('state', 'not in', ['cancel', 'done']), ('picking_id', 'in', ids)])
 
517
                if move_ids:
 
518
                    move_pool.write(cr, uid, move_ids, {'pts_id':pts_id})
 
519
                    proc_ids = proc_pool.search(cr, uid, [('state', 'not in', ['cancel', 'done']), ('move_id', 'in', move_ids)])
 
520
                    if proc_ids:
 
521
                        proc_pool.write(cr, uid, proc_ids, {'pts_id':pts_id})
 
522
            
 
523
#            Actually DONE in fields.function
 
524
#            route_lines = drl_pool.search(cr, uid, [('state', 'in', ['draft']), ('picking_id', 'in', ids)])
 
525
#            if route_lines:
 
526
#                drl_pool.write(cr, uid, route_lines, {'dts_id': vals['dts_id']})
 
527
        return super(stock_picking_out, self).write(cr, uid, ids, vals, context=context)
 
528
    
 
529
    
 
530
    def pts_id_change(self, cr, uid, ids, pts_id, context=None):
 
531
        res = {}
 
532
        context = context or {}
 
533
        if type(ids) != type([]):
 
534
            ids = [ids]
 
535
        move_pool = self.pool.get('stock.move')
 
536
        proc_pool = self.pool.get('procurement.order')
 
537
        pts_pool = self.pool.get('delivery.time')
 
538
        drl_pool = self.pool.get('delivery.route.line')
 
539
        
 
540
        if pts_id:
 
541
            pts = pts_pool.browse(cr, uid, [pts_id])[0]
 
542
            #self.write(cr, uid, ids, {'dts_id':pts and pts.dts_id and pts.dts_id.id or False})
 
543
            res['dts_id'] = pts and pts.dts_id and pts.dts_id.id or False
 
544
        
 
545
        move_ids = move_pool.search(cr, uid, [('state', 'not in', ['cancel', 'done']), ('picking_id', 'in', ids)])
 
546
        if move_ids:
 
547
            vals = {'pts_id':pts_id}
 
548
            if pts_id:
 
549
                vals.update({'date_expected':pts.dts_id.start_date})
 
550
            move_pool.write(cr, uid, move_ids, vals)
 
551
            proc_ids = proc_pool.search(cr, uid, [('state', 'not in', ['cancel', 'done']), ('move_id', 'in', move_ids)])
 
552
            if proc_ids:
 
553
                proc_pool.write(cr, uid, proc_ids, {'pts_id':pts_id})
 
554
        
 
555
#        Actually DONE in fields.function
 
556
#        route_lines = drl_pool.search(cr, uid, [('state', 'in', ['draft']), ('picking_id', 'in', ids)])
 
557
#        if route_lines and 'dts_id' in res:
 
558
#            drl_pool.write(cr, uid, route_lines, {'dts_id': res['dts_id']})
 
559
        return {'value': res}
 
560
    
 
561
 
 
562
    def create(self, cr, uid, data, context=None):
 
563
        """
 
564
        create route line 
 
565
        """
 
566
        if not data.get('pts_id', False) and data.get('origin', False):
 
567
            so_obj = self.pool.get('sale.order')
 
568
            so_ids = so_obj.search(cr, uid, [('name', '=', data.get('origin'))]) or []
 
569
            for so in so_obj.browse(cr, uid, so_ids):
 
570
                data.update({'pts_id':so.pts_id and so.pts_id.id or False, 'dts_id':so.pts_id and so.pts_id.dts_id and so.pts_id.dts_id.id or False})
 
571
        
 
572
        return_type = data.get('return', 'none')
 
573
        sp = super(stock_picking_out, self).create(cr, uid, data, context=context)
 
574
        if data.get('pts_id', False) and return_type not in ['customer', 'supplier']:
 
575
            self.pool.get('delivery.route.line').create(cr, uid, {'picking_id':sp, })
 
576
        return sp
 
577
 
 
578
stock_picking_out()
 
579
 
 
580
 
 
581
class stock_picking_in(osv.osv):
 
582
    _inherit = "stock.picking.in"
 
583
    
 
584
    _columns = {
 
585
        'batch_id': fields.many2one('picking.batch', 'Picking Batch', change_default=True),
 
586
        'route_line_id': fields.one2many('delivery.route.line', 'picking_id', 'Delivery Time'),
 
587
    }
 
588
    
 
589
    def write(self, cr, uid, ids, vals, context=None):
 
590
        context = context or {}
 
591
        pts_id = False
 
592
        if type(ids) != type([]):
 
593
            ids = [ids]
 
594
        if 'dts_id' in vals:
 
595
            move_pool = self.pool.get('stock.move')
 
596
            proc_pool = self.pool.get('procurement.order')
 
597
            pts_pool = self.pool.get('delivery.time')
 
598
            drl_pool = self.pool.get('delivery.route.line')
 
599
            
 
600
            if 'pts_id' not in vals:
 
601
                pts_id = pts_pool.search(cr, uid, [('active', '=', True), ('type', '=', 'pts'), ('dts_id', '=', vals['dts_id'])], order='start_date DESC')
 
602
                if pts_id:
 
603
                    pts_id = pts_id[0]
 
604
                    vals.update({'pts_id':pts_id})
 
605
            else:
 
606
                pts_id = vals['pts_id']
 
607
            
 
608
            if pts_id:
 
609
                move_ids = move_pool.search(cr, uid, [('state', 'not in', ['cancel', 'done']), ('picking_id', 'in', ids)])
 
610
                if move_ids:
 
611
                    move_pool.write(cr, uid, move_ids, {'pts_id':pts_id})
 
612
                    proc_ids = proc_pool.search(cr, uid, [('state', 'not in', ['cancel', 'done']), ('move_id', 'in', move_ids)])
 
613
                    if proc_ids:
 
614
                        proc_pool.write(cr, uid, proc_ids, {'pts_id':pts_id})
 
615
                        
 
616
#            Actually DONE in fields.function
 
617
#            route_lines = drl_pool.search(cr, uid, [('state', 'in', ['draft']), ('picking_id', 'in', ids)])
 
618
#            if route_lines:
 
619
#                drl_pool.write(cr, uid, route_lines, {'dts_id': vals['dts_id']})
 
620
        return super(stock_picking_in, self).write(cr, uid, ids, vals, context=context)
 
621
    
 
622
    
 
623
    def pts_id_change(self, cr, uid, ids, pts_id, context=None):
 
624
        res = {}
 
625
        context = context or {}
 
626
        if type(ids) != type([]):
 
627
            ids = [ids]
 
628
        move_pool = self.pool.get('stock.move')
 
629
        proc_pool = self.pool.get('procurement.order')
 
630
        pts_pool = self.pool.get('delivery.time')
 
631
        drl_pool = self.pool.get('delivery.route.line')
 
632
        
 
633
        if pts_id:
 
634
            pts = pts_pool.browse(cr, uid, [pts_id])[0]
 
635
            #self.write(cr, uid, ids, {'dts_id':pts and pts.dts_id and pts.dts_id.id or False})
 
636
            res['dts_id'] = pts and pts.dts_id and pts.dts_id.id or False
 
637
        
 
638
        move_ids = move_pool.search(cr, uid, [('state', 'not in', ['cancel', 'done']), ('picking_id', 'in', ids)])
 
639
        if move_ids:
 
640
            vals = {'pts_id':pts_id}
 
641
            if pts_id:
 
642
                vals.update({'date_expected':pts.dts_id.start_date})
 
643
            move_pool.write(cr, uid, move_ids, vals)
 
644
            proc_ids = proc_pool.search(cr, uid, [('state', 'not in', ['cancel', 'done']), ('move_id', 'in', move_ids)])
 
645
            if proc_ids:
 
646
                proc_pool.write(cr, uid, proc_ids, {'pts_id':pts_id})
 
647
        
 
648
#        Actually DONE in fields.function
 
649
#        route_lines = drl_pool.search(cr, uid, [('state', 'in', ['draft']), ('picking_id', 'in', ids)])
 
650
#        if route_lines and 'dts_id' in res:
 
651
#            drl_pool.write(cr, uid, route_lines, {'dts_id': res['dts_id']})
 
652
        return {'value': res}
 
653
    
 
654
 
 
655
    def create(self, cr, uid, data, context=None):
 
656
        """
 
657
        create route line 
 
658
        """
 
659
        if not data.get('pts_id', False) and data.get('origin', False):
 
660
            so_obj = self.pool.get('sale.order')
 
661
            so_ids = so_obj.search(cr, uid, [('name', '=', data.get('origin'))]) or []
 
662
            for so in so_obj.browse(cr, uid, so_ids):
 
663
                data.update({'pts_id':so.pts_id and so.pts_id.id or False, 'dts_id':so.pts_id and so.pts_id.dts_id and so.pts_id.dts_id.id or False})
 
664
        
 
665
        purchase_id = data.get('purchase_id', False)
 
666
        return_type = data.get('return', 'none')
 
667
        if purchase_id:
 
668
            po = self.pool.get('purchase.order').browse(cr, uid, purchase_id)
 
669
            if not po.is_collected:
 
670
                purchase_id = False
 
671
        
 
672
        sp = super(stock_picking_in, self).create(cr, uid, data, context=context)
 
673
        if data.get('pts_id', False) and return_type not in ['customer', 'supplier'] and not purchase_id:
 
674
            self.pool.get('delivery.route.line').create(cr, uid, {'picking_id':sp, })
 
675
        return sp
 
676
 
 
677
stock_picking_in()
 
678
 
 
679
 
 
680
class stock_tracking(osv.osv):
 
681
    _inherit = "stock.tracking"
 
682
    
 
683
    _columns = {
 
684
        'picking_id': fields.many2one('stock.picking', 'Picking Related', change_default=True),
 
685
        'ul_id': fields.many2one('product.ul', 'Picking Box', change_default=True),
 
686
    }
 
687
stock_tracking()
 
688
 
 
689
 
 
690
class mrp_production(osv.osv):
 
691
    _inherit = 'mrp.production'
 
692
 
 
693
    _columns = {
 
694
        'pts_id':  fields.many2one('delivery.time', 'Preparation Time', select=True, domain=[('type', '=', 'pts')]),
 
695
    }
 
696
    
 
697
    def _make_production_internal_shipment_line(self, cr, uid, production_line, shipment_id, parent_move_id, destination_location_id=False, context=None):
 
698
        stock_move = self.pool.get('stock.move')
 
699
        production = production_line.production_id
 
700
        date_planned = production.date_planned
 
701
        # Internal shipment is created for Stockable and Consumer Products
 
702
        if production_line.product_id.type not in ('product', 'consu'):
 
703
            return False
 
704
        source_location_id = production.location_src_id.id
 
705
        if not destination_location_id:
 
706
            destination_location_id = source_location_id
 
707
        return stock_move.create(cr, uid, {
 
708
                        'name': production.name,
 
709
                        'picking_id': shipment_id,
 
710
                        'product_id': production_line.product_id.id,
 
711
                        'product_qty': production_line.product_qty,
 
712
                        'product_uom': production_line.product_uom.id,
 
713
                        'product_uos_qty': production_line.product_uos and production_line.product_uos_qty or False,
 
714
                        'product_uos': production_line.product_uos and production_line.product_uos.id or False,
 
715
                        'date': date_planned,
 
716
                        'move_dest_id': parent_move_id,
 
717
                        'location_id': source_location_id,
 
718
                        'location_dest_id': destination_location_id,
 
719
                        'state': 'waiting',
 
720
                        'company_id': production.company_id.id,
 
721
                        'pts_id': production.pts_id and production.pts_id.id or False,
 
722
                })
 
723
    
 
724
    def _make_production_internal_shipment(self, cr, uid, production, context=None):
 
725
        ir_sequence = self.pool.get('ir.sequence')
 
726
        stock_picking = self.pool.get('stock.picking')
 
727
        routing_loc = None
 
728
        pick_type = 'internal'
 
729
        partner_id = False
 
730
 
 
731
        # Take routing address as a Shipment Address.
 
732
        # If usage of routing location is a internal, make outgoing shipment otherwise internal shipment
 
733
        if production.bom_id.routing_id and production.bom_id.routing_id.location_id:
 
734
            routing_loc = production.bom_id.routing_id.location_id
 
735
            if routing_loc.usage != 'internal':
 
736
                pick_type = 'out'
 
737
            partner_id = routing_loc.partner_id and routing_loc.partner_id.id or False
 
738
 
 
739
        # Take next Sequence number of shipment base on type
 
740
        # SHOULD USE ir_sequence.next_by_code() or ir_sequence.next_by_id()
 
741
        pick_name = ir_sequence.get(cr, uid, 'stock.picking.' + pick_type)
 
742
 
 
743
        picking_id = stock_picking.create(cr, uid, {
 
744
            'name': pick_name,
 
745
            'origin': (production.origin or '').split(':')[0] + ':' + production.name,
 
746
            'type': pick_type,
 
747
            'move_type': 'one',
 
748
            'state': 'auto',
 
749
            'partner_id': partner_id,
 
750
            'auto_picking': self._get_auto_picking(cr, uid, production),
 
751
            'company_id': production.company_id.id,
 
752
            'pts_id': production.pts_id and production.pts_id.id or False,
 
753
            'dts_id': production.pts_id and production.pts_id.dts_id and production.pts_id.dts_id.id or False,
 
754
        })
 
755
        production.write({'picking_id': picking_id}, context=context)
 
756
        return picking_id
 
757
 
 
758
    def _make_production_produce_line(self, cr, uid, production, context=None):
 
759
        stock_move = self.pool.get('stock.move')
 
760
        source_location_id = production.product_id.property_stock_production.id
 
761
        destination_location_id = production.location_dest_id.id
 
762
        data = {
 
763
            'name': production.name,
 
764
            'date': production.date_planned,
 
765
            'product_id': production.product_id.id,
 
766
            'product_qty': production.product_qty,
 
767
            'product_uom': production.product_uom.id,
 
768
            'product_uos_qty': production.product_uos and production.product_uos_qty or False,
 
769
            'product_uos': production.product_uos and production.product_uos.id or False,
 
770
            'location_id': source_location_id,
 
771
            'location_dest_id': destination_location_id,
 
772
            'move_dest_id': production.move_prod_id.id,
 
773
            'state': 'waiting',
 
774
            'company_id': production.company_id.id,
 
775
            'pts_id': production.pts_id and production.pts_id.id or False,
 
776
        }
 
777
        move_id = stock_move.create(cr, uid, data, context=context)
 
778
        production.write({'move_created_ids': [(6, 0, [move_id])]}, context=context)
 
779
        return move_id
 
780
 
 
781
    def _make_production_consume_line(self, cr, uid, production_line, parent_move_id, source_location_id=False, context=None):
 
782
        stock_move = self.pool.get('stock.move')
 
783
        production = production_line.production_id
 
784
        # Internal shipment is created for Stockable and Consumer Products
 
785
        if production_line.product_id.type not in ('product', 'consu'):
 
786
            return False
 
787
        destination_location_id = production.product_id.property_stock_production.id
 
788
        if not source_location_id:
 
789
            source_location_id = production.location_src_id.id
 
790
        move_id = stock_move.create(cr, uid, {
 
791
            'name': production.name,
 
792
            'date': production.date_planned,
 
793
            'product_id': production_line.product_id.id,
 
794
            'product_qty': production_line.product_qty,
 
795
            'product_uom': production_line.product_uom.id,
 
796
            'product_uos_qty': production_line.product_uos and production_line.product_uos_qty or False,
 
797
            'product_uos': production_line.product_uos and production_line.product_uos.id or False,
 
798
            'location_id': source_location_id,
 
799
            'location_dest_id': destination_location_id,
 
800
            'move_dest_id': parent_move_id,
 
801
            'state': 'waiting',
 
802
            'company_id': production.company_id.id,
 
803
            'pts_id': production.pts_id and production.pts_id.id or False,
 
804
        })
 
805
        production.write({'move_lines': [(4, move_id)]}, context=context)
 
806
        return move_id
 
807
    
 
808
mrp_production()
 
809
 
 
810
 
 
811
class procurement_order(osv.osv):
 
812
    _inherit = 'procurement.order'
 
813
 
 
814
    _columns = {
 
815
        'pts_id':  fields.many2one('delivery.time', 'Preparation Time', select=True, domain=[('type', '=', 'pts')]),
 
816
        'message': fields.char(_('Latest error'), size=2048, help="Exception occurred while computing procurement orders."),
 
817
    }
 
818
 
 
819
    def make_mo(self, cr, uid, ids, context=None):
 
820
        """ Make Manufacturing(production) order from procurement
 
821
        @return: New created Production Orders procurement wise 
 
822
        """
 
823
        res = {}
 
824
        company = self.pool.get('res.users').browse(cr, uid, uid, context).company_id
 
825
        production_obj = self.pool.get('mrp.production')
 
826
        move_obj = self.pool.get('stock.move')
 
827
        wf_service = netsvc.LocalService("workflow")
 
828
        procurement_obj = self.pool.get('procurement.order')
 
829
        for procurement in procurement_obj.browse(cr, uid, ids, context=context):
 
830
            res_id = procurement.move_id.id
 
831
            newdate = datetime.strptime(procurement.date_planned, '%Y-%m-%d %H:%M:%S') - relativedelta(days=procurement.product_id.produce_delay or 0.0)
 
832
            newdate = newdate - relativedelta(days=company.manufacturing_lead)
 
833
            produce_id = production_obj.create(cr, uid, {
 
834
                'origin': procurement.origin,
 
835
                'product_id': procurement.product_id.id,
 
836
                'product_qty': procurement.product_qty,
 
837
                'product_uom': procurement.product_uom.id,
 
838
                'product_uos_qty': procurement.product_uos and procurement.product_uos_qty or False,
 
839
                'product_uos': procurement.product_uos and procurement.product_uos.id or False,
 
840
                'location_src_id': procurement.location_id.id,
 
841
                'location_dest_id': procurement.location_id.id,
 
842
                'bom_id': procurement.bom_id and procurement.bom_id.id or False,
 
843
                'date_planned': newdate.strftime('%Y-%m-%d %H:%M:%S'),
 
844
                'move_prod_id': res_id,
 
845
                'company_id': procurement.company_id.id,
 
846
                'pts_id': procurement.pts_id and procurement.pts_id.id or context and context.get('force_pts_id', False) or False,
 
847
            })
 
848
            
 
849
            res[procurement.id] = produce_id
 
850
            self.write(cr, uid, [procurement.id], {'state': 'running', 'production_id': produce_id})   
 
851
            bom_result = production_obj.action_compute(cr, uid,
 
852
                    [produce_id], properties=[x.id for x in procurement.property_ids])
 
853
            wf_service.trg_validate(uid, 'mrp.production', produce_id, 'button_confirm', cr)
 
854
            if res_id:
 
855
                move_obj.write(cr, uid, [res_id], {'location_id': procurement.location_id.id})
 
856
        self.production_order_create_note(cr, uid, ids, context=context)
 
857
        return res
 
858
 
 
859
procurement_order()
 
860
 
 
861
 
 
862
class delivery_time(osv.osv):
 
863
    _inherit = 'delivery.time'
 
864
    
 
865
    def search(self, cr, uid, args, offset=0, limit=None, order='name', context=None, count=False):
 
866
        # now = datetime.now()
 
867
        # args.append(('name','>=',datetime.strftime(now,'%y%m%d')))
 
868
        return super(delivery_time, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count)
 
869
 
 
870
delivery_time()
 
871
 
 
872
 
 
873
class delivery_return_type(osv.osv):
 
874
    _name = 'delivery.return.type'
 
875
    
 
876
    _columns = {
 
877
        'name': fields.char('Name', size=64, required=True, select=True),
 
878
        'sequence': fields.integer('Sequence'),
 
879
    }
 
880
    _order = 'sequence'
 
881
delivery_return_type()
 
882
    
 
883
 
 
884
class delivery_return_reason(osv.osv):
 
885
    _name = 'delivery.return.reason'
 
886
    
 
887
    _columns = {
 
888
        'type': fields.many2one('delivery.return.type', 'Type', required=True),
 
889
        'reason': fields.char('Name', size=1024, required=False),
 
890
        'route_line_id': fields.many2one('delivery.route.line', 'Delivery Route Line'),
 
891
    }
 
892
delivery_return_reason()
 
893
 
 
894
 
 
895
class delivery_route(osv.osv):
 
896
    _inherit = 'delivery.route'
 
897
    
 
898
    def _auto_init(self, cr, context=None):
 
899
        super(delivery_route, self)._auto_init(cr, context=context)
 
900
        cr.execute("SELECT table_name FROM information_schema.tables WHERE table_name = 'delivery_scheduler_running'")
 
901
        if not cr.fetchone():
 
902
            cr.execute('CREATE TABLE delivery_scheduler_running (running boolean)')
 
903
            cr.commit()
 
904
            cr.execute('INSERT INTO delivery_scheduler_running (running) VALUES (FALSE)')
 
905
    
 
906
    def set_confirm_cs(self, cr, uid, ids, context=None):
 
907
        self.write(cr, uid, ids, {'confirm_cs': True}, context=context)
 
908
        return True
 
909
    
 
910
delivery_route()
 
911
 
 
912
    
 
913
class delivery_route_line(osv.osv):
 
914
    _inherit = 'delivery.route.line'
 
915
    
 
916
    def search(self, cr, uid, args, offset=0, limit=None, order='dts_name', context=None, count=False):
 
917
        context = context or {}
 
918
        new_order = context.get('sorting', order)
 
919
        return super(delivery_route_line, self).search(cr, uid, args, offset=offset, limit=limit, order=new_order, context=context, count=count)
 
920
    
 
921
    def set_not_vip(self, cr, uid, ids, context=None):
 
922
        return self.write(cr, uid, ids, {'vip':False})
 
923
 
 
924
    def set_vip(self, cr, uid, ids, context=None):
 
925
        return self.write(cr, uid, ids, {'vip':True})
 
926
    
 
927
    def write(self, cr, uid, ids, vals, context=None):
 
928
        if type(ids) != type([]):
 
929
            ids = [ids]
 
930
        context = context or {}
 
931
        if ('route_id' in vals or 'color' in vals or 'sequence' in vals) and not 'force_update' in context:
 
932
            for line in self.browse(cr, uid, ids):
 
933
                if line.state == 'draft':
 
934
                    current_dts = context.get('force_dts_id_kanban', False) or False
 
935
                    # if current_dts:
 
936
                    #    vals.update({'dts_id':current_dts})
 
937
                    if 'route_id' in vals and vals['route_id']:
 
938
                        route_state = self.pool.get('delivery.route').read(cr, uid, [vals['route_id']], ['state', 'name'])
 
939
                        if route_state[0]['state'] != 'draft': 
 
940
                            print('The Route %s is confirmed, you can not add lines to it.' % (route_state[0]['name']))
 
941
                            raise osv.except_osv(_('Error'), _('The Route %s is confirmed, you can not add lines to it.' % (route_state[0]['name'])))
 
942
                        
 
943
                        elif 'update_color' in context and context['update_color'] == 1:
 
944
                            current_color = str(line.color)
 
945
                            cr.execute("SELECT color FROM (select count(*) as cpt, color as color from delivery_route_line WHERE route_id=" + str(vals['route_id']) + " AND color != " + current_color + " AND color IS NOT null AND color > 0 GROUP BY color) t ORDER BY cpt DESC")
 
946
                            color = cr.fetchone()
 
947
                            if color and color[0]:
 
948
                                vals.update({'color':color[0]})
 
949
                            
 
950
                            elif current_dts:
 
951
                                cr.execute("SELECT DISTINCT color FROM delivery_route_line WHERE (dts_id=" + str(current_dts) + " OR id = " + str(line.id) + ") AND color IS NOT null AND color > 0")
 
952
                                colors = map(lambda x: x[0], cr.fetchall())
 
953
                                color = False
 
954
                                
 
955
                                for idx in range(1, 22):
 
956
                                    if idx not in colors and not color and idx != current_color:
 
957
                                        color = idx
 
958
                                if color:
 
959
                                    vals.update({'color':color})
 
960
                                else:
 
961
                                    print('No more Route available for the DTS %s.' % (line.dts_id.name))
 
962
                                    raise osv.except_osv(_('Error'), _('No more Route available for the DTS %s.' % (line.dts_id.name)))
 
963
                    
 
964
                    elif 'check4color' in context and context['check4color'] and 'color' in vals:
 
965
                        cr.execute("SELECT DISTINCT route_id FROM delivery_route_line WHERE dts_id=" + str(line.dts_id.id) + " AND color = " + str(vals['color']) + " AND color > 0 AND state not in ('draft','cancel')")
 
966
                        route_line_ids = map(lambda x: x[0], cr.fetchall())
 
967
                        if route_line_ids:
 
968
                            print('The Route Line %s (origin: %s) can not be put in a confirmed Route (%s).' % (line.picking_id.name, line.picking_id.origin, route_line_ids))
 
969
                            raise osv.except_osv(_('Error'), _('The Route Line %s (origin: %s) can not be put in a confirmed Route (%s).' % (line.picking_id.name, line.picking_id.origin, route_line_ids)))
 
970
                else:
 
971
                    print('The Route Line %s (origin: %s) is confirmed. You can not change it.' % (line.picking_id.name, line.picking_id.origin))
 
972
                    raise osv.except_osv(_('Error'), _('The Route Line %s (origin: %s) is confirmed. You can not change it.' % (line.picking_id.name, line.picking_id.origin)))
 
973
        return super(delivery_route_line, self).write(cr, uid, ids, vals, context=context)
 
974
    
 
975
    
 
976
    def set_van(self, cr, uid, ids, van=0, context=None):
 
977
        self.write(cr, uid, ids, {'color': van}, context=context)
 
978
        return True
 
979
    def set_van_0(self, cr, uid, ids, context=None):
 
980
        return self.set_van(cr, uid, ids, 0, context)
 
981
    def set_van_1(self, cr, uid, ids, context=None):
 
982
        return self.set_van(cr, uid, ids, 1, context)
 
983
    def set_van_2(self, cr, uid, ids, context=None):
 
984
        return self.set_van(cr, uid, ids, 2, context)
 
985
    def set_van_3(self, cr, uid, ids, context=None):
 
986
        return self.set_van(cr, uid, ids, 3, context)
 
987
    def set_van_4(self, cr, uid, ids, context=None):
 
988
        return self.set_van(cr, uid, ids, 4, context)
 
989
    def set_van_5(self, cr, uid, ids, context=None):
 
990
        return self.set_van(cr, uid, ids, 5, context)
 
991
    def set_van_6(self, cr, uid, ids, context=None):
 
992
        return self.set_van(cr, uid, ids, 6, context)
 
993
    def set_van_7(self, cr, uid, ids, context=None):
 
994
        return self.set_van(cr, uid, ids, 7, context)
 
995
    def set_van_8(self, cr, uid, ids, context=None):
 
996
        return self.set_van(cr, uid, ids, 8, context)
 
997
    def set_van_9(self, cr, uid, ids, context=None):
 
998
        return self.set_van(cr, uid, ids, 9, context)
 
999
    
 
1000
    
 
1001
    def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
 
1002
        context = context or {}
 
1003
        if 'view_name' in context and view_type == 'kanban':
 
1004
            view_ids = self.pool.get('ir.ui.view').search(cr, uid, [('model', '=', 'delivery.route.line'), ('name', '=', context['view_name'])], context=context)
 
1005
            if view_ids:
 
1006
                view_id = view_ids[0]
 
1007
                del context['view_name']
 
1008
        return super(delivery_route_line, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
 
1009
    
 
1010
    
 
1011
    def _get_neighborhood(self, cr, uid, ids, fields, args, context=None):
 
1012
        res = {}
 
1013
        for route in self.browse(cr, uid, ids):
 
1014
            # res[route.id] = route.picking_id and route.picking_id.sale_id and route.picking_id.sale_id.deliver_zone or route.picking_id and route.picking_id.partner_id and route.picking_id.partner_id.vm_district or False
 
1015
            res[route.id] = route.picking_id and route.picking_id.partner_id and route.picking_id.partner_id.deliver_zone or False
 
1016
        return res
 
1017
    
 
1018
    def _get_dts_id(self, cr, uid, ids, fields, args, context=None):
 
1019
        context = context or {}
 
1020
        res = {}
 
1021
        for route in self.browse(cr, uid, ids):
 
1022
            if route.state in ['draft'] and context.get('set_dts', True):
 
1023
                res[route.id] = route.picking_id and route.picking_id.pts_id and route.picking_id.pts_id.dts_id and route.picking_id.pts_id.dts_id.id or False
 
1024
            else:
 
1025
                res[route.id] = route.dts_id and route.dts_id.id or False
 
1026
        return res
 
1027
    
 
1028
    def _get_dts_name(self, cr, uid, ids, fields, args, context=None):
 
1029
        context = context or {}
 
1030
        res = {}
 
1031
        for route in self.browse(cr, uid, ids):
 
1032
            if route.state in ['draft'] and context.get('set_dts', True):
 
1033
                res[route.id] = route.picking_id and route.picking_id.pts_id and route.picking_id.pts_id.dts_id and route.picking_id.pts_id.dts_id.name or 'n/a'
 
1034
            else:
 
1035
                res[route.id] = route.dts_name or 'n/a'
 
1036
        return res
 
1037
    
 
1038
    def _get_special_time(self, cr, uid, ids, fields, args, context=None):
 
1039
        tz = pytz.timezone('Asia/Shanghai')
 
1040
        result = {}
 
1041
        for route in self.browse(cr, uid, ids):
 
1042
            res = {}
 
1043
            customer_date = ''
 
1044
            route_dts_id = route.dts_id and route.dts_id.id
 
1045
            so_dts_id    = route.picking_id.sale_id.dts_id and route.picking_id.sale_id.dts_id.id
 
1046
            
 
1047
            if so_dts_id and route_dts_id != so_dts_id:
 
1048
                customer_date = route.picking_id.sale_id.dts_id.name
 
1049
            
 
1050
            elif route.picking_id.sale_id:
 
1051
                date_start = route.picking_id.sale_id.start_date or False
 
1052
                date_end = route.picking_id.sale_id.end_date or False
 
1053
                
 
1054
                if date_start:
 
1055
                    date_start = datetime.strptime(date_start, '%Y-%m-%d %H:%M:%S')            
 
1056
                    date_start = pytz.utc.localize(date_start).astimezone(tz)
 
1057
                    
 
1058
                    customer_date = datetime.strftime(date_start, '%H:%M')
 
1059
                    #LY if customer_date is 00:00, no special time. 
 
1060
                    if customer_date != '00:00':    
 
1061
                        if date_end:
 
1062
                            date_end = datetime.strptime(date_end, '%Y-%m-%d %H:%M:%S')            
 
1063
                            date_end = pytz.utc.localize(date_end).astimezone(tz)
 
1064
                            customer_date += ' - '
 
1065
                            customer_date += datetime.strftime(date_end, '%H:%M')
 
1066
            res['customer_date'] = customer_date or ' '
 
1067
            result[route.id] = res
 
1068
        return result
 
1069
    
 
1070
    def _get_street(self, cr, uid, ids, fields, args, context=None):
 
1071
        res = {}
 
1072
        for route in self.browse(cr, uid, ids):
 
1073
            res[route.id] = route.picking_id and route.picking_id.partner_id and route.picking_id.partner_id.street or ' n/a'
 
1074
        return res
 
1075
    
 
1076
    def _route_to_update_after_picking_change(self, cr, uid, ids, fields=None, arg=None, context=None):
 
1077
        if type(ids) != type([]):
 
1078
            ids = [ids]
 
1079
        return self.pool.get('delivery.route.line').search(cr, uid, [('picking_id', 'in', ids)]) or []
 
1080
    
 
1081
    def _route_to_update_after_dts_change(self, cr, uid, ids, fields=None, arg=None, context=None):
 
1082
        if type(ids) != type([]):
 
1083
            ids = [ids]
 
1084
        picking_ids = self.pool.get('stock.picking').search(cr, uid, [('dts_id', 'in', ids)]) or []
 
1085
        return self.pool.get('delivery.route.line')._route_to_update_after_picking_change(cr, uid, picking_ids, None, None, context=context)
 
1086
    
 
1087
    def _route_to_update_after_so_change(self, cr, uid, ids, fields=None, arg=None, context=None):
 
1088
        if type(ids) != type([]):
 
1089
            ids = [ids]
 
1090
        return self.pool.get('delivery.route.line').search(cr, uid, [('sale_order_id', 'in', ids)]) or []
 
1091
    
 
1092
    def _route_to_update_after_po_change(self, cr, uid, ids, fields=None, arg=None, context=None):
 
1093
        if type(ids) != type([]):
 
1094
            ids = [ids]
 
1095
        return self.pool.get('delivery.route.line').search(cr, uid, [('purchase_id', 'in', ids)]) or []
 
1096
    
 
1097
    def _route_to_update_after_partner_change(self, cr, uid, ids, fields=None, arg=None, context=None):
 
1098
        if type(ids) != type([]):
 
1099
            ids = [ids]
 
1100
        picking_ids = self.pool.get('stock.picking').search(cr, uid, [('partner_id', 'in', ids)]) or []
 
1101
        return self.pool.get('delivery.route.line')._route_to_update_after_picking_change(cr, uid, picking_ids, None, None, context=context)
 
1102
    
 
1103
    _store_dts = {
 
1104
        'delivery.route.line': (lambda self, cr, uid, ids, context: ids, ['picking_id'], 10),
 
1105
        'stock.picking': (_route_to_update_after_picking_change, ['pts_id'], 10),
 
1106
    }
 
1107
    _store_dts_name = {
 
1108
        'delivery.route.line': (lambda self, cr, uid, ids, context: ids, ['picking_id'], 10),
 
1109
        'stock.picking': (_route_to_update_after_picking_change, ['pts_id'], 10),
 
1110
        'delivery.time': (_route_to_update_after_dts_change, ['name'], 10),
 
1111
    }
 
1112
    _store_special_time = {
 
1113
        'delivery.route.line': (lambda self, cr, uid, ids, context: ids, ['picking_id'], 12),
 
1114
        'stock.picking': (_route_to_update_after_picking_change, ['sale_id','dts_id'], 12),
 
1115
        'sale.order': (_route_to_update_after_so_change, ['dts_id'], 12),
 
1116
    }
 
1117
    _store_neighborhood = {
 
1118
        'delivery.route.line': (lambda self, cr, uid, ids, context: ids, ['picking_id'], 10),
 
1119
        'stock.picking': (_route_to_update_after_picking_change, ['partner_id'], 10),
 
1120
        'res.partner': (_route_to_update_after_partner_change, ['vm_district'], 10),
 
1121
    }
 
1122
    
 
1123
    _store_amount = {
 
1124
        'delivery.route.line': (lambda self, cr, uid, ids, context: ids, ['picking_id', 'adjustment'], 10),
 
1125
        'stock.picking': (_route_to_update_after_picking_change, ['sale_id', 'purchase_id', 'origin'], 10),
 
1126
        'purchase.order': (_route_to_update_after_po_change, ['amount_total'], 10),
 
1127
    }
 
1128
    
 
1129
    _store_street = {
 
1130
        'delivery.route.line': (lambda self,cr,uid,ids,context: ids,['picking_id'],10), 
 
1131
        'stock.picking': (_route_to_update_after_picking_change, ['partner_id'], 10),
 
1132
    }
 
1133
    
 
1134
    def _get_amount(self, cr, uid, ids, fields, args, context=None):
 
1135
        result = {}
 
1136
        for route in self.browse(cr, uid, ids):
 
1137
            res = {}
 
1138
            res['amount_total'] = 0.0
 
1139
            res['amount_unpaid'] = 0.0
 
1140
            res['to_be_received'] = 0.0
 
1141
            
 
1142
            if route.picking_id and route.picking_id.sale_id:
 
1143
                res['amount_total'] = route.picking_id.sale_id.amount_total
 
1144
                res['amount_unpaid'] = res['amount_total']
 
1145
                res['to_be_received'] = res['amount_unpaid'] + route.adjustment
 
1146
            elif route.picking_id and route.picking_id.purchase_id:
 
1147
                res['amount_total'] = route.picking_id.purchase_id.amount_total
 
1148
                res['amount_unpaid'] = 0.0
 
1149
                res['to_be_received'] = res['amount_unpaid'] + route.adjustment
 
1150
            result[route.id] = res
 
1151
        return result
 
1152
    
 
1153
    _columns = {
 
1154
        'dts_id': fields.function(_get_dts_id, type='many2one', obj='delivery.time', store=_store_dts, string='Delivery Time', _classic_read=True),
 
1155
        'dts_name': fields.function(_get_dts_name, type='char', size=124, store=_store_dts_name, string='Delivery Time'),
 
1156
        'return_reasons': fields.one2many('delivery.return.reason', 'route_line_id', 'Return Reasons', readonly=False),
 
1157
        'delivered_cpt': fields.related('picking_id', 'delivered_cpt', type='integer', string='Delivered x times', readonly=True),
 
1158
        'customer_date': fields.function(_get_special_time, type='char', size=64, store=_store_special_time, multi="special_time", string=_('Customer Delivery Time')),
 
1159
        'neighborhood': fields.function(_get_neighborhood, type='char', size=255, store=_store_neighborhood, string=_('Neighborhood')),
 
1160
        'street': fields.function(_get_street, type='char', size=128, store=_store_street, string='Street'),
 
1161
        'vip': fields.boolean('is VIP ?'),
 
1162
        'amount_total':  fields.function(_get_amount, type='float', multi="amount", store=_store_amount, string='Total'),
 
1163
        'amount_unpaid': fields.function(_get_amount, type='float', multi="amount", store=_store_amount, string='Unpaid'),
 
1164
        'adjustment': fields.float('Adjustment'),
 
1165
        'cs_remark': fields.text('CS Remark'),
 
1166
        'to_be_received': fields.function(_get_amount, type='float', multi="amount", store=_store_amount, string='To be Received'),
 
1167
        'amount_received': fields.float('Received'),
 
1168
        'account_checked': fields.boolean('Checked'),
 
1169
        'account_remark': fields.text('Remark Accounting'),
 
1170
    }
 
1171
delivery_route_line()
 
1172
 
 
1173
 
 
1174
class res_users(osv.osv):
 
1175
    _inherit = 'res.users'
 
1176
    
 
1177
    _columns = {
 
1178
        'dts_id': fields.many2one('delivery.time', 'Last Used Delivery Time'),
 
1179
    }
 
1180
res_users()
 
1181
 
 
1182
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: