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

« back to all changes in this revision

Viewing changes to msf_cross_docking/cross_docking.py

UF-358 [ADD] Initial creation : backup of this day

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) 2012 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
23
 
from osv import fields
24
 
from tools.translate import _
25
 
import logging
26
 
import tools
27
 
from os import path
28
 
 
29
 
 
30
 
class purchase_order(osv.osv):
31
 
    '''
32
 
    Enables the option cross docking
33
 
    '''
34
 
    _name = 'purchase.order'
35
 
    _inherit = 'purchase.order'
36
 
 
37
 
    _columns = {
38
 
        'cross_docking_ok': fields.boolean('Cross docking'),
39
 
        'location_id': fields.many2one('stock.location', 'Destination', required=True, domain=[('usage', '<>', 'view')],
40
 
        help="""This location is set according to the Warehouse selected, or according to the option 'Cross docking'
41
 
        or freely if you do not select 'Warehouse'.But if the 'Order category' is set to 'Transport' or 'Service',
42
 
        you cannot have an other location than 'Service'"""),
43
 
    }
44
 
 
45
 
    _defaults = {
46
 
        'cross_docking_ok': False,
47
 
    }
48
 
 
49
 
    def onchange_internal_type(self, cr, uid, ids, order_type, partner_id, categ, dest_partner_id=False, warehouse_id=False, delivery_requested_date=False):
50
 
        '''
51
 
        Changes destination location
52
 
        '''
53
 
        res = super(purchase_order, self).onchange_internal_type(cr, uid, ids, order_type, partner_id, categ, dest_partner_id, warehouse_id, delivery_requested_date)
54
 
        if order_type == 'direct':
55
 
            location_id = self.onchange_cross_docking_ok(cr, uid, ids, False, warehouse_id, categ)['value']['location_id']
56
 
            if 'value' in res:
57
 
                res['value'].update({'location_id': location_id})
58
 
            else:
59
 
                res.update({'value': {'location_id': location_id}})
60
 
        return res
61
 
 
62
 
    def onchange_cross_docking_ok(self, cr, uid, ids, cross_docking_ok, warehouse_id, categ, context=None):
63
 
        """ Finds location id for changed cross_docking_ok.
64
 
        @param cross_docking_ok: Changed value of cross_docking_ok.
65
 
        @return: Dictionary of values.
66
 
        """
67
 
        if isinstance(ids, (int, long)):
68
 
            ids = [ids]
69
 
        if cross_docking_ok:
70
 
            c_dock_loc = self.pool.get('stock.location').get_cross_docking_location(cr, uid)
71
 
        else:
72
 
            warehouse_obj = self.pool.get('stock.warehouse')
73
 
            if not warehouse_id:
74
 
                warehouse_ids = warehouse_obj.search(cr, uid, [], limit=1)
75
 
                if not warehouse_ids:
76
 
                    return {'warning': {'title': _('Error !'), 'message': _('No Warehouse defined !')}, 'value': {'location_id': False}}
77
 
                warehouse_id = warehouse_ids[0]
78
 
            if categ not in ('service', 'transport'):
79
 
                c_dock_loc = warehouse_obj.read(cr, uid, [warehouse_id], ['lot_input_id'])[0]['lot_input_id'][0]
80
 
            else:
81
 
                c_dock_loc = self.pool.get('stock.location').get_service_location(cr, uid)
82
 
        return {'value': {'location_id': c_dock_loc}}
83
 
 
84
 
    def onchange_location_id(self, cr, uid, ids, location_id, categ, context=None):
85
 
        """ If location_id == cross docking we tick the box "cross docking".
86
 
        @param location_id: Changed value of location_id.
87
 
        @return: Dictionary of values.
88
 
        """
89
 
        if isinstance(ids, (int, long)):
90
 
            ids = [ids]
91
 
        stock_loc_obj = self.pool.get('stock.location')
92
 
        res = {}
93
 
        res['value'] = {}
94
 
        if location_id == stock_loc_obj.get_cross_docking_location(cr, uid) and categ not in ['service', 'transport']:
95
 
            cross_docking_ok = True
96
 
        elif location_id != stock_loc_obj.get_cross_docking_location(cr, uid):
97
 
            cross_docking_ok = False
98
 
        elif location_id != stock_loc_obj.get_service_location(cr, uid) and categ in ['service', 'transport']:
