1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# OpenERP, Open Source Management Solution
5
# Copyright (C) 2013 CodUP (<http://codup.com>).
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
##############################################################################
25
from openerp.osv import fields, osv
27
class mro_pm_meter(osv.osv):
28
_name = 'mro.pm.meter'
29
_description = 'Asset Meters'
31
READING_TYPE_SELECTION = [
40
('reset', 'Detached'),
41
('reading', 'Reading')
44
def _get_utilization(self, cr, uid, ids, name, arg, context=None):
46
for meter in self.browse(cr, uid, ids, context=context):
47
Dn = 1.0*calendar.timegm(time.strptime(time.strftime('%Y-%m-%d',time.gmtime()),"%Y-%m-%d"))
48
Da = Dn - 3600*24*meter.av_time
49
meter_line_obj = self.pool.get('mro.pm.meter.line')
50
meter_line_ids = meter_line_obj.search(cr, uid, [('meter_id', '=', meter.id),('date', '<=', time.strftime('%Y-%m-%d',time.gmtime(Da)))], limit=1, order='date desc')
51
if not len(meter_line_ids):
52
meter_line_ids = meter_line_obj.search(cr, uid, [('meter_id', '=', meter.id),('date', '>', time.strftime('%Y-%m-%d',time.gmtime(Da)))], limit=1, order='date')
53
if not len(meter_line_ids):
54
res[meter.id] = meter.min_utilization
56
meter_line = meter_line_obj.browse(cr, uid, meter_line_ids[0])
57
Dci = 1.0*calendar.timegm(time.strptime(meter_line.date, "%Y-%m-%d"))
58
Сi = meter_line.total_value
61
meter_line_ids = meter_line_obj.search(cr, uid, [('meter_id', '=', meter.id),('date', '>',meter_line.date)], order='date')
62
for meter_line in meter_line_obj.browse(cr, uid, meter_line_ids):
63
Dci1 = 1.0*calendar.timegm(time.strptime(meter_line.date, "%Y-%m-%d"))
64
Сi1 = meter_line.total_value
66
Us = Us + (3600*24*(Сi1 - Сi))/(Dci1 - Dci)
72
if U<meter.min_utilization:
73
U = meter.min_utilization
74
else: U = meter.min_utilization
78
def _get_lines(self, cr, uid, ids, name, arg, context=None):
80
for meter in self.browse(cr, uid, ids, context=context):
81
line_ids = self.pool.get('mro.pm.meter.line').search(cr, uid,[('meter_id', '=', meter.id)], limit=1)
82
res[meter.id] = line_ids
86
'name': fields.char('Meter', size=64, required=True, translate=True),
87
'state': fields.selection(STATE_SELECTION, 'Status', readonly=True),
88
'reading_type': fields.selection(READING_TYPE_SELECTION, 'Reading Type', required=True, readonly=True, states={'draft': [('readonly', False)]}),
89
'meter_line_ids': fields.one2many('mro.pm.meter.line', 'meter_id', 'Meters'),
90
'view_line_ids': fields.function(_get_lines, relation="mro.pm.meter.line", method=True, type="one2many"),
91
'new_value': fields.float('New value'),
92
'date': fields.related('meter_line_ids', 'date', type='date', string='Date'),
93
'value': fields.related('meter_line_ids', 'value', type='float', string='Value'),
94
'total_value': fields.related('meter_line_ids', 'total_value', type='float', string='Total Value'),
95
'meter_uom': fields.many2one('product.uom', 'Unit of Measure', required=True, readonly=True, states={'draft': [('readonly', False)]}),
96
'asset_id': fields.many2one('asset.asset', 'Asset', ondelete='restrict'),
97
'parent_meter_id': fields.many2one('mro.pm.meter', 'Source Meter', ondelete='restrict', readonly=True, states={'draft': [('readonly', False)]}),
98
'parent_ratio_id': fields.many2one('mro.pm.meter.ratio', 'Ratio to Source', ondelete='restrict'),
99
'utilization': fields.function(_get_utilization, method=True, string='Utilization (per day)'),
100
'min_utilization': fields.float('Min Utilization (per day)', required=True),
101
'av_time': fields.float('Averaging time (days)', required=True),
105
'state': lambda *a: 'draft',
106
'reading_type': lambda *a: 'inc',
107
'min_utilization': 10,
108
'date': lambda *a: time.strftime('%Y-%m-%d'),
109
'meter_uom': lambda self, cr, uid, c: self.pool.get('ir.model.data').get_object(cr, uid, 'product', 'product_uom_hour', context=c).id,
112
def get_reading(self, cr, uid, id, date, context=None):
113
D = 1.0*calendar.timegm(time.strptime(date, "%Y-%m-%d %H:%M:%S"))
114
meter = self.browse(cr, uid, id, context=context)
115
meter_line_obj = self.pool.get('mro.pm.meter.line')
116
prev_read = meter_line_obj.search(cr, uid, [('meter_id', '=', meter.id),('date', '<=', date)], limit=1, order='date desc')
117
next_read = meter_line_obj.search(cr, uid, [('meter_id', '=', meter.id),('date', '>', date)], limit=2, order='date')
118
if not len(prev_read):
119
if len(next_read) == 2:
120
reads = meter_line_obj.browse(cr, uid, next_read)
121
D1 = 1.0*calendar.timegm(time.strptime(reads[0].date, "%Y-%m-%d"))
122
D2 = 1.0*calendar.timegm(time.strptime(reads[1].date, "%Y-%m-%d"))
123
C1 = reads[0].total_value
124
C2 = reads[1].total_value
125
value = C1 - (D1-D)*(C2-C1)/(D2-D1)
127
reads = meter_line_obj.browse(cr, uid, next_read)
128
D1 = 1.0*calendar.timegm(time.strptime(reads[0].date, "%Y-%m-%d"))
129
C1 = reads[0].total_value
130
value = C1 - (D1-D)*meter.utilization/(3600*24)
131
elif not len(next_read):
132
reads = meter_line_obj.browse(cr, uid, prev_read)
133
D1 = 1.0*calendar.timegm(time.strptime(reads[0].date, "%Y-%m-%d"))
134
C1 = reads[0].total_value
135
value = C1 + (D-D1)*meter.utilization/(3600*24)
137
reads = meter_line_obj.browse(cr, uid, [prev_read[0],next_read[0]])
138
D1 = 1.0*calendar.timegm(time.strptime(reads[0].date, "%Y-%m-%d"))
139
D2 = 1.0*calendar.timegm(time.strptime(reads[1].date, "%Y-%m-%d"))
140
C1 = reads[0].total_value
141
C2 = reads[1].total_value
142
value = C1 + (D-D1)*(C2-C1)/(D2-D1)
145
def create(self, cr, uid, vals, context=None):
146
if not vals.get('asset_id',False): return
147
meter_id = super(mro_pm_meter, self).create(cr, uid, vals, context=context)
149
'date': vals.get('date',time.strftime('%Y-%m-%d')),
150
'value': vals.get('value',0),
151
'total_value': vals.get('total_value',0),
152
'meter_id': meter_id,
154
self.pool.get('mro.pm.meter.line').create(cr, uid, values)
157
def write(self, cr, uid, ids, vals, context=None):
158
for meter in self.browse(cr, uid, ids):
159
if vals.get('new_value',False) and meter.state == 'reading':
160
if meter.reading_type == 'inc':
161
if meter.value < vals['new_value']:
162
total_value = meter.total_value + vals['new_value'] - meter.value
163
vals.update({'value': vals['new_value']})
164
vals.update({'total_value': total_value})
165
vals.update({'date': time.strftime('%Y-%m-%d')})
166
if meter.date != time.strftime('%Y-%m-%d'):
167
self.pool.get('mro.pm.meter.line').create(cr, uid, {
168
'date': vals.get('date',time.strftime('%Y-%m-%d')),
169
'value': vals.get('new_value',0),
170
'total_value': vals.get('total_value',0),
171
'meter_id': meter.id,
173
child_meter_ids = self.search(cr, uid, [('parent_meter_id', '=', meter.id),('state', '=', 'reading')])
174
self.write(cr, uid, child_meter_ids, {'new_value':vals['new_value'] - meter.value})
176
del vals['new_value']
177
elif meter.reading_type == 'dec':
178
if meter.value > vals['new_value']:
179
total_value = meter.total_value - vals['new_value'] + meter.value
180
vals.update({'value': vals['new_value']})
181
vals.update({'total_value': total_value})
182
vals.update({'date': time.strftime('%Y-%m-%d')})
183
if meter.date != time.strftime('%Y-%m-%d'):
184
self.pool.get('mro.pm.meter.line').create(cr, uid, {
185
'date': vals.get('date',time.strftime('%Y-%m-%d')),
186
'value': vals.get('new_value',0),
187
'total_value': vals.get('total_value',0),
188
'meter_id': meter.id,
190
child_meter_ids = self.search(cr, uid, [('parent_meter_id', '=', meter.id),('state', '=', 'reading')])
191
self.write(cr, uid, child_meter_ids, {'new_value':meter.value - vals['new_value']})
193
del vals['new_value']
194
elif meter.reading_type == 'cng':
195
total_value = meter.total_value + vals['new_value']
196
vals.update({'value': vals['new_value']})
197
vals.update({'total_value': total_value})
198
vals.update({'date': time.strftime('%Y-%m-%d')})
199
if meter.date != time.strftime('%Y-%m-%d'):
200
self.pool.get('mro.pm.meter.line').create(cr, uid, {
201
'date': vals.get('date',time.strftime('%Y-%m-%d')),
202
'value': vals.get('new_value',0),
203
'total_value': vals.get('total_value',0),
204
'meter_id': meter.id,
206
child_meter_ids = self.search(cr, uid, [('parent_meter_id', '=', meter.id),('state', '=', 'reading')])
207
self.write(cr, uid, child_meter_ids, {'new_value':vals['new_value']})
208
vals.update({'new_value': 0})
209
elif meter.reading_type == 'src':
210
if meter.parent_ratio_id:
211
vals.update({'new_value': self.pool.get('mro.pm.meter.ratio').calculate(cr, uid, meter.parent_ratio_id.id, vals['new_value'])})
212
if meter.parent_meter_id.reading_type == 'inc':
213
total_value = meter.total_value + vals['new_value']
214
vals.update({'value': vals['new_value'] + meter.value})
215
vals.update({'total_value': total_value})
216
vals.update({'date': time.strftime('%Y-%m-%d')})
217
if meter.date != time.strftime('%Y-%m-%d'):
218
self.pool.get('mro.pm.meter.line').create(cr, uid, {
219
'date': vals.get('date',time.strftime('%Y-%m-%d')),
220
'value': vals.get('value',0),
221
'total_value': vals.get('total_value',0),
222
'meter_id': meter.id,
224
vals.update({'new_value': vals.get('value',0)})
225
elif meter.parent_meter_id.reading_type == 'dec':
226
total_value = meter.total_value + vals['new_value']
227
vals.update({'value': meter.value - vals['new_value']})
228
vals.update({'total_value': total_value})
229
vals.update({'date': time.strftime('%Y-%m-%d')})
230
if meter.date != time.strftime('%Y-%m-%d'):
231
self.pool.get('mro.pm.meter.line').create(cr, uid, {
232
'date': vals.get('date',time.strftime('%Y-%m-%d')),
233
'value': vals.get('value',0),
234
'total_value': vals.get('total_value',0),
235
'meter_id': meter.id,
237
vals.update({'new_value': vals.get('value',0)})
238
elif meter.parent_meter_id.reading_type == 'cng':
239
total_value = meter.total_value + vals['new_value']
240
vals.update({'value': vals['new_value']})
241
vals.update({'total_value': total_value})
242
vals.update({'date': time.strftime('%Y-%m-%d')})
243
if meter.date != time.strftime('%Y-%m-%d'):
244
self.pool.get('mro.pm.meter.line').create(cr, uid, {
245
'date': vals.get('date',time.strftime('%Y-%m-%d')),
246
'value': vals.get('value',0),
247
'total_value': vals.get('total_value',0),
248
'meter_id': meter.id,
250
vals.update({'new_value': 0})
251
return super(mro_pm_meter, self).write(cr, uid, ids, vals, context=context)
253
def onchange_value(self, cr, uid, ids, value):
255
onchange handler of value.
258
fields['value'] = {'date': time.strftime('%Y-%m-%d')}
259
for meter in self.browse(cr, uid, ids):
260
fields['value'].update({'value': value})
261
if meter.state == 'reading':
262
if meter.reading_type == 'inc':
263
if meter.value < value:
264
total_value = meter.total_value + value - meter.value
265
fields['value'].update({'total_value': total_value})
267
fields['value'].update({'new_value': meter.value})
268
fields['value'].update({'value': meter.value})
269
fields['value'].update({'total_value': meter.total_value})
270
fields['value'].update({'date': meter.date})
271
elif meter.reading_type == 'dec':
272
if meter.value > value:
273
total_value = meter.total_value - value + meter.value
274
fields['value'].update({'total_value': total_value})
276
fields['value'].update({'new_value': meter.value})
277
fields['value'].update({'value': meter.value})
278
fields['value'].update({'total_value': meter.total_value})
279
fields['value'].update({'date': meter.date})
281
total_value = meter.total_value + value
282
fields['value'].update({'total_value': total_value})
285
def activate_meter(self, cr, uid, ids, context=None):
289
for meter in self.browse(cr, uid, ids):
290
fields = {'state': 'reading'}
291
if meter.reading_type == 'cng':
292
fields['new_value'] = 0
294
fields['new_value'] = meter.value
295
if meter.reading_type != 'src': fields['parent_meter_id'] = False
296
self.write(cr, uid, ids, fields)
299
def reset_meter(self, cr, uid, ids, context=None):
303
self.write(cr, uid, ids, {'state': 'reset'})
306
def run_meter(self, cr, uid, ids, context=None):
310
self.write(cr, uid, ids, {'state': 'reading'})
314
class mro_pm_meter_line(osv.osv):
315
_name = 'mro.pm.meter.line'
316
_description = 'History of Asset Meter Reading'
318
'date': fields.date('Date', required=True),
319
'value': fields.float('Reading Value', required=True),
320
'total_value': fields.float('Total Value', required=True),
321
'meter_id': fields.many2one('mro.pm.meter', 'Meter', ondelete='restrict'),
327
class mro_pm_meter_ratio(osv.osv):
328
_name = 'mro.pm.meter.ratio'
329
_description = 'Rules for Meter to Meter Ratio'
331
ROUNDING_TYPE_SELECTION = [
334
('round', 'Rounding')
337
RATIO_TYPE_SELECTION = [
338
('bigger', 'Source Bigger'),
339
('smaller', 'Source Smaller')
343
'name': fields.char('Name', size=64, required=True, translate=True),
344
'rounding_type': fields.selection(ROUNDING_TYPE_SELECTION, 'Rounding Type', required=True),
345
'precision': fields.float('Rounding Precision'),
346
'ratio': fields.float('Ratio', required=True),
347
'ratio_type': fields.selection(RATIO_TYPE_SELECTION, 'Ratio Type', required=True),
351
'rounding_type': lambda *a: 'ceil',
352
'ratio_type': lambda *a: 'bigger',
357
def onchange_precision(self, cr, uid, ids, precision):
359
onchange handler of precision.
361
if precision < 0.01: precision = 0.01 #we can't view smaller value
362
return {'value': {'precision': 10**math.floor(math.log10(precision))}}
364
def calculate(self, cr, uid, ratio_id, value):
365
""" Calculate value according to ratio.
368
if not ratio_id or not value:
370
ratio = self.browse(cr, uid, ratio_id)
371
if ratio.ratio_type == 'bigger':
372
value = value/ratio.ratio
374
value = value*ratio.ratio
375
if ratio.rounding_type == 'round':
376
value = round(value / ratio.precision) * ratio.precision
377
elif ratio.rounding_type == 'ceil':
378
value = math.ceil(value / ratio.precision) * ratio.precision
379
elif ratio.rounding_type == 'floor':
380
value = math.floor(value / ratio.precision) * ratio.precision
385
class mro_pm_meter_interval(osv.osv):
386
_name = 'mro.pm.meter.interval'
387
_description = 'Meter interval'
389
def _get_name(self, cr, uid, ids, name, arg, context=None):
391
for interval in self.browse(cr, uid, ids, context=context):
392
if interval.interval_min == interval.interval_max: res[interval.id] = str(interval.interval_min)
393
else: res[interval.id] = str(interval.interval_min) + ' - ' + str(interval.interval_max)
397
'name': fields.function(_get_name, method=True, type='char', string='Interval'),
398
'interval_min': fields.float('Min', required=True),
399
'interval_max': fields.float('Max', required=True),
403
'interval_min': 0.01,
404
'interval_max': 0.01,
407
def onchange_min(self, cr, uid, ids, min, max):
409
onchange handler of min value.
411
if min < 0.01: min = 0.01 #interval can't be 0
412
if min > max: max = min
413
return {'value': {'interval_min': min,'interval_max': max,}}
415
def onchange_max(self, cr, uid, ids, min, max):
417
onchange handler of max value.
419
if max < 0.01: max = 0.01 #interval can't be 0
420
if min > max: min = max
421
return {'value': {'interval_min': min,'interval_max': max,}}
424
class mro_pm_rule(osv.osv):
426
Defines Preventive Maintenance rules.
428
_name = "mro.pm.rule"
429
_description = "Preventive Maintenance Rule"
432
'name': fields.char('Name', size=64),
433
'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the PM without removing it."),
434
'asset_id': fields.many2one('asset.asset', 'Asset', ondelete='restrict', required=True),
435
'meter_id': fields.many2one('mro.pm.meter', 'Meter', ondelete='restrict', required=True),
436
'meter_uom': fields.related('meter_id', 'meter_uom', type='many2one', relation='product.uom', string='Unit of Measure'),
437
'horizon': fields.float('Planning horizon (months)', digits=(12,0), required=True),
438
'pm_rules_line_ids': fields.one2many('mro.pm.rule.line', 'pm_rule_id', 'Tasks'),
445
def onchange_asset(self, cr, uid, ids, rule_lines):
447
onchange handler of asset.
450
value['meter_id'] = False
451
value['pm_rules_line_ids'] = [[2,line[1],line[2]] for line in rule_lines if line[0]]
452
return {'value': value}
454
def onchange_meter(self, cr, uid, ids, meter):
456
onchange handler of meter.
460
meter_uom = self.pool.get('mro.pm.meter').browse(cr, uid, meter).meter_uom.id
461
value['meter_uom'] = meter_uom
462
return {'value': value}
464
def create(self, cr, uid, vals, context=None):
465
if vals.get('name','/')=='/':
466
vals['name'] = self.pool.get('ir.sequence').get(cr, uid, 'mro.pm.rule') or '/'
467
return super(mro_pm_rule, self).create(cr, uid, vals, context=context)
471
class mro_pm_rule_line(osv.osv):
472
_name = 'mro.pm.rule.line'
473
_description = 'Rule for Task'
475
'task_id': fields.many2one('mro.task', 'Task', ondelete='restrict', required=True),
476
'meter_interval_id': fields.many2one('mro.pm.meter.interval', 'Meter Interval', ondelete='restrict', required=True),
477
'pm_rule_id': fields.many2one('mro.pm.rule', 'PM Rule', ondelete='restrict'),
482
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
b'\\ No newline at end of file'