371 lines
18 KiB
Python
371 lines
18 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from datetime import date, datetime, timedelta
|
|
|
|
from odoo import api, fields, models, _
|
|
from odoo.exceptions import UserError
|
|
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
|
|
|
|
|
|
class MaintenanceStage(models.Model):
|
|
""" Model for case stages. This models the main stages of a Maintenance Request management flow. """
|
|
|
|
_name = 'maintenance.stage'
|
|
_description = 'Maintenance Stage'
|
|
_order = 'sequence, id'
|
|
|
|
name = fields.Char('Name', required=True, translate=True)
|
|
sequence = fields.Integer('Sequence', default=20)
|
|
fold = fields.Boolean('Folded in Maintenance Pipe')
|
|
done = fields.Boolean('Request Done')
|
|
|
|
|
|
class MaintenanceEquipmentCategory(models.Model):
|
|
_name = 'maintenance.equipment.category'
|
|
_inherit = ['mail.alias.mixin', 'mail.thread']
|
|
_description = 'Asset Category'
|
|
|
|
@api.one
|
|
@api.depends('equipment_ids')
|
|
def _compute_fold(self):
|
|
self.fold = False if self.equipment_count else True
|
|
|
|
name = fields.Char('Category Name', required=True, translate=True)
|
|
technician_user_id = fields.Many2one('res.users', 'Responsible', track_visibility='onchange', default=lambda self: self.env.uid, oldname='user_id')
|
|
color = fields.Integer('Color Index')
|
|
note = fields.Text('Comments', translate=True)
|
|
equipment_ids = fields.One2many('maintenance.equipment', 'category_id', string='Equipments', copy=False)
|
|
equipment_count = fields.Integer(string="Equipment", compute='_compute_equipment_count')
|
|
maintenance_ids = fields.One2many('maintenance.request', 'category_id', copy=False)
|
|
maintenance_count = fields.Integer(string="Maintenance", compute='_compute_maintenance_count')
|
|
alias_id = fields.Many2one(
|
|
'mail.alias', 'Alias', ondelete='cascade', required=True,
|
|
help="Email alias for this equipment category. New emails will automatically "
|
|
"create new maintenance request for this equipment category.")
|
|
fold = fields.Boolean(string='Folded in Maintenance Pipe', compute='_compute_fold', store=True)
|
|
|
|
@api.multi
|
|
def _compute_equipment_count(self):
|
|
equipment_data = self.env['maintenance.equipment'].read_group([('category_id', 'in', self.ids)], ['category_id'], ['category_id'])
|
|
mapped_data = dict([(m['category_id'][0], m['category_id_count']) for m in equipment_data])
|
|
for category in self:
|
|
category.equipment_count = mapped_data.get(category.id, 0)
|
|
|
|
@api.multi
|
|
def _compute_maintenance_count(self):
|
|
maintenance_data = self.env['maintenance.request'].read_group([('category_id', 'in', self.ids)], ['category_id'], ['category_id'])
|
|
mapped_data = dict([(m['category_id'][0], m['category_id_count']) for m in maintenance_data])
|
|
for category in self:
|
|
category.maintenance_count = mapped_data.get(category.id, 0)
|
|
|
|
@api.model
|
|
def create(self, vals):
|
|
self = self.with_context(alias_model_name='maintenance.request', alias_parent_model_name=self._name)
|
|
if not vals.get('alias_name'):
|
|
vals['alias_name'] = vals.get('name')
|
|
category_id = super(MaintenanceEquipmentCategory, self).create(vals)
|
|
category_id.alias_id.write({'alias_parent_thread_id': category_id.id, 'alias_defaults': {'category_id': category_id.id}})
|
|
return category_id
|
|
|
|
@api.multi
|
|
def unlink(self):
|
|
MailAlias = self.env['mail.alias']
|
|
for category in self:
|
|
if category.equipment_ids or category.maintenance_ids:
|
|
raise UserError(_("You cannot delete an equipment category containing equipments or maintenance requests."))
|
|
MailAlias += category.alias_id
|
|
res = super(MaintenanceEquipmentCategory, self).unlink()
|
|
MailAlias.unlink()
|
|
return res
|
|
|
|
def get_alias_model_name(self, vals):
|
|
return vals.get('alias_model', 'maintenance.equipment')
|
|
|
|
def get_alias_values(self):
|
|
values = super(MaintenanceEquipmentCategory, self).get_alias_values()
|
|
values['alias_defaults'] = {'category_id': self.id}
|
|
return values
|
|
|
|
|
|
class MaintenanceEquipment(models.Model):
|
|
_name = 'maintenance.equipment'
|
|
_inherit = ['mail.thread']
|
|
_description = 'Equipment'
|
|
|
|
@api.multi
|
|
def _track_subtype(self, init_values):
|
|
self.ensure_one()
|
|
if 'owner_user_id' in init_values and self.owner_user_id:
|
|
return 'maintenance.mt_mat_assign'
|
|
return super(MaintenanceEquipment, self)._track_subtype(init_values)
|
|
|
|
@api.multi
|
|
def name_get(self):
|
|
result = []
|
|
for record in self:
|
|
if record.name and record.serial_no:
|
|
result.append((record.id, record.name + '/' + record.serial_no))
|
|
if record.name and not record.serial_no:
|
|
result.append((record.id, record.name))
|
|
return result
|
|
|
|
@api.model
|
|
def name_search(self, name, args=None, operator='ilike', limit=100):
|
|
args = args or []
|
|
recs = self.browse()
|
|
if name:
|
|
recs = self.search([('name', '=', name)] + args, limit=limit)
|
|
if not recs:
|
|
recs = self.search([('name', operator, name)] + args, limit=limit)
|
|
return recs.name_get()
|
|
|
|
name = fields.Char('Equipment Name', required=True, translate=True)
|
|
active = fields.Boolean(default=True)
|
|
technician_user_id = fields.Many2one('res.users', string='Technician', track_visibility='onchange', oldname='user_id')
|
|
owner_user_id = fields.Many2one('res.users', string='Owner', track_visibility='onchange')
|
|
category_id = fields.Many2one('maintenance.equipment.category', string='Equipment Category', track_visibility='onchange')
|
|
partner_id = fields.Many2one('res.partner', string='Vendor', domain="[('supplier', '=', 1)]")
|
|
partner_ref = fields.Char('Vendor Reference')
|
|
location = fields.Char('Location')
|
|
model = fields.Char('Model')
|
|
serial_no = fields.Char('Serial Number', copy=False)
|
|
assign_date = fields.Date('Assigned Date', track_visibility='onchange')
|
|
cost = fields.Float('Cost')
|
|
note = fields.Text('Note')
|
|
warranty = fields.Date('Warranty')
|
|
color = fields.Integer('Color Index')
|
|
scrap_date = fields.Date('Scrap Date')
|
|
maintenance_ids = fields.One2many('maintenance.request', 'equipment_id')
|
|
maintenance_count = fields.Integer(compute='_compute_maintenance_count', string="Maintenance", store=True)
|
|
maintenance_open_count = fields.Integer(compute='_compute_maintenance_count', string="Current Maintenance", store=True)
|
|
period = fields.Integer('Days between each preventive maintenance')
|
|
next_action_date = fields.Date(compute='_compute_next_maintenance', string='Date of the next preventive maintenance', store=True)
|
|
maintenance_team_id = fields.Many2one('maintenance.team', string='Maintenance Team')
|
|
maintenance_duration = fields.Float(help="Maintenance Duration in minutes and seconds.")
|
|
|
|
@api.depends('period', 'maintenance_open_count', 'maintenance_ids.request_date')
|
|
def _compute_next_maintenance(self):
|
|
for equipment in self:
|
|
create_date = equipment.create_date and datetime.strptime(equipment.create_date, DEFAULT_SERVER_DATETIME_FORMAT)
|
|
if equipment.period:
|
|
next_date = create_date
|
|
if equipment.maintenance_ids:
|
|
maintenance = equipment.maintenance_ids.sorted(lambda x: x.request_date)[0]
|
|
next_date = maintenance.request_date and datetime.strptime(maintenance.request_date, DEFAULT_SERVER_DATE_FORMAT) or create_date
|
|
equipment.next_action_date = next_date and (next_date + timedelta(days=equipment.period)).strftime(DEFAULT_SERVER_DATE_FORMAT)
|
|
|
|
@api.one
|
|
@api.depends('maintenance_ids.stage_id.done')
|
|
def _compute_maintenance_count(self):
|
|
self.maintenance_count = len(self.maintenance_ids)
|
|
self.maintenance_open_count = len(self.maintenance_ids.filtered(lambda x: not x.stage_id.done))
|
|
|
|
@api.onchange('category_id')
|
|
def _onchange_category_id(self):
|
|
self.technician_user_id = self.category_id.technician_user_id
|
|
|
|
_sql_constraints = [
|
|
('serial_no', 'unique(serial_no)', "Another asset already exists with this serial number!"),
|
|
]
|
|
|
|
@api.model
|
|
def create(self, vals):
|
|
equipment = super(MaintenanceEquipment, self).create(vals)
|
|
if equipment.owner_user_id:
|
|
equipment.message_subscribe_users(user_ids=[equipment.owner_user_id.id])
|
|
return equipment
|
|
|
|
@api.multi
|
|
def write(self, vals):
|
|
if vals.get('owner_user_id'):
|
|
self.message_subscribe_users(user_ids=[vals['owner_user_id']])
|
|
return super(MaintenanceEquipment, self).write(vals)
|
|
|
|
@api.multi
|
|
def _read_group_category_ids(self, domain, read_group_order=None, access_rights_uid=None):
|
|
""" Read group customization in order to display all the category in the
|
|
kanban view, even if they are empty
|
|
"""
|
|
category_obj = self.env['maintenance.equipment.category']
|
|
order = category_obj._order
|
|
access_rights_uid = access_rights_uid or self._uid
|
|
if read_group_order == 'category_id desc':
|
|
order = '%s desc' % order
|
|
|
|
category_ids = category_obj._search([], order=order, access_rights_uid=access_rights_uid)
|
|
result = [category.name_get()[0] for category in category_obj.browse(category_ids)]
|
|
# restore order of the search
|
|
result.sort(lambda x, y: cmp(category_ids.index(x[0]), category_ids.index(y[0])))
|
|
|
|
fold = {}
|
|
for category in category_obj.browse(category_ids):
|
|
fold[category.id] = category.fold
|
|
return result, fold
|
|
|
|
@api.model
|
|
def _cron_generate_requests(self):
|
|
for equipment in self.search([]):
|
|
if equipment.period and equipment.next_action_date == date.today().strftime(DEFAULT_SERVER_DATE_FORMAT):
|
|
self.env['maintenance.request'].create({
|
|
'name': _('Preventive Maintenance - %s' % equipment.next_action_date),
|
|
'request_date': equipment.next_action_date,
|
|
'category_id': equipment.category_id.id,
|
|
'equipment_id': equipment.id,
|
|
'maintenance_type': 'preventive',
|
|
})
|
|
|
|
_group_by_full = {
|
|
'category_id': _read_group_category_ids
|
|
}
|
|
|
|
|
|
class MaintenanceRequest(models.Model):
|
|
_name = 'maintenance.request'
|
|
_inherit = ['mail.thread']
|
|
_description = 'Maintenance Requests'
|
|
_order = "id desc"
|
|
|
|
@api.returns('self')
|
|
def _default_stage(self):
|
|
return self.env['maintenance.stage'].search([], limit=1)
|
|
|
|
@api.multi
|
|
def _track_subtype(self, init_values):
|
|
self.ensure_one()
|
|
if 'stage_id' in init_values and self.stage_id.sequence <= 1:
|
|
return 'maintenance.mt_req_created'
|
|
elif 'stage_id' in init_values and self.stage_id.sequence > 1:
|
|
return 'maintenance.mt_req_status'
|
|
return super(MaintenanceRequest, self)._track_subtype(init_values)
|
|
|
|
def _get_default_team_id(self):
|
|
return self.env.ref('maintenance.equipment_team_maintenance', raise_if_not_found=False)
|
|
|
|
name = fields.Char('Subjects', required=True)
|
|
description = fields.Text('Description')
|
|
request_date = fields.Date('Request Date', track_visibility='onchange', default=fields.Date.context_today)
|
|
|
|
owner_user_id = fields.Many2one('res.users', string='Created by', default=lambda s: s.env.uid)
|
|
category_id = fields.Many2one('maintenance.equipment.category', related='equipment_id.category_id', string='Category', store=True, readonly=True)
|
|
equipment_id = fields.Many2one('maintenance.equipment', string='Equipment', select=True)
|
|
technician_user_id = fields.Many2one('res.users', string='Owner', track_visibility='onchange', oldname='user_id')
|
|
stage_id = fields.Many2one('maintenance.stage', string='Stage', track_visibility='onchange', default=_default_stage)
|
|
priority = fields.Selection([('0', 'Very Low'), ('1', 'Low'), ('2', 'Normal'), ('3', 'High')], string='Priority')
|
|
color = fields.Integer('Color Index')
|
|
close_date = fields.Date('Close Date')
|
|
kanban_state = fields.Selection([('normal', 'In Progress'), ('blocked', 'Blocked'), ('done', 'Ready for next stage')],
|
|
string='Kanban State', required=True, default='normal', track_visibility='onchange')
|
|
# active = fields.Boolean(default=True, help="Set active to false to hide the maintenance request without deleting it.")
|
|
archive = fields.Boolean(default=False, help="Set archive to true to hide the maintenance request without deleting it.")
|
|
maintenance_type = fields.Selection([('corrective', 'Corrective'), ('preventive', 'Preventive')], string='Maintenance Type', default="corrective")
|
|
schedule_date = fields.Datetime('Scheduled Date')
|
|
maintenance_team_id = fields.Many2one('maintenance.team', string='Team', required=True, default=_get_default_team_id)
|
|
duration = fields.Float(help="Duration in minutes and seconds.")
|
|
|
|
@api.multi
|
|
def archive_equipment_request(self):
|
|
self.write({'archive': True})
|
|
|
|
@api.multi
|
|
def reset_equipment_request(self):
|
|
""" Reinsert the maintenance request into the maintenance pipe in the first stage"""
|
|
first_stage_obj = self.env['maintenance.stage'].search([], order="sequence asc", limit=1)
|
|
# self.write({'active': True, 'stage_id': first_stage_obj.id})
|
|
self.write({'archive': False, 'stage_id': first_stage_obj.id})
|
|
|
|
@api.onchange('equipment_id')
|
|
def onchange_equipment_id(self):
|
|
if self.equipment_id:
|
|
self.technician_user_id = self.equipment_id.technician_user_id if self.equipment_id.technician_user_id else self.equipment_id.category_id.technician_user_id
|
|
self.category_id = self.equipment_id.category_id
|
|
if self.equipment_id.maintenance_team_id:
|
|
self.maintenance_team_id = self.equipment_id.maintenance_team_id.id
|
|
|
|
@api.onchange('category_id')
|
|
def onchange_category_id(self):
|
|
if not self.technician_user_id or not self.equipment_id or (self.technician_user_id and not self.equipment_id.technician_user_id):
|
|
self.technician_user_id = self.category_id.technician_user_id
|
|
|
|
@api.model
|
|
def create(self, vals):
|
|
# context: no_log, because subtype already handle this
|
|
self = self.with_context(mail_create_nolog=True)
|
|
request = super(MaintenanceRequest, self).create(vals)
|
|
if request.owner_user_id:
|
|
request.message_subscribe_users(user_ids=[request.owner_user_id.id])
|
|
if request.equipment_id and not request.maintenance_team_id:
|
|
request.maintenance_team_id = request.equipment_id.maintenance_team_id
|
|
return request
|
|
|
|
@api.multi
|
|
def write(self, vals):
|
|
# Overridden to reset the kanban_state to normal whenever
|
|
# the stage (stage_id) of the Maintenance Request changes.
|
|
if vals and 'kanban_state' not in vals and 'stage_id' in vals:
|
|
vals['kanban_state'] = 'normal'
|
|
if vals.get('owner_user_id'):
|
|
self.message_subscribe_users(user_ids=[vals['owner_user_id']])
|
|
res = super(MaintenanceRequest, self).write(vals)
|
|
if self.stage_id.done and 'stage_id' in vals:
|
|
self.write({'close_date': fields.Date.today()})
|
|
return res
|
|
|
|
@api.multi
|
|
def _read_group_stage_ids(self, domain, read_group_order=None, access_rights_uid=None):
|
|
""" Read group customization in order to display all the stages in the
|
|
kanban view, even if they are empty
|
|
"""
|
|
stage_obj = self.env['maintenance.stage']
|
|
order = stage_obj._order
|
|
access_rights_uid = access_rights_uid or self._uid
|
|
|
|
if read_group_order == 'stage_id desc':
|
|
order = '%s desc' % order
|
|
|
|
stage_ids = stage_obj._search([], order=order, access_rights_uid=access_rights_uid)
|
|
result = [stage.name_get()[0] for stage in stage_obj.browse(stage_ids)]
|
|
|
|
# restore order of the search
|
|
result.sort(lambda x, y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
|
|
|
|
fold = {}
|
|
for stage in stage_obj.browse(stage_ids):
|
|
fold[stage.id] = stage.fold or False
|
|
return result, fold
|
|
|
|
_group_by_full = {
|
|
'stage_id': _read_group_stage_ids
|
|
}
|
|
|
|
|
|
class MaintenanceTeam(models.Model):
|
|
_name = 'maintenance.team'
|
|
_description = 'Maintenance Teams'
|
|
|
|
name = fields.Char(required=True)
|
|
partner_id = fields.Many2one('res.partner', string='Subcontracting Partner')
|
|
color = fields.Integer(default=0)
|
|
request_ids = fields.One2many('maintenance.request', 'maintenance_team_id', copy=False)
|
|
equipment_ids = fields.One2many('maintenance.equipment', 'maintenance_team_id', copy=False)
|
|
|
|
# For the dashboard only
|
|
todo_request_ids = fields.One2many('maintenance.request', copy=False, compute='_compute_todo_requests')
|
|
todo_request_count = fields.Integer(compute='_compute_todo_requests')
|
|
todo_request_count_date = fields.Integer(compute='_compute_todo_requests')
|
|
todo_request_count_high_priority = fields.Integer(compute='_compute_todo_requests')
|
|
todo_request_count_block = fields.Integer(compute='_compute_todo_requests')
|
|
|
|
@api.one
|
|
@api.depends('request_ids.stage_id.done')
|
|
def _compute_todo_requests(self):
|
|
self.todo_request_ids = self.request_ids.filtered(lambda e: e.stage_id.done==False)
|
|
self.todo_request_count = len(self.todo_request_ids)
|
|
self.todo_request_count_date = len(self.todo_request_ids.filtered(lambda e: e.schedule_date != False))
|
|
self.todo_request_count_high_priority = len(self.todo_request_ids.filtered(lambda e: e.priority == '3'))
|
|
self.todo_request_count_block = len(self.todo_request_ids.filtered(lambda e: e.kanban_state == 'blocked'))
|
|
|
|
@api.one
|
|
@api.depends('equipment_ids')
|
|
def _compute_equipment(self):
|
|
self.equipment_count = len(self.equipment_ids)
|