99
 
            return {'warning': {'title': _('Error !'), 'message': _("""
100
 
            If the 'Order Category' is 'Service' or 'Transport', you cannot have an other location than 'Service'
101
 
            """)}, 'value': {'location_id': stock_loc_obj.get_service_location(cr, uid)}}
102
 
        res['value']['cross_docking_ok'] = cross_docking_ok
103
 
        return res
104
 
 
105
 
    def onchange_warehouse_id(self, cr, uid, ids,  warehouse_id, order_type, dest_address_id):
106
 
        """ Set cross_docking_ok to False when we change warehouse.
107
 
        @param warehouse_id: Changed id of warehouse.
108
 
        @return: Dictionary of values.
109
 
        """
110
 
        res = super(purchase_order, self).onchange_warehouse_id(cr, uid, ids,  warehouse_id, order_type, dest_address_id)
111
 
        if warehouse_id:
112
 
            res['value'].update({'cross_docking_ok': False})
113
 
        return res
114
 
 
115
 
    def onchange_categ(self, cr, uid, ids, categ, warehouse_id, cross_docking_ok, location_id, context=None):
116
 
        """ Sets cross_docking to False if the categ is service or transport.
117
 
        @param categ: Changed value of categ.
118
 
        @return: Dictionary of values.
119
 
        """
120
 
        if isinstance(ids, (int, long)):
121
 
            ids = [ids]
122
 
        stock_loc_obj = self.pool.get('stock.location')
123
 
        warehouse_obj = self.pool.get('stock.warehouse')
124
 
        value = {}
125
 
        message = {}
126
 
        setup = self.pool.get('unifield.setup.configuration').get_config(cr, uid)
127
 
        cross_loc = False
128
 
        if setup.allocation_setup != 'unallocated':
129
 
            cross_loc = stock_loc_obj.get_cross_docking_location(cr, uid)
130
 
        service_loc = stock_loc_obj.get_service_location(cr, uid)
131
 
        if cross_docking_ok:
132
 
            value = {'location_id': cross_loc}
133
 
        elif categ in ['service', 'transport']:
134
 
            value = {'location_id': service_loc}
135
 
        elif location_id == service_loc or (setup.allocation_setup != 'unallocated' and location_id == cross_loc):
136
 
            if warehouse_id:
137
 
                value = {'location_id': warehouse_obj.read(cr, uid, [warehouse_id], ['lot_input_id'])[0]['lot_input_id'][0]}
138
 
            else:
139
 
                value = {'location_id': False}
140
 
        
141
 
        if ids and categ in ['service', 'transport']:
142
 
            # Avoid selection of non-service producs on Service PO
143
 
            category = categ=='service' and 'service_recep' or 'transport'
144
 
            transport_cat = ''
145
 
            if category == 'transport':
146
 
                transport_cat = 'OR p.transport_ok = False'
147
 
            cr.execute('''SELECT p.default_code AS default_code, t.name AS name
148
 
                          FROM purchase_order_line l
149
 
                            LEFT JOIN product_product p ON l.product_id = p.id
150
 
                            LEFT JOIN product_template t ON p.product_tmpl_id = t.id
151
 
                            LEFT JOIN purchase_order po ON l.order_id = po.id
152
 
                          WHERE (t.type != 'service_recep' %s) AND po.id in (%s) LIMIT 1''' % (transport_cat, ','.join(str(x) for x in ids)))
153
 
            res = cr.fetchall()
154
 
            if res:
155
 
                cat_name = categ=='service' and 'Service' or 'Transport'
156
 
                message.update({'title': _('Warning'),
157
 
                                'message': _('The product [%s] %s is not a \'%s\' product. You can purchase only \'%s\' products on a \'%s\' purchase order. Please remove this line before saving.') % (res[0][0], res[0][1], cat_name, cat_name, cat_name)})
158
 
                
159
 
        return {'value': value, 'warning': message}
160
 
 
161
 
    def write(self, cr, uid, ids, vals, context=None):
162
 
        if isinstance(ids, (int, long)):
163
 
            ids = [ids]
164
 
        stock_loc_obj = self.pool.get('stock.location')
165
 
        if 'order_type' in vals and vals['order_type'] == 'direct':
166
 
            vals.update({'cross_docking_ok': False})
167
 
        if 'cross_docking_ok' in vals and vals['cross_docking_ok']:
