From 41d702be503bf4406ae2102f919c2702f1696bc5 Mon Sep 17 00:00:00 2001 From: Milan Date: Sat, 2 Nov 2024 15:11:47 +0100 Subject: [PATCH] [MIG] maintenance_usage 17.0 Installs Automated tests pass No further functionality testing so far --- maintenance_usage/README.rst | 41 +++++ maintenance_usage/__init__.py | 1 + maintenance_usage/__manifest__.py | 27 +++ maintenance_usage/models/__init__.py | 1 + maintenance_usage/models/maintenance.py | 136 +++++++++++++++ .../security/ir.model.access.csv | 3 + maintenance_usage/tests/__init__.py | 1 + .../tests/test_maintenance_usage.py | 43 +++++ maintenance_usage/views/maintenance_views.xml | 160 ++++++++++++++++++ 9 files changed, 413 insertions(+) create mode 100644 maintenance_usage/README.rst create mode 100644 maintenance_usage/__init__.py create mode 100644 maintenance_usage/__manifest__.py create mode 100644 maintenance_usage/models/__init__.py create mode 100644 maintenance_usage/models/maintenance.py create mode 100644 maintenance_usage/security/ir.model.access.csv create mode 100644 maintenance_usage/tests/__init__.py create mode 100644 maintenance_usage/tests/test_maintenance_usage.py create mode 100644 maintenance_usage/views/maintenance_views.xml diff --git a/maintenance_usage/README.rst b/maintenance_usage/README.rst new file mode 100644 index 00000000..865420ea --- /dev/null +++ b/maintenance_usage/README.rst @@ -0,0 +1,41 @@ +************************* +Hibou - Maintenance Usage +************************* + +Keep track of usage on different types of equipment. + +For more information and add-ons, visit `Hibou.io `_. + +============= +Main Features +============= + +* New Model: Maintenance Usage Log +* Adds new fields for usage on equipments. +* Adds new descriptive UOM on categories. +* Allows users to create preventative maintenance requests based on usage. +* Creates Maintenance Log based on changes in usage or employee ownership, to provide a report on equipment changes over time. + + +.. image:: https://user-images.githubusercontent.com/15882954/41305818-f62a43b6-6e28-11e8-9d30-80d06b273354.png + :alt: 'Equipment Detail' + :width: 988 + :align: left + +New Equipment Usage View + +.. image:: https://user-images.githubusercontent.com/15882954/41305848-09a038ec-6e29-11e8-9ad5-7b3d34bd7b64.png + :alt: 'Equipment Usage Detail' + :width: 988 + :align: left + + + + +======= +License +======= + +Please see `LICENSE `_. + +Copyright Hibou Corp. 2018 diff --git a/maintenance_usage/__init__.py b/maintenance_usage/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/maintenance_usage/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/maintenance_usage/__manifest__.py b/maintenance_usage/__manifest__.py new file mode 100644 index 00000000..f947e7c6 --- /dev/null +++ b/maintenance_usage/__manifest__.py @@ -0,0 +1,27 @@ +{ + 'name': 'Equipment Usage', + 'version': '17.0.1.0.0', + 'author': 'Hibou Corp. ', + 'category': 'Human Resources', + 'summary': 'Keep track of usage on different types of equipment.', + 'description': """ +Equipment Usage +=============== + +Keep track of usage on different types of equipment. Adds fields for usage on equipments +and descriptive UOM on categories. + +Create preventative maintenance requests based on usage. +""", + 'website': 'https://hibou.io/', + 'depends': [ + 'hr_maintenance', + 'product', + ], + 'data': [ + 'security/ir.model.access.csv', + 'views/maintenance_views.xml', + ], + 'installable': True, + 'auto_install': False, +} \ No newline at end of file diff --git a/maintenance_usage/models/__init__.py b/maintenance_usage/models/__init__.py new file mode 100644 index 00000000..12bf298f --- /dev/null +++ b/maintenance_usage/models/__init__.py @@ -0,0 +1 @@ +from . import maintenance diff --git a/maintenance_usage/models/maintenance.py b/maintenance_usage/models/maintenance.py new file mode 100644 index 00000000..3622d5a8 --- /dev/null +++ b/maintenance_usage/models/maintenance.py @@ -0,0 +1,136 @@ +from math import floor +from odoo import _, api, fields, models + + +class MaintenanceEquipmentCategory(models.Model): + _inherit = 'maintenance.equipment.category' + + usage_uom_id = fields.Many2one('uom.uom', string='Usage UOM') + + +class MaintenanceEquipment(models.Model): + _inherit = 'maintenance.equipment' + + employee_id = fields.Many2one(track_visibility=False) + department_id = fields.Many2one(track_visibility=False) + usage_qty = fields.Float(string='Usage', default=0.0) + usage_uom_id = fields.Many2one('uom.uom', related='category_id.usage_uom_id') + usage_log_ids = fields.One2many('maintenance.usage.log', 'equipment_id', string='Usage') + usage_count = fields.Integer(string='Usage Count', compute='_compute_usage_count') + maintenance_usage = fields.Float(string='Preventative Usage') + period = fields.Integer(compute='_compute_period', string='Days betweeen each preventive maintenance', + help='Computed based on scheduled recurring maintenance.') + + def _compute_usage_count(self): + for equipment in self: + equipment.usage_count = len(equipment.usage_log_ids) + + def _compute_period(self): + for equipment in self: + recurring = equipment.maintenance_ids.filtered('recurring_maintenance') + equipment.period = min(recurring.mapped('period'), default=0) + + @api.model + def create(self, values): + record = super(MaintenanceEquipment, self).create(values) + # create first usage record + record._log_usage() + return record + + def write(self, values): + usage_qty = values.get('usage_qty') + employee_id = values.get('employee_id') + department_id = values.get('department_id') + if any((usage_qty, employee_id, department_id)): + for equipment in self: + log_values = {} + + if (equipment.usage_qty is not None + and usage_qty is not None + and abs(usage_qty - equipment.usage_qty) > 0.0001): + log_values['qty'] = usage_qty + if employee_id != equipment.employee_id.id: + log_values['employee_id'] = employee_id + if department_id != equipment.department_id.id: + log_values['department_id'] = department_id + + if log_values: + equipment._log_usage(values=log_values) + + # Check to have the original fields before write. + self._check_maintenance_usage(usage_qty) + + result = super(MaintenanceEquipment, self).write(values) + return result + + def _log_usage(self, values=None): + if not values: + values = {} + values['equipment_id'] = self.id + values['date'] = fields.Datetime.now() + self.env['maintenance.usage.log'].create(values) + + def _check_maintenance_usage(self, usage_qty): + for e in self.filtered(lambda e: e.maintenance_usage): + try: + if floor(e.usage_qty / e.maintenance_usage) != floor(usage_qty / e.maintenance_usage): + next_requests = self.env['maintenance.request'].search([('stage_id.done', '=', False), + ('equipment_id', '=', e.id), + ('maintenance_type', '=', 'preventive'), + ]) + if not next_requests: + e._create_new_request(fields.Date.today()) + except TypeError: + pass + + # Ported from odoo 13.0 as it has been removed since + def _create_new_request(self, date): + self.ensure_one() + self.env['maintenance.request'].create({ + 'name': _('Preventive Maintenance - %s') % self.name, + 'request_date': date, + 'schedule_date': date, + 'category_id': self.category_id.id, + 'equipment_id': self.id, + 'maintenance_type': 'preventive', + 'owner_user_id': self.owner_user_id.id, + 'user_id': self.technician_user_id.id, + 'maintenance_team_id': self.maintenance_team_id.id, + 'company_id': self.company_id.id or self.env.company.id + }) + + def action_open_usage_log(self): + for equipment in self: + action = self.env.ref('maintenance_usage.maintenance_usage_log_action_reports').read()[0] + action['domain'] = [('equipment_id', '=', equipment.id)] + action['context'] = { + 'default_equipment_id': equipment.id, + 'default_employee_id': equipment.employee_id.id, + 'default_department_id': equipment.department_id.id, + 'default_qty': equipment.usage_qty, + } + return action + + +class MaintenanceUsageLog(models.Model): + _name = 'maintenance.usage.log' + _order = 'date DESC' + _log_access = False + + date = fields.Datetime(string='Date', default=fields.Datetime.now) + equipment_id = fields.Many2one('maintenance.equipment', string='Equipment', required=True) + employee_id = fields.Many2one('hr.employee', string='Employee') + department_id = fields.Many2one('hr.department', string='Department') + qty = fields.Float(string='Quantity') + uom_id = fields.Many2one(string='Unit of Measure', related='equipment_id.category_id.usage_uom_id') + + @api.model + def create(self, values): + equipment = self.env['maintenance.equipment'].browse(values.get('equipment_id')) + if not values.get('employee_id'): + values['employee_id'] = equipment.employee_id.id + if not values.get('department_id'): + values['department_id'] = equipment.department_id.id + if not values.get('qty'): + values['qty'] = equipment.usage_qty + return super(MaintenanceUsageLog, self).create(values) diff --git a/maintenance_usage/security/ir.model.access.csv b/maintenance_usage/security/ir.model.access.csv new file mode 100644 index 00000000..8c7451e1 --- /dev/null +++ b/maintenance_usage/security/ir.model.access.csv @@ -0,0 +1,3 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"manage_maintenance_usage_log","manage maintenance.usage.log","model_maintenance_usage_log","maintenance.group_equipment_manager",1,1,1,1 +"access_maintenance_usage_log","access maintenance.usage.log","model_maintenance_usage_log","base.group_user",1,0,1,0 diff --git a/maintenance_usage/tests/__init__.py b/maintenance_usage/tests/__init__.py new file mode 100644 index 00000000..bd38ce7a --- /dev/null +++ b/maintenance_usage/tests/__init__.py @@ -0,0 +1 @@ +from . import test_maintenance_usage diff --git a/maintenance_usage/tests/test_maintenance_usage.py b/maintenance_usage/tests/test_maintenance_usage.py new file mode 100644 index 00000000..d0d9992f --- /dev/null +++ b/maintenance_usage/tests/test_maintenance_usage.py @@ -0,0 +1,43 @@ +from odoo.tests import common + + +class TestMaintenanceUsage(common.TransactionCase): + """Tests for usage on creation and update + """ + + def test_create(self): + test_usage = 21.0 + equipment = self.env['maintenance.equipment'].create({ + 'name': 'Monitor', + 'usage_qty': test_usage, + }) + + self.assertTrue(equipment.usage_log_ids) + self.assertEqual(equipment.usage_log_ids[0].qty, test_usage) + + def test_update(self): + test_usage = 21.0 + test_usage2 = 50.1 + equipment = self.env['maintenance.equipment'].create({ + 'name': 'Monitor', + 'usage_qty': test_usage, + }) + equipment.usage_qty = test_usage2 + updated_usage = equipment.usage_log_ids.filtered(lambda u: abs(u.qty - test_usage2) < 0.01) + + self.assertTrue(updated_usage) + self.assertAlmostEqual(updated_usage[0].qty, test_usage2) + + def test_maintenance_usage(self): + test_usage = 21.0 + test_usage2 = 50.1 + equipment = self.env['maintenance.equipment'].create({ + 'name': 'Monitor', + 'usage_qty': test_usage, + 'maintenance_usage': 20.0, + 'maintenance_team_id': self.env['maintenance.team'].search([], limit=1).id + }) + self.assertFalse(equipment.maintenance_ids) + + equipment.usage_qty = test_usage2 + self.assertTrue(equipment.maintenance_ids) diff --git a/maintenance_usage/views/maintenance_views.xml b/maintenance_usage/views/maintenance_views.xml new file mode 100644 index 00000000..052ad953 --- /dev/null +++ b/maintenance_usage/views/maintenance_views.xml @@ -0,0 +1,160 @@ + + + + maintenance.equipment.category.inherited + maintenance.equipment.category + + + + + + + + + + + + equipment.form.inherited + maintenance.equipment + + + + + + + + + + + + + + + + period != 0 or maintenance_usage != 0 + + + + + + + equipment.usage.log.search + maintenance.usage.log + + + + + + + + + + + + + + + + + equipment.usage.log.form + maintenance.usage.log + +
+
+ + + + + + + + + + + + + + + + + + equipment.usage.log.tree + maintenance.usage.log + + + + + + + + + + + + + + equipment.usage.log.graph + maintenance.usage.log + + + + + + + + + + equipment.usage.log.pivot + maintenance.usage.log + + + + + + + + + + equipment.usage.log.calendar + maintenance.usage.log + + + + + + + + + + + Equipment Usage + maintenance.usage.log + tree,form,graph,pivot,calendar + +

+ No usage. +

+
+
+ + + \ No newline at end of file