diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..e0ee1821 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "external/hibou-oca/account-analytic"] + path = external/hibou-oca/account-analytic + url = https://github.com/hibou-io/oca-account-analytic.git diff --git a/account_payment_disperse/README.rst b/account_payment_disperse/README.rst index 8b4a87d1..70b46e87 100644 --- a/account_payment_disperse/README.rst +++ b/account_payment_disperse/README.rst @@ -22,6 +22,11 @@ Main Features * New buttons to easily fill or pre-populate amount remaining or amount due. * Tests to ensure that all the stock payment mechanisms work, and that we make the right reconciliations. +.. image:: https://user-images.githubusercontent.com/15882954/39149575-62a0a1d6-46f4-11e8-8e59-b315cf8f9277.png + :alt: 'Register Payment Detail' + :width: 988 + :align: left + ============= Known Issues ============= @@ -35,3 +40,4 @@ License Please see `LICENSE `_. Copyright Hibou Corp. 2018 + diff --git a/delivery_partner_dhl/__manifest__.py b/delivery_partner_dhl/__manifest__.py index babeee2c..26f40db0 100755 --- a/delivery_partner_dhl/__manifest__.py +++ b/delivery_partner_dhl/__manifest__.py @@ -4,7 +4,7 @@ 'version': '11.0.1.0.0', 'category': 'Stock', 'sequence': 95, - 'summary': 'Fedex Partner Shipping Accounts', + 'summary': 'DHL Partner Shipping Accounts', 'description': """ """, 'website': 'https://hibou.io/', diff --git a/external/hibou-oca/account-analytic b/external/hibou-oca/account-analytic new file mode 160000 index 00000000..7e06779e --- /dev/null +++ b/external/hibou-oca/account-analytic @@ -0,0 +1 @@ +Subproject commit 7e06779e239d05bdeeccd52ac3de21942ef2b48b diff --git a/hr_department_project/__init__.py b/hr_department_project/__init__.py new file mode 100644 index 00000000..a0fdc10f --- /dev/null +++ b/hr_department_project/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import models diff --git a/hr_department_project/__manifest__.py b/hr_department_project/__manifest__.py new file mode 100644 index 00000000..9169d5d7 --- /dev/null +++ b/hr_department_project/__manifest__.py @@ -0,0 +1,24 @@ +{ + 'name': 'HR Department Project', + 'version': '11.0.1.0.0', + 'author': 'Hibou Corp. ', + 'category': 'Human Resources', + 'summary': 'Provide default project per Department', + 'description': """ +HR Department Project +===================== + +Define a 'default project' for every department. This is a bridge module to allow other modules to use this behavior. +""", + 'website': 'https://hibou.io/', + 'depends': [ + 'project', + 'hr', + ], + 'data': [ + 'views/hr_views.xml', + 'views/project_views.xml', + ], + 'installable': True, + 'auto_install': False, +} diff --git a/hr_department_project/models/__init__.py b/hr_department_project/models/__init__.py new file mode 100644 index 00000000..4723a666 --- /dev/null +++ b/hr_department_project/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +from . import project +from . import hr diff --git a/hr_department_project/models/hr.py b/hr_department_project/models/hr.py new file mode 100644 index 00000000..2d0fb2f9 --- /dev/null +++ b/hr_department_project/models/hr.py @@ -0,0 +1,22 @@ +from odoo import api, fields, models, _ + + +class Department(models.Model): + _inherit = 'hr.department' + + project_ids = fields.One2many('project.project', 'department_id', string='Projects') + project_count = fields.Integer(compute='_compute_project_count', string='Project Count') + + def _compute_project_count(self): + for department in self: + department.project_count = len(department.with_context(active_test=False).project_ids) + + def project_tree_view(self): + self.ensure_one() + action = self.env.ref('project.open_view_project_all').read()[0] + action['domain'] = [('department_id', '=', self.id)] + action['context'] = { + 'default_department_id': self.id, + 'default_user_id': self.manager_id.id if self.manager_id else 0, + } + return action diff --git a/hr_department_project/models/project.py b/hr_department_project/models/project.py new file mode 100644 index 00000000..e6bd32f1 --- /dev/null +++ b/hr_department_project/models/project.py @@ -0,0 +1,7 @@ +from odoo import api, fields, models, _ + + +class Project(models.Model): + _inherit = 'project.project' + + department_id = fields.Many2one('hr.department', string='Department') diff --git a/hr_department_project/views/hr_views.xml b/hr_department_project/views/hr_views.xml new file mode 100644 index 00000000..609e23aa --- /dev/null +++ b/hr_department_project/views/hr_views.xml @@ -0,0 +1,15 @@ + + + + hr.department.form.inherited + hr.department + + + + + + + + \ No newline at end of file diff --git a/hr_department_project/views/project_views.xml b/hr_department_project/views/project_views.xml new file mode 100644 index 00000000..7c7af6da --- /dev/null +++ b/hr_department_project/views/project_views.xml @@ -0,0 +1,24 @@ + + + + project.project.form.inherited + project.project + + + + + + + + + + project.project.select.inherited + project.project + + + + + + + + \ No newline at end of file diff --git a/maintenance_equipment_charge/__init__.py b/maintenance_equipment_charge/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/maintenance_equipment_charge/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/maintenance_equipment_charge/__manifest__.py b/maintenance_equipment_charge/__manifest__.py new file mode 100644 index 00000000..83c833ff --- /dev/null +++ b/maintenance_equipment_charge/__manifest__.py @@ -0,0 +1,23 @@ +{ + 'name': 'Equipment Charges', + 'version': '11.0.1.0.0', + 'author': 'Hibou Corp. ', + 'category': 'Human Resources', + 'summary': 'Record related equipment charges.', + 'description': """ +Equipment Charges +================= + +Record related equipment charges, for example fuel charges. +""", + 'website': 'https://www.odoo.com/page/manufacturing', + 'depends': [ + 'hr_maintenance', + ], + 'data': [ + 'security/ir.model.access.csv', + 'views/maintenance_views.xml', + ], + 'installable': True, + 'auto_install': False, +} diff --git a/maintenance_equipment_charge/models/__init__.py b/maintenance_equipment_charge/models/__init__.py new file mode 100644 index 00000000..12bf298f --- /dev/null +++ b/maintenance_equipment_charge/models/__init__.py @@ -0,0 +1 @@ +from . import maintenance diff --git a/maintenance_equipment_charge/models/maintenance.py b/maintenance_equipment_charge/models/maintenance.py new file mode 100644 index 00000000..8ca32e7c --- /dev/null +++ b/maintenance_equipment_charge/models/maintenance.py @@ -0,0 +1,46 @@ +from odoo import api, fields, models + + +class EquipmentChargeType(models.Model): + _name = 'maintenance.equipment.charge.type' + + name = fields.Char(string='Charge Type') + uom_id = fields.Many2one('product.uom', string='Charge UOM') + + +class Equipment(models.Model): + _inherit = 'maintenance.equipment' + + charge_ids = fields.One2many('maintenance.equipment.charge', 'equipment_id', 'Charges', copy=False) + charge_count = fields.Integer(string='Charges', compute='_compute_charge_count') + + @api.multi + def _compute_charge_count(self): + for equipment in self: + self.charge_count = len(equipment.charge_ids) + + def action_open_charges(self): + self.ensure_one() + action = self.env.ref('maintenance_equipment_charge.maintenance_charge_action_reports').read()[0] + action['domain'] = [('equipment_id', '=', self.id)] + action['context'] = { + 'default_equipment_id': self.id, + 'default_employee_id': self.employee_id.id, + 'default_department_id': self.department_id.id, + } + return action + + +class EquipmentCharge(models.Model): + _name = 'maintenance.equipment.charge' + _order = 'date DESC' + + name = fields.Char(string='Description') + date = fields.Date(string='Date', default=fields.Date.today, index=True) + equipment_id = fields.Many2one('maintenance.equipment', copy=False) + type_id = fields.Many2one('maintenance.equipment.charge.type', string='Type') + employee_id = fields.Many2one('hr.employee', string='Employee') + department_id = fields.Many2one('hr.department', string='Department') + qty = fields.Float(string='Quantity', default=1.0) + uom_id = fields.Many2one('product.uom', related='type_id.uom_id') + amount = fields.Float(string='Amount') diff --git a/maintenance_equipment_charge/security/ir.model.access.csv b/maintenance_equipment_charge/security/ir.model.access.csv new file mode 100644 index 00000000..0cc16007 --- /dev/null +++ b/maintenance_equipment_charge/security/ir.model.access.csv @@ -0,0 +1,5 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"manage_maintenance_equipment_charge","manage maintenance.equipment.charge","model_maintenance_equipment_charge","stock.group_stock_manager",1,1,1,1 +"access_maintenance_equipment_charge","access maintenance.equipment.charge","model_maintenance_equipment_charge","base.group_user",1,0,1,0 +"manage_maintenance_equipment_charge_type","manage maintenance.equipment.charge.type","model_maintenance_equipment_charge_type","stock.group_stock_manager",1,1,1,1 +"access_maintenance_equipment_charge_type","access maintenance.equipment.charge.type","model_maintenance_equipment_charge_type","base.group_user",1,0,1,0 \ No newline at end of file diff --git a/maintenance_equipment_charge/tests/__init__.py b/maintenance_equipment_charge/tests/__init__.py new file mode 100644 index 00000000..bd38ce7a --- /dev/null +++ b/maintenance_equipment_charge/tests/__init__.py @@ -0,0 +1 @@ +from . import test_maintenance_usage diff --git a/maintenance_equipment_charge/tests/test_maintenance_usage.py b/maintenance_equipment_charge/tests/test_maintenance_usage.py new file mode 100644 index 00000000..fd60a160 --- /dev/null +++ b/maintenance_equipment_charge/tests/test_maintenance_usage.py @@ -0,0 +1,21 @@ +from odoo.tests import common + + +class TestMaintenanceCharge(common.TransactionCase): + """Tests for charges + """ + + def test_create(self): + test_charge = 21.0 + equipment = self.env['maintenance.equipment'].create({ + 'name': 'Monitor', + }) + + self.assertFalse(equipment.charge_ids) + self.env['maintenance.equipment.charge'].create({ + 'equipment_id': equipment.id, + 'name': 'test', + 'amount': test_charge, + }) + self.assertTrue(equipment.charge_ids) + self.assertAlmostEqual(equipment.charge_ids[0].amount, test_charge) diff --git a/maintenance_equipment_charge/views/maintenance_views.xml b/maintenance_equipment_charge/views/maintenance_views.xml new file mode 100644 index 00000000..00379d1c --- /dev/null +++ b/maintenance_equipment_charge/views/maintenance_views.xml @@ -0,0 +1,184 @@ + + + + Other + + + + equipment.form.inherited + maintenance.equipment + + + + + + + + + + + equipment.charge.search + maintenance.equipment.charge + + + + + + + + + + + + + + + + + + + equipment.charge.form + maintenance.equipment.charge + +
+
+ + + + + + + + + + + + + + + + + + + equipment.charge.tree + maintenance.equipment.charge + + + + + + + + + + + + + + + equipment.charge.graph + maintenance.equipment.charge + + + + + + + + + + + equipment.charge.pivot + maintenance.equipment.charge + + + + + + + + + + + equipment.charge.calendar + maintenance.equipment.charge + + + + + + + + + + + Equipment Charges + maintenance.equipment.charge + tree,form,graph,pivot,calendar + +