168
 
            vals.update({'location_id': stock_loc_obj.get_cross_docking_location(cr, uid)})
169
 
        elif 'categ' in vals and vals['categ'] in ['service', 'transport']:
170
 
            vals.update({'cross_docking_ok': False, 'location_id': stock_loc_obj.get_service_location(cr, uid)})
171
 
        return super(purchase_order, self).write(cr, uid, ids, vals, context=context)
172
 
 
173
 
    def create(self, cr, uid, vals, context=None):
174
 
        stock_loc_obj = self.pool.get('stock.location')
175
 
        if vals.get('order_type') == 'direct':
176
 
            vals.update({'cross_docking_ok': False})
177
 
        if vals.get('cross_docking_ok'):
178
 
            vals.update({'location_id': stock_loc_obj.get_cross_docking_location(cr, uid)})
179
 
        elif 'categ' in vals and vals['categ'] in ['service', 'transport']:
180
 
            vals.update({'cross_docking_ok': False, 'location_id': stock_loc_obj.get_service_location(cr, uid)})
181
 
        return super(purchase_order, self).create(cr, uid, vals, context=context)
182
 
 
183
 
    def _check_cross_docking(self, cr, uid, ids, context=None):
184
 
        """
185
 
        Check that if you select cross docking, you do not have an other location than cross docking
186
 
        """
187
 
        if isinstance(ids, (int, long)):
188
 
            ids = [ids]
189
 
        if context is None:
190
 
            context = {}
191
 
        stock_loc_obj = self.pool.get('stock.location')
192
 
        setup = self.pool.get('unifield.setup.configuration').get_config(cr, uid)
193
 
        for purchase in self.browse(cr, uid, ids, context=context):
194
 
            if purchase.cross_docking_ok:
195
 
                if setup.allocation_setup == 'unallocated':
196
 
                    raise osv.except_osv(_('Error'), _("""The Allocated stocks setup is set to Unallocated.
197
 
In this configuration, you cannot made a Cross-docking Purchase order."""))
198
 
                cross_docking_location = stock_loc_obj.get_cross_docking_location(cr, uid)
199
 
                if purchase.location_id.id != cross_docking_location:
200
 
                    raise osv.except_osv(_('Warning !'), _("""If you tick the box \"cross docking\",
201
 
you cannot have an other location than \"Cross docking\""""))
202
 
                else:
203
 
                    return True
204
 
            else:
205
 
                return True
206
 
 
207
 
    _constraints = [
208
 
        (_check_cross_docking, 'If you tick the box \"cross docking\", you cannot have an other location than \"Cross docking\"', ['location_id']),
209
 
    ]
210
 
 
211
 
purchase_order()
212
 
 
213
 
 
214
 
class procurement_order(osv.osv):
215
 
 
216
 
    _inherit = 'procurement.order'
217
 
 
218
 
    def po_values_hook(self, cr, uid, ids, context=None, *args, **kwargs):
219
 
        '''
220
 
        When you run the scheduler and you have a sale order line with type = make_to_order,
221
 
        we modify the location_id to set 'cross docking' of the purchase order created in mirror
222
 
        But if the sale_order is an Internal Request we do want "Cross docking" but "Input" as location_id
223
 
        '''
224
 
        if context is None:
225
 
            context = {}
226
 
        if isinstance(ids, (int, long)):
227
 
            ids = [ids]
228
 
        stock_loc_obj = self.pool.get('stock.location')
229
 
        sol_obj = self.pool.get('sale.order.line')
230
 
        procurement = kwargs['procurement']
231
 
        setup = self.pool.get('unifield.setup.configuration').get_config(cr, uid)
232
 
        values = super(procurement_order, self).po_values_hook(cr, uid, ids, context=context, *args, **kwargs)
233
 
        sol_ids = sol_obj.search(cr, uid, [('procurement_id', '=', procurement.id)], context=context)
234
 
        if len(sol_ids) and setup.allocation_setup != 'unallocated':
235
 
            if not sol_obj.browse(cr, uid, sol_ids, context=context)[0].order_id.procurement_request:
236
 
                values.update({'cross_docking_ok': True, 'location_id': stock_loc_obj.get_cross_docking_location(cr, uid)})
237
 
        return values
238
 
 
239
 
procurement_order()
240
 
 
241
 
 
242
 
