1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# OpenERP, Open Source Management Solution
5
# Copyright (C) 2011 TeMPO Consulting, MSF
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
##############################################################################
22
from osv import osv, fields
28
from tools.translate import _
29
from dateutil.relativedelta import relativedelta
30
from datetime import datetime
36
define getter for date / time / datetime formats
40
def _get_format(self, cr, uid, type, context=None):
46
type = type + '_format'
47
assert type in self._columns, 'Specified format field does not exist'
48
user_obj = self.pool.get('res.users')
49
# get user context lang
50
user_lang = user_obj.read(cr, uid, uid, ['context_lang'], context=context)['context_lang']
52
lang_id = self.search(cr, uid, [('code','=',user_lang)])
53
# return format value or from default function if not exists
54
format = lang_id and self.read(cr, uid, lang_id[0], [type], context=context)[type] or getattr(self, '_get_default_%s'%type)(cr, uid, context=context)
57
def _get_db_format(self, cr, uid, type, context=None):
59
generic function - for now constant values
72
class date_tools(osv.osv):
74
date related tools for msf project
78
def get_date_format(self, cr, uid, context=None):
80
get the date format for the uid specified user
82
from msf_order_date module
84
lang_obj = self.pool.get('res.lang')
85
return lang_obj._get_format(cr, uid, 'date', context=context)
87
def get_db_date_format(self, cr, uid, context=None):
91
lang_obj = self.pool.get('res.lang')
92
return lang_obj._get_db_format(cr, uid, 'date', context=context)
94
def get_time_format(self, cr, uid, context=None):
96
get the time format for the uid specified user
98
from msf_order_date module
100
lang_obj = self.pool.get('res.lang')
101
return lang_obj._get_format(cr, uid, 'time', context=context)
103
def get_db_time_format(self, cr, uid, context=None):
105
return constant value
107
lang_obj = self.pool.get('res.lang')
108
return lang_obj._get_db_format(cr, uid, 'time', context=context)
110
def get_datetime_format(self, cr, uid, context=None):
112
get the datetime format for the uid specified user
114
return self.get_date_format(cr, uid, context=context) + ' ' + self.get_time_format(cr, uid, context=context)
116
def get_db_datetime_format(self, cr, uid, context=None):
118
return constant value
120
return self.get_db_date_format(cr, uid, context=context) + ' ' + self.get_db_time_format(cr, uid, context=context)
122
def get_date_formatted(self, cr, uid, d_type='date', datetime=None, context=None):
124
Return the datetime in the format of the user
125
@param d_type: 'date' or 'datetime' : determines which is the out format
126
@param datetime: date to format
128
assert d_type in ('date', 'datetime'), 'Give only \'date\' or \'datetime\' as type parameter'
131
datetime = time.strftime('%Y-%m-%d')
134
d_format = self.get_date_format(cr, uid)
135
date = time.strptime(datetime, '%Y-%m-%d')
136
return time.strftime(d_format, date)
137
elif d_type == 'datetime':
138
d_format = self.get_datetime_format(cr, uid)
139
date = time.strptime(datetime, '%Y-%m-%d %H:%M:%S')
140
return time.strftime(d_format, date)
145
class fields_tools(osv.osv):
147
date related tools for msf project
149
_name = 'fields.tools'
151
def get_field_from_company(self, cr, uid, object=False, field=False, context=None):
153
return the value for field from company for object
155
# field is required for value
159
company_obj = self.pool.get('res.company')
160
# corresponding company
161
company_id = company_obj._company_default_get(cr, uid, object, context=context)
163
res = company_obj.read(cr, uid, [company_id], [field], context=context)[0][field]
166
def get_selection_name(self, cr, uid, object=False, field=False, key=False, context=None):
168
return the name from the key of selection field
170
if not object or not field or not key:
172
# get the selection values list
173
if isinstance(object, str):
174
object = self.pool.get(object)
175
list = object._columns[field].selection
176
name = [x[1] for x in list if x[0] == key][0]
179
def get_ids_from_browse_list(self, cr, uid, browse_list=False, context=None):
181
return the list of ids corresponding to browse list in parameter
186
result = [x.id for x in browse_list]
192
class data_tools(osv.osv):
194
data related tools for msf project
198
def load_common_data(self, cr, uid, ids, context=None):
200
load common data into context
204
context.setdefault('common', {})
206
date_tools = self.pool.get('date.tools')
207
obj_data = self.pool.get('ir.model.data')
208
comp_obj = self.pool.get('res.company')
210
db_date_format = date_tools.get_db_date_format(cr, uid, context=context)
211
context['common']['db_date_format'] = db_date_format
212
date_format = date_tools.get_date_format(cr, uid, context=context)
213
context['common']['date_format'] = date_format
215
date = time.strftime(db_date_format)
216
context['common']['date'] = date
218
company_id = comp_obj._company_default_get(cr, uid, 'stock.picking', context=context)
219
context['common']['company_id'] = company_id
222
stock_id = obj_data.get_object_reference(cr, uid, 'stock', 'stock_location_stock')[1]
223
context['common']['stock_id'] = stock_id
225
kitting_id = obj_data.get_object_reference(cr, uid, 'stock', 'location_production')[1]
226
context['common']['kitting_id'] = kitting_id
228
input_id = obj_data.get_object_reference(cr, uid, 'msf_cross_docking', 'stock_location_input')[1]
229
context['common']['input_id'] = input_id
231
quarantine_anal = obj_data.get_object_reference(cr, uid, 'stock_override', 'stock_location_quarantine_analyze')[1]
232
context['common']['quarantine_anal'] = quarantine_anal
233
# quarantine before scrap
234
quarantine_scrap = obj_data.get_object_reference(cr, uid, 'stock_override', 'stock_location_quarantine_scrap')[1]
235
context['common']['quarantine_scrap'] = quarantine_scrap
237
log = obj_data.get_object_reference(cr, uid, 'stock_override', 'stock_location_logistic')[1]
238
context['common']['log'] = log
240
cross_docking = obj_data.get_object_reference(cr, uid, 'msf_cross_docking', 'stock_location_cross_docking')[1]
241
context['common']['cross_docking'] = cross_docking
244
reason_type_id = obj_data.get_object_reference(cr, uid, 'reason_types_moves', 'reason_type_kit')[1]
245
context['common']['reason_type_id'] = reason_type_id
246
# reason type goods return
247
rt_goods_return = obj_data.get_object_reference(cr, uid, 'reason_types_moves', 'reason_type_goods_return')[1]
248
context['common']['rt_goods_return'] = rt_goods_return
249
# reason type goods replacement
250
rt_goods_replacement = obj_data.get_object_reference(cr, uid, 'reason_types_moves', 'reason_type_goods_replacement')[1]
251
context['common']['rt_goods_replacement'] = rt_goods_replacement
252
# reason type internal supply
253
rt_internal_supply = obj_data.get_object_reference(cr, uid, 'reason_types_moves', 'reason_type_internal_supply')[1]
254
context['common']['rt_internal_supply'] = rt_internal_supply
261
class sequence_tools(osv.osv):
265
_name = 'sequence.tools'
267
def reorder_sequence_number(self, cr, uid, base_object, base_seq_field, dest_object, foreign_field, foreign_ids, seq_field, context=None):
269
receive a browse list corresponding to one2many lines
270
recompute numbering corresponding to specified field
271
compute next number of sequence
273
we must make sure we reorder in conservative way according to original order
280
if isinstance(foreign_ids, (int, long)):
281
foreign_ids = [foreign_ids]
284
base_obj = self.pool.get(base_object)
285
dest_obj = self.pool.get(dest_object)
286
seq_obj = self.pool.get('ir.sequence')
288
for foreign_id in foreign_ids:
289
# will be ordered by default according to db id, it's what we want according to user sequence
290
item_ids = dest_obj.search(cr, uid, [(foreign_field, '=', foreign_id)], context=context)
292
# read line number and id from items
293
item_data = dest_obj.read(cr, uid, item_ids, [seq_field], context=context)
294
# check the line number: data are ordered according to db id, so line number must be equal to index+1
295
for i in range(len(item_data)):
296
if item_data[i][seq_field] != i+1:
297
dest_obj.write(cr, uid, [item_data[i]['id']], {seq_field: i+1}, context=context)
298
# reset sequence to length + 1 all time, checking if needed would take much time
299
# get the sequence id
300
seq_id = base_obj.read(cr, uid, foreign_id, [base_seq_field], context=context)[base_seq_field][0]
301
# we reset the sequence to length+1
302
self.reset_next_number(cr, uid, [seq_id], value=len(item_ids)+1, context=context)
306
def reorder_sequence_number_from_unlink(self, cr, uid, ids, base_object, base_seq_field, dest_object, foreign_field, seq_field, context=None):
308
receive a browse list corresponding to one2many lines
309
recompute numbering corresponding to specified field
310
compute next number of sequence
312
for unlink, only items with id > min(deleted id) are resequenced + reset the sequence value
314
we must make sure we reorder in conservative way according to original order
316
this method is called from methods of **destination object**
321
# if no ids as parameter return Tru
326
base_obj = self.pool.get(base_object)
327
dest_obj = self.pool.get(dest_object)
328
seq_obj = self.pool.get('ir.sequence')
330
# find the corresponding base ids
331
base_ids = [x[foreign_field][0] for x in dest_obj.read(cr, uid, ids, [foreign_field], context=context) if x[foreign_field]]
332
# simulate unique sql
333
foreign_ids = set(base_ids)
335
for foreign_id in foreign_ids:
336
# will be ordered by default according to db id, it's what we want according to user sequence
337
# reorder only ids bigger than min deleted + do not select deleted ones
338
item_ids = dest_obj.search(cr, uid, [('id', '>', min(ids)), (foreign_field, '=', foreign_id), ('id', 'not in', ids)], context=context)
339
# start numbering sequence
341
# if deleted object is not the first one, we find the numbering value of previous one
342
before_ids = dest_obj.search(cr, uid, [('id', '<', min(ids)), (foreign_field, '=', foreign_id)], context=context)
344
# we read the numbering value of previous value (biggest id)
345
start_num = dest_obj.read(cr, uid, max(before_ids), [seq_field], context=context)[seq_field]
347
# read line number and id from items
348
item_data = dest_obj.read(cr, uid, item_ids, [seq_field], context=context)
349
# check the line number: data are ordered according to db id, so line number must be equal to index+1
350
for i in range(len(item_data)):
352
start_num = start_num+1
353
if item_data[i][seq_field] != start_num:
354
dest_obj.write(cr, uid, [item_data[i]['id']], {seq_field: start_num}, context=context)
356
# reset sequence to start_num + 1 all time, checking if needed would take much time
357
# get the sequence id
358
seq_id = base_obj.read(cr, uid, foreign_id, [base_seq_field], context=context)[base_seq_field][0]
359
# we reset the sequence to length+1, whether or not items
360
self.reset_next_number(cr, uid, [seq_id], value=start_num+1, context=context)
364
def reset_next_number(self, cr, uid, seq_ids, value=1, context=None):
366
reset the next number of the sequence to value, default value 1
371
if isinstance(seq_ids, (int, long)):
375
seq_obj = self.pool.get('ir.sequence')
376
seq_obj.write(cr, uid, seq_ids, {'number_next': value}, context=context)
379
def create_sequence(self, cr, uid, vals, name, code, prefix='', padding=0, context=None):
381
create a new sequence
383
seq_pool = self.pool.get('ir.sequence')
384
seq_typ_pool = self.pool.get('ir.sequence.type')
386
assert name, 'create sequence: missing name'
387
assert code, 'create sequence: missing code'
389
types = {'name': name,
392
seq_typ_pool.create(cr, uid, types)
399
return seq_pool.create(cr, uid, seq)
404
class picking_tools(osv.osv):
406
picking related tools
408
_name = 'picking.tools'
410
def confirm(self, cr, uid, ids, context=None):
417
if isinstance(ids, (int, long)):
421
pick_obj = self.pool.get('stock.picking')
422
pick_obj.draft_force_assign(cr, uid, ids, context)
425
def check_assign(self, cr, uid, ids, context=None):
427
check assign the picking
432
if isinstance(ids, (int, long)):
436
pick_obj = self.pool.get('stock.picking')
437
pick_obj.action_assign(cr, uid, ids, context)
440
def force_assign(self, cr, uid, ids, context=None):
442
force assign the picking
447
if isinstance(ids, (int, long)):
451
pick_obj = self.pool.get('stock.picking')
452
pick_obj.force_assign(cr, uid, ids, context)
455
def validate(self, cr, uid, ids, context=None):
462
if isinstance(ids, (int, long)):
466
pick_obj = self.pool.get('stock.picking')
467
wf_service = netsvc.LocalService("workflow")
468
# trigger standard workflow for validated picking ticket
470
pick_obj.action_move(cr, uid, [id])
471
wf_service.trg_validate(uid, 'stock.picking', id, 'button_done', cr)
474
def all(self, cr, uid, ids, context=None):
476
confirm - check - validate
478
self.confirm(cr, uid, ids, context=context)
479
self.check_assign(cr, uid, ids, context=context)
480
self.validate(cr, uid, ids, context=context)
486
class ir_translation(osv.osv):
487
_name = 'ir.translation'
488
_inherit = 'ir.translation'
490
def tr_view(self, cr, name, context):
491
if not context or not context.get('lang'):
493
tr = self._get_source(cr, 1, False, 'view', context['lang'], name, True)
495
# sometimes de view name is empty and so the action name is used as view name
496
tr2 = self._get_source(cr, 1, 'ir.actions.act_window,name', 'model', context['lang'], name)