+ No charges. +

+
+
+ + + + + + equipment.charge.type.form + maintenance.equipment.charge.type + +
+
+ + + + + + + + + + + + + + equipment.charge.type.tree + maintenance.equipment.charge.type + + + + + + + + + + + Equipment Charge Types + maintenance.equipment.charge.type + tree,form + + + + \ No newline at end of file diff --git a/maintenance_notebook/__init__.py b/maintenance_notebook/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/maintenance_notebook/__init__.py @@ -0,0 +1 @@ + diff --git a/maintenance_notebook/__manifest__.py b/maintenance_notebook/__manifest__.py new file mode 100644 index 00000000..198c0fb6 --- /dev/null +++ b/maintenance_notebook/__manifest__.py @@ -0,0 +1,22 @@ +{ + 'name': 'Maintenance Notebook', + 'version': '11.0.1.0.0', + 'author': 'Hibou Corp. ', + 'category': 'Human Resources', + 'summary': 'Record time on maintenance requests.', + 'description': """ +Maintenance Notebook +==================== + +Adds a 'notebook' XML element to the Maintenance form to add pages in other modules. +""", + 'website': 'https://hibou.io/', + 'depends': [ + 'maintenance', + ], + 'data': [ + 'views/maintenance_views.xml', + ], + 'installable': True, + 'auto_install': False, +} diff --git a/maintenance_notebook/views/maintenance_views.xml b/maintenance_notebook/views/maintenance_views.xml new file mode 100644 index 00000000..e5558e18 --- /dev/null +++ b/maintenance_notebook/views/maintenance_views.xml @@ -0,0 +1,13 @@ + + + + equipment.request.form.notebook + maintenance.request + + + + + + + + \ No newline at end of file diff --git a/maintenance_repair/__init__.py b/maintenance_repair/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/maintenance_repair/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/maintenance_repair/__manifest__.py b/maintenance_repair/__manifest__.py new file mode 100644 index 00000000..e05f64b7 --- /dev/null +++ b/maintenance_repair/__manifest__.py @@ -0,0 +1,25 @@ +{ + 'name': 'Equipment Repair', + 'version': '11.0.1.0.0', + 'author': 'Hibou Corp. ', + 'category': 'Human Resources', + 'summary': 'Consume products on Maintenance Requests', + 'description': """ +Equipment Repair +================ + +Keep track of parts required to repair equipment. +""", + 'website': 'https://hibou.io/', + 'depends': [ + 'stock', + 'maintenance_notebook', + 'hr_department_project', + ], + 'data': [ + 'security/ir.model.access.csv', + 'views/maintenance_views.xml', + ], + 'installable': True, + 'auto_install': False, +} diff --git a/maintenance_repair/models/__init__.py b/maintenance_repair/models/__init__.py new file mode 100644 index 00000000..12bf298f --- /dev/null +++ b/maintenance_repair/models/__init__.py @@ -0,0 +1 @@ +from . import maintenance diff --git a/maintenance_repair/models/maintenance.py b/maintenance_repair/models/maintenance.py new file mode 100644 index 00000000..9c19152a --- /dev/null +++ b/maintenance_repair/models/maintenance.py @@ -0,0 +1,142 @@ +from odoo import api, fields, models, _ +from odoo.addons import decimal_precision as dp +from odoo.exceptions import UserError, ValidationError + + +class StockMove(models.Model): + _inherit = 'stock.move' + + maintenance_request_id = fields.Many2one('maintenance.request') + + +class MaintenanceTeam(models.Model): + _inherit = 'maintenance.team' + + repair_location_id = fields.Many2one('stock.location', string='Default Repair Parts Source') + repair_location_dest_id = fields.Many2one('stock.location', string='Default Repair Parts Destination') + + +class MaintenanceRequest(models.Model): + _inherit = 'maintenance.request' + + maintenance_type = fields.Selection(selection_add=[('negligence', 'Negligence')]) + repair_line_ids = fields.One2many('maintenance.request.repair.line', 'request_id', 'Parts', copy=True) + repair_status = fields.Selection([ + ('repaired', 'Repaired'), + ('to repair', 'To Repair'), + ('no', 'Nothing to Repair') + ], string='Repair Status', compute='_get_repaired', store=True, readonly=True) + repair_location_id = fields.Many2one('stock.location', string='Source Location') + repair_location_dest_id = fields.Many2one('stock.location', string='Destination Location') + total_lst_price = fields.Float(string='Total Price', compute='_compute_repair_totals', stored=True) + total_standard_price = fields.Float(string='Total Est. Cost', compute='_compute_repair_totals', stored=True) + total_cost = fields.Float(string='Total Cost', compute='_compute_repair_totals', stored=True) + + @api.depends('repair_line_ids.lst_price', 'repair_line_ids.standard_price', 'repair_line_ids.cost') + def _compute_repair_totals(self): + for repair in self: + repair.total_lst_price = sum(l.lst_price for l in repair.repair_line_ids) + repair.total_standard_price = sum(l.standard_price for l in repair.repair_line_ids) + repair.total_cost = sum(l.cost for l in repair.repair_line_ids) + + @api.depends('repair_line_ids.state') + def _get_repaired(self): + for request in self: + if not request.repair_line_ids: + request.repair_status = 'no' + elif request.repair_line_ids.filtered(lambda l: l.state != 'done'): + request.repair_status = 'to repair' + else: + request.repair_status = 'repaired' + + @api.onchange('maintenance_team_id') + def _onchange_maintenance_team(self): + for request in self: + if request.maintenance_team_id: + request.repair_location_id = request.maintenance_team_id.repair_location_id + request.repair_location_dest_id = request.maintenance_team_id.repair_location_dest_id + + + def action_complete_repair(self): + for request in self.filtered(lambda r: r.repair_status == 'to repair'): + request.repair_line_ids.action_complete() + return True + + +class MaintenanceRequestRepairLine(models.Model): + _name = 'maintenance.request.repair.line' + + request_id = fields.Many2one('maintenance.request', copy=False) + product_id = fields.Many2one('product.product', 'Product', required=True, + states={'done': [('readonly', True)]}) + product_uom_qty = fields.Float('Quantity', default=1.0, + digits=dp.get_precision('Product Unit of Measure'), required=True, + states={'done': [('readonly', True)]}) + product_uom_id = fields.Many2one('product.uom', 'Product Unit of Measure', required=True, + states={'done': [('readonly', True)]}) + state = fields.Selection([ + ('draft', 'Draft'), + ('done', 'Done'), + ], string='State', copy=False, default='draft') + move_id = fields.Many2one('stock.move', string='Stock Move') + lst_price = fields.Float(string='Sale Price', states={'done': [('readonly', True)]}) + standard_price = fields.Float(string='Est. Cost', states={'done': [('readonly', True)]}) + cost = fields.Float(string='Cost', compute='_compute_actual_cost', stored=True) + + @api.multi + def unlink(self): + if self.filtered(lambda l: l.state == 'done'): + raise UserError(_('Only draft lines can be deleted.')) + return super(MaintenanceRequestRepairLine, self).unlink() + + @api.onchange('product_id', 'product_uom_qty') + def onchange_product_id(self): + if self.product_id: + self.product_uom_id = self.product_id.uom_id + self.lst_price = self.product_id.lst_price * self.product_uom_qty + self.standard_price = self.product_id.standard_price * self.product_uom_qty + + @api.depends('product_id', 'move_id') + def _compute_actual_cost(self): + for line in self: + if line.move_id: + line.cost = sum(abs(m.amount) for m in line.move_id.account_move_ids) + else: + line.cost = 0.0 + + @api.multi + def action_complete(self): + # Create stock movements. - Inspired by mrp_repair + MoveObj = self.env['stock.move'] + for line in self.filtered(lambda l: not l.state == 'done'): + request = line.request_id + + # Optional hooks to `maintenance_timesheet` and `stock_analytic` + analytic_account_id = False + if getattr(request, 'project_id', False): + analytic_account_id = request.project_id.analytic_account_id.id + + move = MoveObj.create({ + 'name': request.name, + 'product_id': line.product_id.id, + 'product_uom_qty': line.product_uom_qty, + 'product_uom': line.product_uom_id.id, + 'location_id': request.repair_location_id.id, + 'location_dest_id': request.repair_location_dest_id.id, + 'maintenance_request_id': request.id, + 'origin': request.name, + 'analytic_account_id': analytic_account_id, + }) + move = move.sudo() + move._action_confirm() + move._action_assign() + if move.state != 'assigned': + raise ValidationError(_('Unable to reserve inventory.')) + + move.quantity_done = line.product_uom_qty + move._action_done() + if move.state != 'done': + raise ValidationError(_('Unable to move inventory.')) + + line.write({'move_id': move.id, 'state': 'done'}) + return True diff --git a/maintenance_repair/security/ir.model.access.csv b/maintenance_repair/security/ir.model.access.csv new file mode 100644 index 00000000..26e989ac --- /dev/null +++ b/maintenance_repair/security/ir.model.access.csv @@ -0,0 +1,2 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_maintenance_request_repair_line","access maintenance.request.repair.line","model_maintenance_request_repair_line","base.group_user",1,1,1,1 \ No newline at end of file diff --git a/maintenance_repair/tests/__init__.py b/maintenance_repair/tests/__init__.py new file mode 100644 index 00000000..3820c3df --- /dev/null +++ b/maintenance_repair/tests/__init__.py @@ -0,0 +1 @@ +from . import test_maintenance_repair diff --git a/maintenance_repair/tests/test_maintenance_repair.py b/maintenance_repair/tests/test_maintenance_repair.py new file mode 100644 index 00000000..00e93754 --- /dev/null +++ b/maintenance_repair/tests/test_maintenance_repair.py @@ -0,0 +1,37 @@ +from odoo.tests import common + + +class TestMaintenanceRepair(common.TransactionCase): + """Tests for repairs + """ + + def test_create(self): + equipment = self.env['maintenance.equipment'].create({ + 'name': 'Monitor', + }) + + loc_from = self.env.ref('stock.stock_location_stock') + loc_to = self.env.ref('stock.location_inventory') + request = self.env['maintenance.request'].create({ + 'name': 'Repair Monitor', + 'equipment_id': equipment.id, + 'repair_location_id': loc_from.id, + 'repair_location_dest_id': loc_to.id, + }) + self.assertEqual(request.repair_status, 'no') + + product_to_repair = self.env.ref('product.product_product_24_product_template') + line = self.env['maintenance.request.repair.line'].create({ + 'request_id': request.id, + 'product_id': product_to_repair.id, + 'product_uom_id': product_to_repair.uom_id.id, + }) + + self.assertEqual(request.repair_status, 'to repair') + line.action_complete() + self.assertEqual(request.repair_status, 'repaired') + self.assertEqual(line.state, 'done') + self.assertTrue(line.move_id, 'Expect a stock move to be done.') + + + diff --git a/maintenance_repair/views/maintenance_views.xml b/maintenance_repair/views/maintenance_views.xml new file mode 100644 index 00000000..4fdf6daf --- /dev/null +++ b/maintenance_repair/views/maintenance_views.xml @@ -0,0 +1,81 @@ + + + + maintenance.team.form.inherited + maintenance.team + + + + + + + + + + + equipment.request.form.inherited + maintenance.request + + + + + + + + + + + maintenance.request.view.form.inherit.hr.inherited + maintenance.request + + + + + + + + + + + equipment.request.form.notebook.inherited + maintenance.request + + + + + + + + + + + + + + + + + + + + + + + + + + + equipment.request.tree.inherited + maintenance.request + + + + + + + + \ No newline at end of file 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..f445d745 --- /dev/null +++ b/maintenance_usage/__manifest__.py @@ -0,0 +1,26 @@ +{ + 'name': 'Equipment Usage', + 'version': '11.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', + ], + 'data': [ + 'security/ir.model.access.csv', + 'views/maintenance_views.xml', + ], + 'installable': True, + 'auto_install': False, +} 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..57d86c9f --- /dev/null +++ b/maintenance_usage/models/maintenance.py @@ -0,0 +1,113 @@ +from math import floor +from odoo import api, fields, models + + +class MaintenanceEquipmentCategory(models.Model): + _inherit = 'maintenance.equipment.category' + + usage_uom_id = fields.Many2one('product.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('product.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') + + def _compute_usage_count(self): + for equipment in self: + equipment.usage_count = len(equipment.usage_log_ids) + + @api.model + def create(self, values): + record = super(MaintenanceEquipment, self).create(values) + # create first usage record + record._log_usage() + return record + + @api.multi + 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={}): + values['equipment_id'] = self.id + values['date'] = fields.Datetime.now() + self.env['maintenance.usage.log'].create(values) + + @api.multi + 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 + + 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..aea213d8 --- /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","stock.group_stock_manager",1,1,1,1 +"access_maintenance_usage_log","access maintenance.usage.log","model_maintenance_usage_log","base.group_user",1,0,1,0 \ No newline at end of file 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..0899ac92 --- /dev/null +++ b/maintenance_usage/views/maintenance_views.xml @@ -0,0 +1,157 @@ + + + + maintenance.equipment.category.inherited + maintenance.equipment.category + + + + + + + + + + + + equipment.form.inherited + maintenance.equipment + + + + + + + + + + + + + + + + + + + + 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 diff --git a/stock_analytic b/stock_analytic new file mode 120000 index 00000000..60b47bd7 --- /dev/null +++ b/stock_analytic @@ -0,0 +1 @@ +external/hibou-oca/account-analytic/stock_analytic \ No newline at end of file