class stock_picking(osv.osv):
243
 
    '''
244
 
    do_partial(=function which is originally called from delivery_mechanism) modification
245
 
    for the selection of the LOCATION for IN (incoming shipment) and OUT (delivery orders)
246
 
    '''
247
 
    _inherit = 'stock.picking'
248
 
 
249
 
    def init(self, cr):
250
 
        """
251
 
        Load msf_cross_docking_data.xml before self
252
 
        """
253
 
        if hasattr(super(stock_picking, self), 'init'):
254
 
            super(stock_picking, self).init(cr)
255
 
        logging.getLogger('init').info('HOOK: module msf_cross_docking: loading data/msf_msf_cross_docking_data.xml')
256
 
        pathname = path.join('msf_cross_docking', 'data/msf_cross_docking_data.xml')
257
 
        file = tools.file_open(pathname)
258
 
        tools.convert_xml_import(cr, 'msf_cross_docking', file, {}, mode='init', noupdate=False)
259
 
 
260
 
    def _get_allocation_setup(self, cr, uid, ids, field_name, args, context=None):
261
 
        '''
262
 
        Returns the Unifield configuration value
263
 
        '''
264
 
        res = {}
265
 
        setup = self.pool.get('unifield.setup.configuration').get_config(cr, uid)
266
 
        for order in ids:
267
 
            res[order] = setup.allocation_setup
268
 
        return res
269
 
 
270
 
    _columns = {
271
 
        'cross_docking_ok': fields.boolean('Cross docking'),
272
 
        'allocation_setup': fields.function(_get_allocation_setup, type='selection',
273
 
                                            selection=[('allocated', 'Allocated'),
274
 
                                                       ('unallocated', 'Unallocated'),
275
 
                                                       ('mixed', 'Mixed')], string='Allocated setup', method=True, store=False),
276
 
    }
277
 
 
278
 
    def default_get(self, cr, uid, fields, context=None):
279
 
        '''
280
 
        Fill the unallocated_ok field according to Unifield setup
281
 
        '''
282
 
        res = super(stock_picking, self).default_get(cr, uid, fields, context=context)
283
 
        setup = self.pool.get('unifield.setup.configuration').get_config(cr, uid)
284
 
        res.update({'allocation_setup': setup.allocation_setup})
285
 
        return res
286
 
 
287
 
    def write(self, cr, uid, ids, vals, context=None):
288
 
        """
289
 
        Here we check if all stock move are in stock or in cross docking
290
 
        """
291
 
        if context is None:
292
 
            context = {}
293
 
        if isinstance(ids, (int, long)):
294
 
            ids = [ids]
295
 
        move_obj = self.pool.get('stock.move')
296
 
        pick_obj = self.pool.get('stock.picking')
297
 
        for pick in pick_obj.browse(cr, uid, ids, context=context):
298
 
            move_lines = pick.move_lines
299
 
            if len(move_lines) >= 1:
300
 
                for move in move_lines:
301
 
                    move_ids = move.id
302
 
                    for move in move_obj.browse(cr, uid, [move_ids], context=context):
303
 
                        if move.move_cross_docking_ok:
304
 
                            vals.update({'cross_docking_ok': True, })
305
 
                        elif not move.move_cross_docking_ok:
306
 
                            vals.update({'cross_docking_ok': False, })
307
 
        return super(stock_picking, self).write(cr, uid, ids, vals, context=context)
308
 
 
309
 
    def button_cross_docking_all(self, cr, uid, ids, context=None):
310
 
        """
311
 
        set all stock moves with the source location to 'cross docking'
312
 
        """
313
 
        if context is None:
314
 
            context = {}
315
 
        if isinstance(ids, (int, long)):
316
 
            ids = [ids]
317
 
        move_obj = self.pool.get('stock.move')
318
 
        pick_obj = self.pool.get('stock.picking')
319
 
        # Check the allocation setup
320
 
        setup = self.pool.get('unifield.setup.configuration').get_config(cr, uid)
321
 
        if setup.allocation_setup == 'unallocated':
322
 
            raise osv.except_osv(_('Error'), _("""You cannot made moves from/to Cross-docking
323
 
locations when the Allocated stocks configuration is set to \'Unallocated\'."""))
324
 
        cross_docking_location = self.pool.get('stock.location').get_cross_docking_location(cr, uid)
325
 
        for pick in pick_obj.browse(cr, uid, ids, context=context):
326
 
            move_lines = pick.move_lines
