1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# OpenERP, Open Source Management Solution
5
# Copyright (C) 2012 TeMPO Consulting, MSF, Smile
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.
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.
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/>.
20
##############################################################################
23
from osv import fields
24
from tools.translate import _
29
class purchase_order(osv.osv):
31
Enables the option cross docking
33
_name = 'purchase.order'
34
_inherit = 'purchase.order'
37
'cross_docking_ok': fields.boolean('Cross docking'),
38
'location_id': fields.many2one('stock.location', 'Destination', required=True, domain=[('usage','<>','view')],
39
help="""This location is set according to the Warehouse selected, or according to the option 'Cross docking'
40
or freely if you do not select 'Warehouse'.But if the 'Order category' is set to 'Transport' or 'Service',
41
you cannot have an other location than 'Service'"""),
45
'cross_docking_ok': False,
48
def onchange_internal_type(self, cr, uid, ids, order_type, partner_id, dest_partner_id=False, warehouse_id=False):
50
Changes destination location
52
res = super(purchase_order, self).onchange_internal_type(cr, uid, ids, order_type, partner_id, dest_partner_id, warehouse_id)
53
if order_type == 'direct':
54
location_id = self.onchange_cross_docking_ok(cr, uid, ids, False, warehouse_id)['value']['location_id']
57
res['value'].update({'location_id': location_id})
59
res.update({'value': {'location_id': location_id}})
63
def onchange_cross_docking_ok(self, cr, uid, ids, cross_docking_ok, warehouse_id, context=None):
64
""" Finds location id for changed cross_docking_ok.
65
@param cross_docking_ok: Changed value of cross_docking_ok.
66
@return: Dictionary of values.
68
if isinstance(ids, (int, long)):
70
obj_data = self.pool.get('ir.model.data')
72
l = self.pool.get('stock.location').get_cross_docking_location(cr, uid)
74
warehouse_obj = self.pool.get('stock.warehouse')
76
warehouse_ids = warehouse_obj.search(cr, uid, [], limit=1)
78
return {'warning': {'title': _('Error !'), 'message': _('No Warehouse defined !')}, 'value': {'location_id': False}}
79
warehouse_id = warehouse_ids[0]
80
l = warehouse_obj.read(cr, uid, [warehouse_id], ['lot_input_id'])[0]['lot_input_id'][0]
81
return {'value': {'location_id': l}}
83
def onchange_location_id(self, cr, uid, ids, location_id, categ, context=None):
84
""" If location_id == cross docking we tick the box "cross docking".
85
@param location_id: Changed value of location_id.
86
@return: Dictionary of values.
88
if isinstance(ids, (int, long)):
92
obj_data = self.pool.get('ir.model.data')
93
if location_id == self.pool.get('stock.location').get_cross_docking_location(cr, uid) and categ not in ['service', 'transport']:
94
cross_docking_ok = True
95
elif location_id != self.pool.get('stock.location').get_cross_docking_location(cr, uid):
96
cross_docking_ok = False
97
elif location_id != self.pool.get('stock.location').get_service_location(cr, uid) and categ in ['service', 'transport']:
98
return {'warning': {'title': _('Error !'), 'message': _("""
99
If the 'Order Category' is 'Service' or 'Transport', you cannot have an other location than 'Service'
100
""")}, 'value': {'location_id': self.pool.get('stock.location').get_service_location(cr, uid)}}
101
res['value']['cross_docking_ok'] = cross_docking_ok
104
def onchange_warehouse_id(self, cr, uid, ids, warehouse_id, order_type, dest_address_id):
105
""" Set cross_docking_ok to False when we change warehouse.
106
@param warehouse_id: Changed id of warehouse.
107
@return: Dictionary of values.
109
res = super(purchase_order, self).onchange_warehouse_id(cr, uid, ids, warehouse_id, order_type, dest_address_id)
111
res['value'].update({'cross_docking_ok': False})
115
def onchange_categ(self, cr, uid, ids, categ, 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.
120
if isinstance(ids, (int, long)):
122
obj_data = self.pool.get('ir.model.data')
124
defined_location = None
125
if categ in ['service', 'transport']:
127
defined_location = self.pool.get('stock.location').get_service_location(cr, uid)
128
return {'value': {'cross_docking_ok': bool_value, 'location_id':defined_location}}
130
def write(self, cr, uid, ids, vals, context=None):
131
if isinstance(ids, (int, long)):
133
obj_data = self.pool.get('ir.model.data')
135
cross_docking_ok = False
136
active_id = context.get('active_id', False)
137
order = self.browse(cr, uid, ids, context=context)[0]
138
default_cross_docking_location = self.pool.get('stock.location').get_cross_docking_location(cr, uid)
139
if vals.get('location_id', order.location_id.id):
140
default_location_id = vals.get('location_id', order.location_id.id)
141
elif order.warehouse_id.lot_input_id:
142
default_location_id = order.warehouse_id.lot_input_id.id
143
type_value = vals.get('order_type', order.order_type)
144
if type_value == 'direct':
145
vals.update({'cross_docking_ok': False})
146
if vals.get('cross_docking_ok'):
147
cross_docking_ok = vals.get('cross_docking_ok')
149
if self.read(cr, uid, [active_id],['cross_docking_ok'], context=context):
150
cross_docking_ok = self.read(cr, uid, [active_id],['cross_docking_ok'], context=context)[0]['cross_docking_ok']
151
if vals.get('location_id', False):
152
location_id = vals.get('location_id', False)
154
if self.read(cr, uid, [active_id],['cross_docking_ok'], context=context):
155
location_id = self.read(cr, uid, [active_id],['location_id'], context=context)[0]['location_id'][0]
156
if cross_docking_ok or location_id == default_cross_docking_location:
157
if not vals.get('categ') in ['service', 'transport']:
158
default_location_id = default_cross_docking_location
159
vals.update({'location_id': default_location_id, 'cross_docking_ok': True})
161
vals.update({'location_id': default_location_id, 'cross_docking_ok': False})
162
elif not cross_docking_ok or location_id != default_cross_docking_location:
163
if vals.get('categ') in ['service', 'transport']:
164
default_location_id = self.pool.get('stock.location').get_service_location(cr, uid)
165
vals.update({'location_id': default_location_id, 'cross_docking_ok': False})
167
vals.update({'location_id': default_location_id, 'cross_docking_ok': False})
168
elif location_id == self.pool.get('stock.location').get_cross_docking_location(cr, uid):
169
vals.update({'location_id': default_cross_docking_location, 'cross_docking_ok': True})
170
return super(purchase_order, self).write(cr, uid, ids, vals, context=context)
172
def create(self, cr, uid, vals, context=None):
173
obj_data = self.pool.get('ir.model.data')
174
if vals.get('order_type') == 'direct':
175
vals.update({'cross_docking_ok': False})
176
if vals.get('cross_docking_ok'):
177
vals.update({'location_id': self.pool.get('stock.location').get_cross_docking_location(cr, uid)})
178
return super(purchase_order, self).create(cr, uid, vals, context=context)
180
def _check_cross_docking(self, cr, uid, ids, context=None):
182
Check that if you select cross docking, you do not have an other location than cross docking
184
if isinstance(ids, (int, long)):
188
obj_data = self.pool.get('ir.model.data')
189
cross_docking_location = self.pool.get('stock.location').get_cross_docking_location(cr, uid)
190
for purchase in self.browse(cr, uid, ids, context=context):
191
if purchase.cross_docking_ok and purchase.location_id.id != cross_docking_location:
192
raise osv.except_osv(_('Warning !'), _('If you tick the box \"cross docking\", you cannot have an other location than \"Cross docking\"'))
197
(_check_cross_docking, 'If you tick the box \"cross docking\", you cannot have an other location than \"Cross docking\"', ['location_id']),
202
class procurement_order(osv.osv):
204
_inherit = 'procurement.order'
206
def po_values_hook(self, cr, uid, ids, context=None, *args, **kwargs):
208
When you run the scheduler and you have a sale order line with type = make_to_order,
209
we modify the location_id to set 'cross docking' of the purchase order created in mirror
213
if isinstance(ids, (int, long)):
215
sol_obj = self.pool.get('sale.order.line')
216
obj_data = self.pool.get('ir.model.data')
217
procurement = kwargs['procurement']
219
values = super(procurement_order, self).po_values_hook(cr, uid, ids, context=context, *args, **kwargs)
220
ids = sol_obj.search(cr, uid, [('procurement_id', '=', procurement.id)], context=context)
222
values.update({'cross_docking_ok': True, 'location_id' : self.pool.get('stock.location').get_cross_docking_location(cr, uid)})
227
class stock_picking(osv.osv):
229
do_partial(=function which is originally called from delivery_mechanism) modification
230
for the selection of the LOCATION for IN (incoming shipment) and OUT (delivery orders)
232
_inherit = 'stock.picking'
236
Load msf_cross_docking_data.xml before self
238
if hasattr(super(stock_picking, self), 'init'):
239
super(stock_picking, self).init(cr)
241
mod_obj = self.pool.get('ir.module.module')
242
logging.getLogger('init').info('HOOK: module msf_cross_docking: loading data/msf_msf_cross_docking_data.xml')
243
pathname = path.join('msf_cross_docking', 'data/msf_cross_docking_data.xml')
244
file = tools.file_open(pathname)
245
tools.convert_xml_import(cr, 'msf_cross_docking', file, {}, mode='init', noupdate=False)
248
'cross_docking_ok': fields.boolean('Cross docking'),
252
do_partial(=function which is originally called from delivery_mechanism) modification
253
for the selection of the LOCATION for IN (incoming shipment) and OUT (delivery orders)
256
def write(self, cr, uid, ids, vals, context=None):
258
Here we check if all stock move are in stock or in cross docking
262
if isinstance(ids, (int, long)):
264
obj_data = self.pool.get('ir.model.data')
265
move_obj = self.pool.get('stock.move')
266
pick_obj = self.pool.get('stock.picking')
267
cross_docking_location = self.pool.get('stock.location').get_cross_docking_location(cr, uid)
268
for pick in pick_obj.browse(cr,uid,ids,context=context):
269
move_lines = pick.move_lines
270
if len(move_lines) >= 1 :
271
for move in move_lines:
273
for move in move_obj.browse(cr,uid,[move_ids],context=context):
274
move_cross_docking_ok_value = move_obj.read(cr, uid, [move_ids], ['move_cross_docking_ok'], context=context)
275
if move.move_cross_docking_ok == True:
276
vals.update({'cross_docking_ok': True,})
277
elif move.move_cross_docking_ok == False:
278
vals.update({'cross_docking_ok': False,})
279
return super(stock_picking, self).write(cr, uid, ids, vals, context=context)
281
def button_cross_docking_all (self, cr, uid, ids, context=None):
283
set all stock moves with the source location to 'cross docking'
287
if isinstance(ids, (int, long)):
289
obj_data = self.pool.get('ir.model.data')
290
move_obj = self.pool.get('stock.move')
291
pick_obj = self.pool.get('stock.picking')
292
cross_docking_location = self.pool.get('stock.location').get_cross_docking_location(cr, uid)
293
for pick in pick_obj.browse(cr,uid,ids,context=context):
294
move_lines = pick.move_lines
295
if len(move_lines) >= 1 :
296
for move in move_lines:
298
for move in move_obj.browse(cr,uid,[move_ids],context=context):
299
# Don't change done stock moves
300
if move.state != 'done':
301
move_obj.write(cr, uid, [move_ids], {'location_id': cross_docking_location, 'move_cross_docking_ok': True}, context=context)
302
self.write(cr, uid, ids, {'cross_docking_ok': True}, context=context)
304
raise osv.except_osv(_('Warning !'), _('Please, enter some stock moves before changing the source location to CROSS DOCKING'))
305
# we check availability : cancel then check
306
self.cancel_assign(cr, uid, ids)
307
self.action_assign(cr, uid, ids)
310
def button_stock_all (self, cr, uid, ids, context=None):
312
set all stock move with the source location to 'stock'
316
if isinstance(ids, (int, long)):
318
obj_data = self.pool.get('ir.model.data')
319
move_obj = self.pool.get('stock.move')
320
pick_obj = self.pool.get('stock.picking')
321
for pick in pick_obj.browse(cr,uid,ids,context=context):
322
move_lines = pick.move_lines
323
if len(move_lines) >= 1 :
324
for move in move_lines:
326
for move in move_obj.browse(cr,uid,[move_ids],context=context):
327
# Don't change done stock moves
328
if move.state != 'done':
329
move_obj.write(cr, uid, [move_ids], {'location_id': pick.warehouse_id.lot_stock_id.id, 'move_cross_docking_ok': False}, context=context)
330
self.write(cr, uid, ids, {'cross_docking_ok': False}, context=context)
332
raise osv.except_osv(_('Warning !'), _('Please, enter some stock moves before changing the source location to STOCK'))
333
# we check availability : cancel then check
334
self.cancel_assign(cr, uid, ids)
335
self.action_assign(cr, uid, ids)
338
def _do_incoming_shipment_first_hook(self, cr, uid, ids, context=None, *args, **kwargs):
340
This hook refers to delivery_mechanism>delivery_mechanism.py>_do_incoming_shipment.
341
It updates the location_dest_id (to cross docking or to stock)
342
of selected stock moves when the linked 'incoming shipment' is validated
343
-> only related to 'in' type stock.picking
345
values = super(stock_picking, self)._do_incoming_shipment_first_hook(cr, uid, ids, context=context, *args, **kwargs)
346
assert values is not None, 'missing values'
350
if isinstance(ids, (int, long)):
353
# take ids of the wizard from the context.
354
# NB: the wizard_ids is created in delivery_mechanism>delivery_mecanism.py> in the method "_stock_picking_action_process_hook"
355
wiz_ids = context.get('wizard_ids')
360
# ------ referring to locations 'cross docking' and 'stock' ------------------------------------------------------
361
obj_data = self.pool.get('ir.model.data')
362
cross_docking_location = self.pool.get('stock.location').get_cross_docking_location(cr, uid)
363
stock_location_input = obj_data.get_object_reference(cr, uid, 'msf_cross_docking', 'stock_location_input')[1]
364
# ----------------------------------------------------------------------------------------------------------------
365
partial_picking_obj = self.pool.get('stock.partial.picking')
367
# We browse over the wizard (stock.partial.picking)
368
for var in partial_picking_obj.browse(cr, uid, wiz_ids, context=context):
369
"""For incoming shipment """
370
# we check the dest_type for INCOMING shipment (and not the source_type which is reserved for OUTGOING shipment)
371
if var.dest_type == 'to_cross_docking':
372
# below, "source_type" is only used for the outgoing shipment. We set it to "None" because by default it is "default"and we do not want that info on INCOMING shipment
373
var.source_type = None
374
product_id = values['product_id']
375
product_type = self.pool.get('product.product').read(cr, uid, product_id, ['type'], context=context)['type']
376
if product_type not in ('service_recep', 'service'):
377
# treat moves towards CROSS DOCKING if NOT SERVICE
378
values.update({'location_dest_id': cross_docking_location})
379
elif var.dest_type == 'to_stock' :
380
var.source_type = None
381
# below, "source_type" is only used for the outgoing shipment. We set it to "None" because by default it is "default"and we do not want that info on INCOMING shipment
382
product_id = values['product_id']
383
product_type = self.pool.get('product.product').read(cr, uid, product_id, ['type'], context=context)['type']
384
if product_type not in ('service_recep', 'service'):
385
# treat moves towards STOCK if NOT SERVICE
386
values.update({'location_dest_id': stock_location_input})
389
def _do_partial_hook(self, cr, uid, ids, context, *args, **kwargs):
391
hook to update defaults data of the current object, which is stock.picking.
392
The defaults data are taken from the _do_partial_hook which is on the stock_partial_picking
393
osv_memory object used for the wizard of deliveries.
394
For outgoing shipment
396
if isinstance(ids, (int, long)):
398
# variable parameters
399
move = kwargs.get('move')
400
assert move, 'missing move'
401
partial_datas = kwargs.get('partial_datas')
402
assert partial_datas, 'missing partial_datas'
404
# calling super method
405
defaults = super(stock_picking, self)._do_partial_hook(cr, uid, ids, context, *args, **kwargs)
406
# 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"
407
location_id = partial_datas.get('move%s'%(move.id), False).get('location_id')
409
defaults.update({'location_id': location_id})
415
class stock_move(osv.osv):
416
_inherit = 'stock.move'
418
The field below 'move_cross_docking_ok' is used solely for the view using attrs. I has been named especially
419
'MOVE_cross_docking_ok' for not being in conflict with the other 'cross_docking_ok' in the stock.picking object
420
which also uses attrs according to the value of cross_docking_ok'.
423
'move_cross_docking_ok': fields.boolean('Cross docking'),
426
def default_get(self, cr, uid, fields, context=None):
427
""" To get default values for the object:
428
If cross docking is checked on the purchase order, we set "cross docking" to the destination location
429
else we keep the default values i.e. "Input"
431
default_data = super(stock_move, self).default_get(cr, uid, fields, context=context)
434
purchase_id = context.get('purchase_id', [])
438
obj_data = self.pool.get('ir.model.data')
439
purchase_browse = self.pool.get('purchase.order').browse(cr, uid, purchase_id, context=context)
440
# If the purchase order linked has the option cross docking then the new created stock move should have the destination location to cross docking
441
if purchase_browse.cross_docking_ok:
442
default_data.update({'location_dest_id': self.pool.get('stock.location').get_cross_docking_location(cr, uid)})
445
def button_cross_docking (self, cr, uid, ids, context=None):
447
for each stock move we enable to change the source location to cross docking
451
if isinstance(ids, (int, long)):
453
obj_data = self.pool.get('ir.model.data')
454
cross_docking_location = self.pool.get('stock.location').get_cross_docking_location(cr, uid)
457
for move in self.browse(cr, uid, ids, context=context):
458
if move.state != 'done':
462
ret = self.write(cr, uid, todo, {'location_id': cross_docking_location, 'move_cross_docking_ok': True}, context=context)
464
# below we cancel availability to recheck it
465
stock_picking_id = self.read(cr, uid, todo, ['picking_id'], context=context)[0]['picking_id'][0]
466
# we cancel availability
467
self.pool.get('stock.picking').cancel_assign(cr, uid, [stock_picking_id])
468
# we recheck availability
469
self.pool.get('stock.picking').action_assign(cr, uid, [stock_picking_id])
472
def button_stock (self, cr, uid, ids, context=None):
474
for each stock move we enable to change the source location to stock
478
if isinstance(ids, (int, long)):
480
obj_data = self.pool.get('ir.model.data')
483
for move in self.browse(cr, uid, ids, context=context):
484
if move.state != 'done':
485
self.write(cr, uid, move.id, {'location_id': move.picking_id.warehouse_id.lot_stock_id.id, 'move_cross_docking_ok': False}, context=context)
489
# below we cancel availability to recheck it
490
stock_picking_id = self.read(cr, uid, todo, ['picking_id'], context=context)[0]['picking_id'][0]
491
# we cancel availability
492
self.pool.get('stock.picking').cancel_assign(cr, uid, [stock_picking_id])
493
# we recheck availability
494
self.pool.get('stock.picking').action_assign(cr, uid, [stock_picking_id])