327
 
            if len(move_lines) >= 1:
328
 
                for move in move_lines:
329
 
                    move_ids = move.id
330
 
                    for move in move_obj.browse(cr, uid, [move_ids], context=context):
331
 
                        # Don't change done stock moves
332
 
                        if move.state != 'done':
333
 
                            move_obj.write(cr, uid, [move_ids], {'location_id': cross_docking_location,
334
 
                                                                 'move_cross_docking_ok': True}, context=context)
335
 
                self.write(cr, uid, ids, {'cross_docking_ok': True}, context=context)
336
 
            else:
337
 
                raise osv.except_osv(_('Warning !'), _('Please, enter some stock moves before changing the source location to CROSS DOCKING'))
338
 
        # we check availability : cancel then check
339
 
        self.cancel_assign(cr, uid, ids, context)
340
 
        self.action_assign(cr, uid, ids, context)
341
 
        return False
342
 
 
343
 
    def button_stock_all(self, cr, uid, ids, context=None):
344
 
        """
345
 
        set all stock move with the source location to 'stock'
346
 
        """
347
 
        if context is None:
348
 
            context = {}
349
 
        if isinstance(ids, (int, long)):
350
 
            ids = [ids]
351
 
        obj_data = self.pool.get('ir.model.data')
352
 
        move_obj = self.pool.get('stock.move')
353
 
        pick_obj = self.pool.get('stock.picking')
354
 
        for pick in pick_obj.browse(cr, uid, ids, context=context):
355
 
            move_lines = pick.move_lines
356
 
            if len(move_lines) >= 1:
357
 
                for move in move_lines:
358
 
                    move_ids = move.id
359
 
                    for move in move_obj.browse(cr, uid, [move_ids], context=context):
360
 
                        if move.state != 'done':
361
 
                            '''
362
 
                            Specific rules for non-stockable products:
363
 
                               * if the move is an outgoing delivery, picked them from cross-docking
364
 
                               * else picked them from the non-stockable location
365
 
                            '''
366
 
                            if move.product_id.type in ('consu', 'service_recep'):
367
 
                                if move.picking_id.type == 'out':
368
 
                                    id_loc_s = obj_data.get_object_reference(cr, uid, 'msf_cross_docking', 'stock_location_cross_docking')[1]
369
 
                                elif move.product_id.type == 'consu':
370
 
                                    id_loc_s = obj_data.get_object_reference(cr, uid, 'stock_override', 'stock_location_non_stockable')[1]
371
 
                                else:
372
 
                                    id_loc_s = self.pool.get('stock.location').get_service_location(cr, uid)
373
 
                                move_obj.write(cr, uid, [move_ids], {'location_id': id_loc_s, 'move_cross_docking_ok': False}, context=context)
374
 
                            else:
375
 
                                move_obj.write(cr, uid, [move_ids], {'location_id': pick.warehouse_id.lot_stock_id.id,
376
 
                                                                     'move_cross_docking_ok': False}, context=context)
377
 
                self.write(cr, uid, ids, {'cross_docking_ok': False}, context=context)
378
 
            else:
379
 
                raise osv.except_osv(_('Warning !'), _('Please, enter some stock moves before changing the source location to STOCK'))
380
 
        # we check availability : cancel then check
381
 
        self.cancel_assign(cr, uid, ids, context)
382
 
        self.action_assign(cr, uid, ids, context)
383
 
        return False
384
 
 
385
 
    def _do_incoming_shipment_first_hook(self, cr, uid, ids, context=None, *args, **kwargs):
386
 
        '''
387
 
        This hook refers to delivery_mechanism>delivery_mechanism.py>_do_incoming_shipment.
388
 
        It updates the location_dest_id (to cross docking or to stock)
389
 
        of selected stock moves when the linked 'incoming shipment' is validated
390
 
        -> only related to 'in' type stock.picking
391
 
        '''
392
 
        values = super(stock_picking, self)._do_incoming_shipment_first_hook(cr, uid, ids, context=context, *args, **kwargs)
393
 
        assert values is not None, 'missing values'
394
 
        if context is None:
395
 
            context = {}
396
 
        if isinstance(ids, (int, long)):
397
 
            ids = [ids]
398
 
        # take ids of the wizard from the context.
399
 
        # NB: the wizard_ids is created in delivery_mechanism>delivery_mecanism.py> in the method "_stock_picking_action_process_hook"
400
 
        wiz_ids = context.get('wizard_ids')
401
 
        res = {}
402
 
        if not wiz_ids:
403
 
            return res
404
 
# ------ check the allocation setup ------------------------------------------------------------------------------
405
 
        setup = self.pool.get('unifield.setup.configuration').get_config(cr, uid)
406
 
 
407
 
# ------ referring to locations 'cross docking' and 'stock' ------------------------------------------------------
408
 
        obj_data = self.pool.get('ir.model.data')
409
 
        if setup.allocation_setup != 'unallocated':
410
 
            cross_docking_location = self.pool.get('stock.location').get_cross_docking_location(cr, uid)
411
 
        stock_location_input = obj_data.get_object_reference(cr, uid, 'msf_cross_docking', 'stock_location_input')[1]
412
 
        stock_location_service = self.pool.get('stock.location').get_service_location(cr, uid)
413
 
        stock_location_non_stockable = self.pool.get('stock.location').search(cr, uid, [('non_stockable_ok', '=', True)])
414
 
        if stock_location_non_stockable:
415
 
            stock_location_non_stockable = stock_location_non_stockable[0]
416
 
# ----------------------------------------------------------------------------------------------------------------
417
 
        partial_picking_obj = self.pool.get('stock.partial.picking')
418
 
        # We browse over the wizard (stock.partial.picking)
419
 
        for var in partial_picking_obj.browse(cr, uid, wiz_ids, context=context):
420
 
            """For incoming shipment """
421
 
            # we check the dest_type for INCOMING shipment (and not the source_type which is reserved for OUTGOING shipment)
422
 
            if var.dest_type == 'to_cross_docking':
423
 
                if setup.allocation_setup == 'unallocated':
424
 
                    raise osv.except_osv(_('Error'), _("""You cannot made moves from/to Cross-docking locations
425
 
                    when the Allocated stocks configuration is set to \'Unallocated\'."""))
426
 
                # below, "source_type" is only used for the outgoing shipment. We set it to "None" because by default it is
427
 
                # "default"and we do not want that info on INCOMING shipment
428
 
                var.source_type = None
429
 
                product_id = values['product_id']
430
 
                product_type = self.pool.get('product.product').read(cr, uid, product_id, ['type'], context=context)['type']
431
 
                values.update({'location_dest_id': cross_docking_location})
432
 
                values.update({'cd_from_bo': True})
433
 
            elif var.dest_type == 'to_stock':
434
 
                var.source_type = None
435
 
                # below, "source_type" is only used for the outgoing shipment. We set it to "None" because
436
 
                #by default it is "default"and we do not want that info on INCOMING shipment
437
 
                product_id = values['product_id']
438
 
                product_type = self.pool.get('product.product').read(cr, uid, product_id, ['type'], context=context)['type']
439
 
                if product_type == 'consu' and stock_location_non_stockable:
440
 
                    values.update({'location_dest_id': stock_location_non_stockable})
441
 
                elif product_type == 'service_recep' and stock_location_service:
442
 
                    values.update({'location_dest_id': stock_location_service})
443
 
                else:
444
 
                    # treat moves towards STOCK if NOT SERVICE
445
 
                    values.update({'location_dest_id': stock_location_input})
446
 
                values.update({'cd_from_bo': False})
447
 
        return values
448
 
 
449
 
    def _do_partial_hook(self, cr, uid, ids, context, *args, **kwargs):
450
 
        '''
451
 
        hook to update defaults data of the current object, which is stock.picking.
452
 
        The defaults data are taken from the _do_partial_hook which is on the stock_partial_picking
453
 
        osv_memory object used for the wizard of deliveries.
454
 
        For outgoing shipment
455
 
        '''
456
 
        if isinstance(ids, (int, long)):
457
 
            ids = [ids]
458
 
        # variable parameters
459
 
        move = kwargs.get('move')
460
 
        assert move, 'missing move'
461
 
        partial_datas = kwargs.get('partial_datas')
462
 
        assert partial_datas, 'missing partial_datas'
463
 
        # calling super method
464
 
        defaults = super(stock_picking, self)._do_partial_hook(cr, uid, ids, context, *args, **kwargs)
465
 
        # location_id is equivalent to the source location: does it exist when we go through the "_do_partial_hook" in the msf_cross_docking> stock_partial_piking> "do_partial_hook"
466
 
        location_id = partial_datas.get('move%s'%(move.id), {}).get('location_id')
467
 
        if location_id:
468
 
            defaults.update({'location_id': location_id})
469
 
        return defaults
470
 
 
471
 
    def check_all_move_cross_docking(self, cr, uid, ids, context=None):
472
 
        '''
473
 
        Check if all stock moves are cross docking or to stock, in this case, the picking will be updated
474
 
        '''
475
 
        stock_todo = []
476
 
        cross_todo = []
477
 
        for pick in self.browse(cr, uid, ids, context=context):
478
 
            to_cross = True
479
 
            to_stock = True
480
 
            for move in pick.move_lines:
481
 
                to_cross = move.move_cross_docking_ok
482
 
                to_stock = not move.move_cross_docking_ok
483
 
            if to_cross:
484
 
                cross_todo.append(pick.id)
485
 
            if to_stock:
486
 
                cross_todo.append(pick.id)
487
 
        if stock_todo:
488
 
            self.write(cr, uid, stock_todo, {'cross docking_ok': False})
489
 
        if cross_todo:
490
 
            self.write(cr, uid, cross_todo, {'cross docking_ok': True})
491
 
        return True
492
 
 
493
 
stock_picking()
494
 
 
495
 
 
496
 
class stock_move(osv.osv):
497
 
    _inherit = 'stock.move'
498
 
    """
499
 
    The field below 'move_cross_docking_ok' is used solely for the view using attrs. I has been named especially
500
 
    'MOVE_cross_docking_ok' for not being in conflict with the other 'cross_docking_ok' in the stock.picking object
501
 
    which also uses attrs according to the value of cross_docking_ok'.
502
 
    """
503
 
 
504
 
    def _get_allocation_setup(self, cr, uid, ids, field_name, args, context=None):
505
 
        '''
506
 
        Returns the Unifield configuration value
507
 
        '''
508
 
        res = {}
509
 
        setup = self.pool.get('unifield.setup.configuration').get_config(cr, uid)
510
 
        for order in ids:
511
 
            res[order] = setup.allocation_setup
512
 
        return res
513
 
 
514
 
    _columns = {
515
 
        'move_cross_docking_ok': fields.boolean('Cross docking'),
516
 
        'allocation_setup': fields.function(_get_allocation_setup, type='selection',
517
 
                                            selection=[('allocated', 'Allocated'),
518
 
                                                       ('unallocated', 'Unallocated'),
519
 
                                                       ('mixed', 'Mixed')], string='Allocated setup', method=True, store=False),
520
 
    }
521
 
 
522
 
    def default_get(self, cr, uid, fields, context=None):
523
 
        """ To get default values for the object:
524
 
        If cross docking is checked on the purchase order, we set "cross docking" to the destination location
525
 
        else we keep the default values i.e. "Input"
526
 
        """
527
 
        default_data = super(stock_move, self).default_get(cr, uid, fields, context=context)
528
 
        setup = self.pool.get('unifield.setup.configuration').get_config(cr, uid)
529
 
        default_data.update({'allocation_setup': setup.allocation_setup})
530
 
        if context is None:
531
 
            context = {}
532
 
        purchase_id = context.get('purchase_id', [])
533
 
        if not purchase_id:
534
 
            return default_data
535
 
        purchase_browse = self.pool.get('purchase.order').browse(cr, uid, purchase_id, context=context)
536
 
        # If the purchase order linked has the option cross docking then the new created
537
 
        #stock move should have the destination location to cross docking
538
 
        if purchase_browse.cross_docking_ok:
539
 
            default_data.update({'location_dest_id': self.pool.get('stock.location').get_cross_docking_location(cr, uid)})
540
 
        return default_data
541
 
 
542
 
    def button_cross_docking(self, cr, uid, ids, context=None):
543
 
        """
544
 
        for each stock move we enable to change the source location to cross docking
545
 
        """
546
 
        if context is None:
547
 
            context = {}
548
 
        if isinstance(ids, (int, long)):
549
 
            ids = [ids]
550
 
        # Check the allocation setup
551
 
        setup = self.pool.get('unifield.setup.configuration').get_config(cr, uid)
552
 
        if setup.allocation_setup == 'unallocated':
553
 
            raise osv.except_osv(_('Error'), _("""You cannot made moves from/to Cross-docking locations
554
 
            when the Allocated stocks configuration is set to \'Unallocated\'."""))
555
 
        cross_docking_location = self.pool.get('stock.location').get_cross_docking_location(cr, uid)
556
 
        todo = []
557
 
        for move in self.browse(cr, uid, ids, context=context):
558
 
            if move.state != 'done':
559
 
                todo.append(move.id)
560
 
        ret = True
561
 
        picking_todo = []
562
 
        if todo:
563
 
            ret = self.write(cr, uid, todo, {'location_id': cross_docking_location, 'move_cross_docking_ok': True}, context=context)
564
 
            
565
 
            # we cancel availability
566
 
            self.cancel_assign(cr, uid, todo, context=context)
567
 
            # we rechech availability
568
 
            self.action_assign(cr, uid, todo)
569
 
            #FEFO
570
 
            self.fefo_update(cr, uid, ids, context)
571
 
            # below we cancel availability to recheck it
572
 
#            stock_picking_id = self.read(cr, uid, todo, ['picking_id'], context=context)[0]['picking_id'][0]
573
 
#            picking_todo.append(stock_picking_id)
574
 
#            # we cancel availability
575
 
#            self.pool.get('stock.picking').cancel_assign(cr, uid, [stock_picking_id])
576
 
#            # we recheck availability
577
 
#            self.pool.get('stock.picking').action_assign(cr, uid, [stock_picking_id])
578
 
#        if picking_todo:
579
 
#            self.pool.get('stock.picking').check_all_move_cross_docking(cr, uid, picking_todo, context=context)
580
 
        return ret
581
 
 
582
 
    def button_stock(self, cr, uid, ids, context=None):
583
 
        """
584
 
        for each stock move we enable to change the source location to stock
585
 
        """
586
 
        if context is None:
587
 
            context = {}
588
 
        if isinstance(ids, (int, long)):
589
 
            ids = [ids]
590
 
        obj_data = self.pool.get('ir.model.data')
591
 
        todo = []
592
 
        picking_todo = []
593
 
        for move in self.browse(cr, uid, ids, context=context):
594
 
            if move.state != 'done':
595
 
                '''
596
 
                Specific rules for non-stockable products:
597
 
                   * if the move is an outgoing delivery, picked them from cross-docking
598
 
                   * else picked them from the non-stockable location
599
 
                '''
600
 
                if move.product_id.type in ('consu', 'service_recep'):
601
 
                    if move.picking_id.type == 'out':
602
 
                        id_loc_s = obj_data.get_object_reference(cr, uid, 'msf_cross_docking', 'stock_location_cross_docking')[1]
603
 
                    elif move.product_id.type == 'consu':
604
 
                        id_loc_s = obj_data.get_object_reference(cr, uid, 'stock_override', 'stock_location_non_stockable')[1]
605
 
                    else:
606
 
                        id_loc_s = self.pool.get('stock.location').get_service_location(cr, uid)
607
 
                    self.write(cr, uid, move.id, {'location_id': id_loc_s, 'move_cross_docking_ok': False}, context=context)
608
 
                else:
609
 
                    self.write(cr, uid, move.id, {'location_id': move.picking_id.warehouse_id.lot_stock_id.id,
610
 
                                                  'move_cross_docking_ok': False}, context=context)
611
 
                todo.append(move.id)
612
 
 
613
 
        if todo:
614
 
            # we cancel availability
615
 
            self.cancel_assign(cr, uid, todo, context=context)
616
 
            # we rechech availability
617
 
            self.action_assign(cr, uid, todo)
618
 
            
619
 
            #FEFO
620
 
            self.fefo_update(cr, uid, todo, context)
621
 
            # below we cancel availability to recheck it
622
 
#            stock_picking_id = self.read(cr, uid, todo, ['picking_id'], context=context)[0]['picking_id'][0]
623
 
#            picking_todo.append(stock_picking_id)
624
 
            # we cancel availability
625
 
#            self.pool.get('stock.picking').cancel_assign(cr, uid, [stock_picking_id])
626
 
            # we recheck availability
627
 
#            self.pool.get('stock.picking').action_assign(cr, uid, [stock_picking_id])
628
 
#        if picking_todo:
629
 
#            self.pool.get('stock.picking').check_all_move_cross_docking(cr, uid, picking_todo, context=context)
630
 
        return True
631
 
 
632
 
stock_move()