From d28e7dbf88d87ac4740e67cee52a33aff1ea990c Mon Sep 17 00:00:00 2001 From: Maxime Chambreuil Date: Tue, 2 Oct 2018 09:00:52 +0200 Subject: [PATCH 001/433] [ADD] fieldservice 0.0.1 (#2) --- fieldservice/README.rst | 122 ++++++ fieldservice/__init__.py | 5 + fieldservice/__manifest__.py | 40 ++ fieldservice/data/fsm_stage.xml | 49 +++ fieldservice/data/ir_sequence.xml | 22 + fieldservice/data/mail_message_subtype.xml | 76 ++++ fieldservice/data/module_category.xml | 9 + fieldservice/models/__init__.py | 13 + fieldservice/models/fsm_location.py | 20 + fieldservice/models/fsm_order.py | 141 +++++++ fieldservice/models/fsm_person.py | 19 + fieldservice/models/fsm_route.py | 32 ++ fieldservice/models/fsm_stage.py | 31 ++ fieldservice/models/fsm_tag.py | 16 + fieldservice/models/res_config_settings.py | 16 + fieldservice/models/res_partner.py | 11 + fieldservice/readme/CONFIGURE.rst | 3 + fieldservice/readme/CONTRIBUTORS.rst | 3 + fieldservice/readme/CREDITS.rst | 3 + fieldservice/readme/DESCRIPTION.rst | 1 + fieldservice/readme/INSTALL.rst | 3 + fieldservice/readme/ROADMAP.rst | 2 + fieldservice/readme/USAGE.rst | 5 + fieldservice/security/ir.model.access.csv | 13 + fieldservice/security/res_groups.xml | 35 ++ fieldservice/static/description/icon.png | Bin 0 -> 17808 bytes fieldservice/static/description/index.html | 463 +++++++++++++++++++++ fieldservice/views/fsm_location.xml | 53 +++ fieldservice/views/fsm_order.xml | 281 +++++++++++++ fieldservice/views/fsm_person.xml | 44 ++ fieldservice/views/fsm_route.xml | 73 ++++ fieldservice/views/fsm_stage.xml | 51 +++ fieldservice/views/fsm_tag.xml | 50 +++ fieldservice/views/menu.xml | 97 +++++ fieldservice/views/res_config_settings.xml | 64 +++ fieldservice/views/res_partner.xml | 38 ++ fieldservice/wizard/__init__.py | 4 + fieldservice/wizard/fsm_wizard.py | 49 +++ fieldservice/wizard/fsm_wizard.xml | 38 ++ 39 files changed, 1995 insertions(+) create mode 100644 fieldservice/README.rst create mode 100644 fieldservice/__init__.py create mode 100644 fieldservice/__manifest__.py create mode 100644 fieldservice/data/fsm_stage.xml create mode 100644 fieldservice/data/ir_sequence.xml create mode 100644 fieldservice/data/mail_message_subtype.xml create mode 100644 fieldservice/data/module_category.xml create mode 100644 fieldservice/models/__init__.py create mode 100644 fieldservice/models/fsm_location.py create mode 100644 fieldservice/models/fsm_order.py create mode 100644 fieldservice/models/fsm_person.py create mode 100644 fieldservice/models/fsm_route.py create mode 100644 fieldservice/models/fsm_stage.py create mode 100644 fieldservice/models/fsm_tag.py create mode 100644 fieldservice/models/res_config_settings.py create mode 100644 fieldservice/models/res_partner.py create mode 100644 fieldservice/readme/CONFIGURE.rst create mode 100644 fieldservice/readme/CONTRIBUTORS.rst create mode 100644 fieldservice/readme/CREDITS.rst create mode 100644 fieldservice/readme/DESCRIPTION.rst create mode 100644 fieldservice/readme/INSTALL.rst create mode 100644 fieldservice/readme/ROADMAP.rst create mode 100644 fieldservice/readme/USAGE.rst create mode 100644 fieldservice/security/ir.model.access.csv create mode 100644 fieldservice/security/res_groups.xml create mode 100644 fieldservice/static/description/icon.png create mode 100644 fieldservice/static/description/index.html create mode 100644 fieldservice/views/fsm_location.xml create mode 100644 fieldservice/views/fsm_order.xml create mode 100644 fieldservice/views/fsm_person.xml create mode 100644 fieldservice/views/fsm_route.xml create mode 100644 fieldservice/views/fsm_stage.xml create mode 100644 fieldservice/views/fsm_tag.xml create mode 100644 fieldservice/views/menu.xml create mode 100644 fieldservice/views/res_config_settings.xml create mode 100644 fieldservice/views/res_partner.xml create mode 100644 fieldservice/wizard/__init__.py create mode 100644 fieldservice/wizard/fsm_wizard.py create mode 100644 fieldservice/wizard/fsm_wizard.xml diff --git a/fieldservice/README.rst b/fieldservice/README.rst new file mode 100644 index 0000000000..53b4fa715f --- /dev/null +++ b/fieldservice/README.rst @@ -0,0 +1,122 @@ +============= +Field Service +============= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Ffield--service-lightgray.png?logo=github + :target: https://github.com/OCA/field-service/tree/11.0/fieldservice + :alt: OCA/field-service +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/field-service-11-0/field-service-11-0-fieldservice + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/264/11.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module is the base of the Field Service application in Odoo. + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +You need to install: + +* PostGIS + +Configuration +============= + +To configure this module, you need to: + +* Go to Field Service > Configuration > Settings + +Usage +===== + +To use this module, you need to: + +* Go to Field Service +* Create or select an order +* Follow the process + +Known issues / Roadmap +====================== + +The roadmap of the Field Service application is documented on +`Github `_. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Open Source Integrators + +Contributors +~~~~~~~~~~~~ + +* Wolfgang Hall +* Maxime Chambreuil +* Steve Campbell + +Other credits +~~~~~~~~~~~~~ + +The development of this module has been financially supported by: + +* Open Source Integrators + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-wolfhall| image:: https://github.com/wolfhall.png?size=40px + :target: https://github.com/wolfhall + :alt: wolfhall +.. |maintainer-max3903| image:: https://github.com/max3903.png?size=40px + :target: https://github.com/max3903 + :alt: max3903 + +Current `maintainers `__: + +|maintainer-wolfhall| |maintainer-max3903| + +This module is part of the `OCA/field-service `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/fieldservice/__init__.py b/fieldservice/__init__.py new file mode 100644 index 0000000000..13d8d1168f --- /dev/null +++ b/fieldservice/__init__.py @@ -0,0 +1,5 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models +from . import wizard diff --git a/fieldservice/__manifest__.py b/fieldservice/__manifest__.py new file mode 100644 index 0000000000..beee4c86e6 --- /dev/null +++ b/fieldservice/__manifest__.py @@ -0,0 +1,40 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Field Service', + 'summary': 'Locations, Orders, Calls', + 'version': '11.0.0.0.1', + 'category': 'Field Service', + 'author': 'Open Source Integrators, Odoo Community Association (OCA)', + 'website': 'https://github.com/OCA/field-service', + 'depends': [ + 'mail', + 'web_timeline', + ], + 'data': [ + 'data/ir_sequence.xml', + 'data/mail_message_subtype.xml', + 'data/module_category.xml', + 'data/fsm_stage.xml', + 'security/res_groups.xml', + 'security/ir.model.access.csv', + 'views/res_config_settings.xml', + 'views/fsm_stage.xml', + 'views/fsm_tag.xml', + 'views/res_partner.xml', + 'views/fsm_location.xml', + 'views/fsm_person.xml', + 'views/fsm_order.xml', + 'views/fsm_route.xml', + 'views/menu.xml', + 'wizard/fsm_wizard.xml', + ], + 'application': True, + 'license': 'AGPL-3', + 'development_status': 'Beta', + 'maintainers': [ + 'wolfhall', + 'max3903', + ], +} diff --git a/fieldservice/data/fsm_stage.xml b/fieldservice/data/fsm_stage.xml new file mode 100644 index 0000000000..f397177a56 --- /dev/null +++ b/fieldservice/data/fsm_stage.xml @@ -0,0 +1,49 @@ + + + + + New + 10 + + + + Confirmed + 20 + + + + Scheduled + 30 + + + + Assigned + 40 + + + + Planned + 50 + + + + En Route + 60 + + + + Started + 70 + + + + Completed + 80 + + + + Cancelled + 100 + True + + diff --git a/fieldservice/data/ir_sequence.xml b/fieldservice/data/ir_sequence.xml new file mode 100644 index 0000000000..4d8e713315 --- /dev/null +++ b/fieldservice/data/ir_sequence.xml @@ -0,0 +1,22 @@ + + + + + + FSM Order + fsm.order + FO + 3 + + + + + + FSM Route + fsm.route + SR + 3 + + + + diff --git a/fieldservice/data/mail_message_subtype.xml b/fieldservice/data/mail_message_subtype.xml new file mode 100644 index 0000000000..3924dbb708 --- /dev/null +++ b/fieldservice/data/mail_message_subtype.xml @@ -0,0 +1,76 @@ + + + + + Order Created + 0 + fsm.order + + + Order created + + + + Order Confirmed + 10 + fsm.order + + + Order confirmed + + + + Order Scheduled + 20 + fsm.order + + + Order scheduled + + + + Order Assigned + 30 + fsm.order + + + Order assigned + + + + Order En Route + 40 + fsm.order + + + Order en route + + + + Order Started + 50 + fsm.order + + + Order started + + + + Order Completed + 60 + fsm.order + + + Order completed + + + + Order Cancelled + 100 + fsm.order + + + Order cancelled + + + diff --git a/fieldservice/data/module_category.xml b/fieldservice/data/module_category.xml new file mode 100644 index 0000000000..4fd7c0ba17 --- /dev/null +++ b/fieldservice/data/module_category.xml @@ -0,0 +1,9 @@ + + + + + Field Service + 20 + + + diff --git a/fieldservice/models/__init__.py b/fieldservice/models/__init__.py new file mode 100644 index 0000000000..acb7632eb0 --- /dev/null +++ b/fieldservice/models/__init__.py @@ -0,0 +1,13 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import ( + res_config_settings, + res_partner, + fsm_stage, + fsm_tag, + fsm_location, + fsm_person, + fsm_order, + fsm_route, +) diff --git a/fieldservice/models/fsm_location.py b/fieldservice/models/fsm_location.py new file mode 100644 index 0000000000..b5f0cad13c --- /dev/null +++ b/fieldservice/models/fsm_location.py @@ -0,0 +1,20 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class FSMLocation(models.Model): + _name = 'fsm.location' + _inherits = {'res.partner': 'partner_id'} + _description = 'Field Service Location' + + direction = fields.Char(string='Directions') + partner_id = fields.Many2one('res.partner', string='Related Partner', + required=True, ondelete='restrict', + auto_join=True) + + @api.model + def create(self, vals): + vals.update({'fsm_location': True}) + return super(FSMLocation, self).create(vals) diff --git a/fieldservice/models/fsm_order.py b/fieldservice/models/fsm_order.py new file mode 100644 index 0000000000..23d283c232 --- /dev/null +++ b/fieldservice/models/fsm_order.py @@ -0,0 +1,141 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from datetime import timedelta +from odoo import api, fields, models, _ +from . import fsm_stage + + +class FSMOrder(models.Model): + _name = 'fsm.order' + _description = 'Field Service Order' + _inherit = ['mail.thread', 'mail.activity.mixin'] + + def _default_stage_id(self): + return self.env.ref('fieldservice.fsm_stage_new') + + stage_id = fields.Many2one('fsm.stage', string='Stage', + track_visibility='onchange', + index=True, + group_expand='_read_group_stage_ids', + default=lambda self: self._default_stage_id()) + priority = fields.Selection(fsm_stage.AVAILABLE_PRIORITIES, + string='Priority', + index=True, + default=fsm_stage.AVAILABLE_PRIORITIES[0][0]) + tag_ids = fields.Many2many('fsm.tag', 'fsm_order_tag_rel', + 'fsm_order_id', + 'tag_id', string='Tags', + help="Classify and analyze your orders") + color = fields.Integer('Color Index', default=0) + + # Request + name = fields.Char(string='Name', required=True, + default=lambda self: _('New')) + customer_id = fields.Many2one('res.partner', string='Customer', + domain=[('customer', '=', True)], + change_default=True, + index=True, + track_visibility='always') + fsm_location_id = fields.Many2one('fsm.location', string='Location', + index=True) + requested_date = fields.Datetime(string='Requested Date') + description = fields.Text(string='Description') + origin = fields.Char(string='Origin') + + # Planning + fsm_person_id = fields.Many2one('fsm.person', + string='Assigned To', + index=True) + fsm_route_id = fields.Many2one('fsm.route', string='Route', index=True) + scheduled_date_start = fields.Datetime(string='Scheduled Start') + scheduled_duration = fields.Float(string='Duration in hours', + help='Scheduled duration of the work in' + ' hours') + scheduled_date_end = fields.Datetime(string="Scheduled End") + sequence = fields.Integer(string='Sequence', default=10) + todo = fields.Text(string='Instructions') + + # Execution + log = fields.Text(string='Log') + date_start = fields.Datetime(string='Actual Start') + date_end = fields.Datetime(string='Actual End') + + @api.model + def _read_group_stage_ids(self, stages, domain, order): + stage_ids = self.env['fsm.stage'].search([]) + return stage_ids + + @api.model + def create(self, vals): + if vals.get('name', _('New')) == _('New'): + vals['name'] = self.env['ir.sequence'].next_by_code('fsm.order') \ + or _('New') + return super(FSMOrder, self).create(vals) + + @api.multi + def write(self, vals): + if 'scheduled_date_end' in vals: + date_to_with_delta = fields.Datetime.from_string( + vals.get('scheduled_date_end')) -\ + timedelta(hours=self.scheduled_duration) + vals['scheduled_date_start'] = str(date_to_with_delta) + if 'scheduled_duration' in vals: + date_to_with_delta = fields.Datetime.from_string( + vals.get('scheduled_date_start', self.scheduled_date_start)) +\ + timedelta(hours=vals.get('scheduled_duration')) + vals['scheduled_date_end'] = str(date_to_with_delta) + if 'scheduled_date_end' not in vals and 'scheduled_date_start' in vals: + date_to_with_delta = fields.Datetime.from_string( + vals.get('scheduled_date_start')) +\ + timedelta(hours=self.scheduled_duration) + vals['scheduled_date_end'] = str(date_to_with_delta) + return super(FSMOrder, self).write(vals) + + def action_confirm(self): + return self.write({'stage_id': self.env.ref( + 'fieldservice.fsm_stage_confirmed').id}) + + def action_schedule(self): + return self.write({'stage_id': self.env.ref( + 'fieldservice.fsm_stage_scheduled').id}) + + def action_assign(self): + return self.write({'stage_id': self.env.ref( + 'fieldservice.fsm_stage_assigned').id}) + + def action_plan(self): + return self.write({'stage_id': self.env.ref( + 'fieldservice.fsm_stage_planned').id}) + + def action_enroute(self): + return self.write({'stage_id': self.env.ref( + 'fieldservice.fsm_stage_enroute').id}) + + def action_start(self): + return self.write({'stage_id': self.env.ref( + 'fieldservice.fsm_stage_started').id}) + + def action_complete(self): + return self.write({'stage_id': self.env.ref( + 'fieldservice.fsm_stage_completed').id}) + + def action_cancel(self): + return self.write({'stage_id': self.env.ref( + 'fieldservice.fsm_stage_cancelled').id}) + + @api.onchange('scheduled_date_end') + def onchange_scheduled_date_end(self): + if self.scheduled_date_end: + date_to_with_delta = fields.Datetime.from_string( + self.scheduled_date_end) -\ + timedelta(hours=self.scheduled_duration) + self.date_start = str(date_to_with_delta) + + @api.onchange('scheduled_duration') + def onchange_scheduled_duration(self): + if self.scheduled_duration: + date_to_with_delta = fields.Datetime.from_string( + self.scheduled_date_start) +\ + timedelta(hours=self.scheduled_duration) + self.scheduled_date_end = str(date_to_with_delta) diff --git a/fieldservice/models/fsm_person.py b/fieldservice/models/fsm_person.py new file mode 100644 index 0000000000..211b08d515 --- /dev/null +++ b/fieldservice/models/fsm_person.py @@ -0,0 +1,19 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class FSMPerson(models.Model): + _name = 'fsm.person' + _inherits = {'res.partner': 'partner_id'} + _description = 'Field Service Person' + + partner_id = fields.Many2one('res.partner', string='Related Partner', + required=True, ondelete='restrict', + auto_join=True) + + @api.model + def create(self, vals): + vals.update({'fsm_person': True}) + return super(FSMPerson, self).create(vals) diff --git a/fieldservice/models/fsm_route.py b/fieldservice/models/fsm_route.py new file mode 100644 index 0000000000..b1f4e8ab83 --- /dev/null +++ b/fieldservice/models/fsm_route.py @@ -0,0 +1,32 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models, _ + + +class FSMRoute(models.Model): + _name = 'fsm.route' + _description = 'Field Service Route' + _order = 'date, name' + + name = fields.Char(string='Name', required=True, + default=lambda self: _('New')) + fsm_order_ids = fields.One2many('fsm.order', 'fsm_route_id', + string='Orders') + fsm_person_id = fields.Many2one('fsm.person', + string='Assigned To', + required=True) + date = fields.Date(string='Date', required=True) + + _sql_constraints = [ + ('fsm_route_person_date_uniq', + 'unique (fsm_person_id, date)', + "You cannot create 2 routes for the same person on the same day!"), + ] + + @api.model + def create(self, vals): + if vals.get('name', _('New')) == _('New'): + vals['name'] = self.env['ir.sequence'].next_by_code('fsm.route')\ + or _('New') + return super(FSMRoute, self).create(vals) diff --git a/fieldservice/models/fsm_stage.py b/fieldservice/models/fsm_stage.py new file mode 100644 index 0000000000..84bb19daa5 --- /dev/null +++ b/fieldservice/models/fsm_stage.py @@ -0,0 +1,31 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +AVAILABLE_PRIORITIES = [ + ('0', 'Normal'), + ('1', 'Low'), + ('2', 'High'), + ('3', 'Urgent'), +] + + +class FSMStage(models.Model): + _name = 'fsm.stage' + _description = 'Field Service Stage' + _order = 'sequence, name, id' + + name = fields.Char(string='Name', required=True) + sequence = fields.Integer('Sequence', default=1, + help="Used to order stages. Lower is better.") + legend_priority = fields.Text('Priority Management Explanation', + translate=True, + help='Explanation text to help users using' + ' the star and priority mechanism on' + ' stages or orders that are in this' + ' stage.') + fold = fields.Boolean('Folded in Kanban', + help='This stage is folded in the kanban view when ' + 'there are no record in that stage to display.') diff --git a/fieldservice/models/fsm_tag.py b/fieldservice/models/fsm_tag.py new file mode 100644 index 0000000000..cd7de9ab57 --- /dev/null +++ b/fieldservice/models/fsm_tag.py @@ -0,0 +1,16 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class FSMTag(models.Model): + _name = 'fsm.tag' + _description = 'Field Service Tag' + + name = fields.Char(string='Name', required=True) + color = fields.Integer('Color Index', default=10) + + _sql_constraints = [ + ('name_uniq', 'unique (name)', "Tag name already exists!"), + ] diff --git a/fieldservice/models/res_config_settings.py b/fieldservice/models/res_config_settings.py new file mode 100644 index 0000000000..ffe064f969 --- /dev/null +++ b/fieldservice/models/res_config_settings.py @@ -0,0 +1,16 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models +# from odoo import apit, _ + + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + group_fsm_team = fields.Boolean( + string='Manage Teams', + implied_group='fieldservice.group_fsm_team') + group_fsm_vehicle = fields.Boolean( + string='Manage Vehicles', + implied_group='fieldservice.group_fsm_vehicle') diff --git a/fieldservice/models/res_partner.py b/fieldservice/models/res_partner.py new file mode 100644 index 0000000000..415f99df02 --- /dev/null +++ b/fieldservice/models/res_partner.py @@ -0,0 +1,11 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + fsm_location = fields.Boolean('Is a FS Location') + fsm_person = fields.Boolean('Is a FS Person') diff --git a/fieldservice/readme/CONFIGURE.rst b/fieldservice/readme/CONFIGURE.rst new file mode 100644 index 0000000000..eff3513290 --- /dev/null +++ b/fieldservice/readme/CONFIGURE.rst @@ -0,0 +1,3 @@ +To configure this module, you need to: + +* Go to Field Service > Configuration > Settings diff --git a/fieldservice/readme/CONTRIBUTORS.rst b/fieldservice/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..db6ccc051b --- /dev/null +++ b/fieldservice/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Wolfgang Hall +* Maxime Chambreuil +* Steve Campbell diff --git a/fieldservice/readme/CREDITS.rst b/fieldservice/readme/CREDITS.rst new file mode 100644 index 0000000000..0eff0acf4e --- /dev/null +++ b/fieldservice/readme/CREDITS.rst @@ -0,0 +1,3 @@ +The development of this module has been financially supported by: + +* Open Source Integrators diff --git a/fieldservice/readme/DESCRIPTION.rst b/fieldservice/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..edf5bcb488 --- /dev/null +++ b/fieldservice/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module is the base of the Field Service application in Odoo. diff --git a/fieldservice/readme/INSTALL.rst b/fieldservice/readme/INSTALL.rst new file mode 100644 index 0000000000..c0c33c5f5b --- /dev/null +++ b/fieldservice/readme/INSTALL.rst @@ -0,0 +1,3 @@ +You need to install: + +* PostGIS diff --git a/fieldservice/readme/ROADMAP.rst b/fieldservice/readme/ROADMAP.rst new file mode 100644 index 0000000000..f607015959 --- /dev/null +++ b/fieldservice/readme/ROADMAP.rst @@ -0,0 +1,2 @@ +The roadmap of the Field Service application is documented on +`Github `_. diff --git a/fieldservice/readme/USAGE.rst b/fieldservice/readme/USAGE.rst new file mode 100644 index 0000000000..b7da1a0e26 --- /dev/null +++ b/fieldservice/readme/USAGE.rst @@ -0,0 +1,5 @@ +To use this module, you need to: + +* Go to Field Service +* Create or select an order +* Follow the process diff --git a/fieldservice/security/ir.model.access.csv b/fieldservice/security/ir.model.access.csv new file mode 100644 index 0000000000..b334a0c41c --- /dev/null +++ b/fieldservice/security/ir.model.access.csv @@ -0,0 +1,13 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_fsm_stage_fsm_user,fsm.stage.user,model_fsm_stage,fieldservice.group_fsm_user,1,0,0,0 +access_fsm_stage_fsm_manager,fsm.stage.manager,model_fsm_stage,fieldservice.group_fsm_manager,1,1,1,1 +access_fsm_tag_fsm_user,fsm.tag.user,model_fsm_tag,fieldservice.group_fsm_user,1,0,0,0 +access_fsm_tag_fsm_manager,fsm.tag.manager,model_fsm_tag,fieldservice.group_fsm_manager,1,1,1,1 +access_fsm_person_user,fsm.person.user,model_fsm_person,fieldservice.group_fsm_user,1,1,0,0 +access_fsm_person_manager,fsm.person.manager,model_fsm_person,fieldservice.group_fsm_manager,1,1,1,1 +access_fsm_location_user,fsm.location.user,model_fsm_location,fieldservice.group_fsm_user,1,1,0,0 +access_fsm_location_dispatcher,fsm.location.dispatcher,model_fsm_location,fieldservice.group_fsm_dispatcher,1,1,1,0 +access_fsm_order_user,fsm.order.user,model_fsm_order,fieldservice.group_fsm_user,1,1,0,0 +access_fsm_order_dispatcher,fsm.order.dispatcher,model_fsm_order,fieldservice.group_fsm_dispatcher,1,1,1,0 +access_fsm_route_user,fsm.route.user,model_fsm_route,fieldservice.group_fsm_user,1,1,0,0 +access_fsm_route_dispatcher,fsm.route.dispatcher,model_fsm_route,fieldservice.group_fsm_dispatcher,1,1,1,1 diff --git a/fieldservice/security/res_groups.xml b/fieldservice/security/res_groups.xml new file mode 100644 index 0000000000..fc75d14ec0 --- /dev/null +++ b/fieldservice/security/res_groups.xml @@ -0,0 +1,35 @@ + + + + + + User + + + + + + Dispatcher + + + + + + Manager + + + + + + + + Manage Field Service Teams + + + + + Manage Field Service Vehicles + + + + diff --git a/fieldservice/static/description/icon.png b/fieldservice/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..955674d8f0b8c47de3ffa9db25cb109fbe4a1091 GIT binary patch literal 17808 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 literal 0 HcmV?d00001 diff --git a/fieldservice/static/description/index.html b/fieldservice/static/description/index.html new file mode 100644 index 0000000000..e7c1b37487 --- /dev/null +++ b/fieldservice/static/description/index.html @@ -0,0 +1,463 @@ + + + + + + +Field Service + + + +
+

Field Service

+ + +

Beta License: AGPL-3 OCA/field-service Translate me on Weblate Try me on Runbot

+

This module is the base of the Field Service application in Odoo.

+

Table of contents

+ +
+

Installation

+

You need to install:

+
    +
  • PostGIS
  • +
+
+
+

Configuration

+

To configure this module, you need to:

+
    +
  • Go to Field Service > Configuration > Settings
  • +
+
+
+

Usage

+

To use this module, you need to:

+
    +
  • Go to Field Service
  • +
  • Create or select an order
  • +
  • Follow the process
  • +
+
+
+

Known issues / Roadmap

+

The roadmap of the Field Service application is documented on +Github.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Open Source Integrators
  • +
+
+ +
+

Other credits

+

The development of this module has been financially supported by:

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainers:

+

wolfhall max3903

+

This module is part of the OCA/field-service project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/fieldservice/views/fsm_location.xml b/fieldservice/views/fsm_location.xml new file mode 100644 index 0000000000..2545459669 --- /dev/null +++ b/fieldservice/views/fsm_location.xml @@ -0,0 +1,53 @@ + + + + + + fsm.location.tree + fsm.location + + + + + + + + + fsm.location.form + fsm.location + +
+ + + + + + + + + + + + + +
+
+
+ + + Service Locations + fsm.location + + form + tree,form + +

+ Create a Service Locations. +

+

+ Module not yet enabled. +

+
+
+ +
diff --git a/fieldservice/views/fsm_order.xml b/fieldservice/views/fsm_order.xml new file mode 100644 index 0000000000..2ac62d400e --- /dev/null +++ b/fieldservice/views/fsm_order.xml @@ -0,0 +1,281 @@ + + + + + + fsm.order.form + fsm.order + +
+
+
+ +
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+ + + fsm.order.tree + fsm.order + + + + + + + + + + + + + + Orders + fsm.order + form + tree,form + +

+ Create an Order. +

+
+
+ + + + fsm.order.kanban + fsm.order + + + + + + + + + + +
+ +
+
+ +
+
+ +
+
+ + + +
+
+ +
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+ + + fsm.order.timeline + fsm.order + timeline + + + + + + + + Orders + fsm.order + form + kanban,timeline,tree,form + +

+ Create an Order. +

+
+
+ + + + fsm.order.graph + fsm.order + + + + + + + + + fsm.order.pivot + fsm.order + + + + + + + + + Orders + fsm.order + form + graph,pivot + +

+ Orders Reports. +

+
+
+ +
diff --git a/fieldservice/views/fsm_person.xml b/fieldservice/views/fsm_person.xml new file mode 100644 index 0000000000..c8fc534449 --- /dev/null +++ b/fieldservice/views/fsm_person.xml @@ -0,0 +1,44 @@ + + + + + + fsm.person.tree + fsm.person + + + + + + + + + fsm.person.form + fsm.person + +
+ + + + + + + + +
+
+
+ + + Field Service Persons + fsm.person + form + tree,form + +

+ Add Field Service Person here. +

+
+
+ +
diff --git a/fieldservice/views/fsm_route.xml b/fieldservice/views/fsm_route.xml new file mode 100644 index 0000000000..0b09429c2f --- /dev/null +++ b/fieldservice/views/fsm_route.xml @@ -0,0 +1,73 @@ + + + + + + fsm.route.tree + fsm.route + + + + + + + + + + + fsm.route.form + fsm.route + +
+ +
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + Day Routes + fsm.route + form + tree,form + +

+ Add Day Routes here. +

+
+
+ +
diff --git a/fieldservice/views/fsm_stage.xml b/fieldservice/views/fsm_stage.xml new file mode 100644 index 0000000000..69e697e34d --- /dev/null +++ b/fieldservice/views/fsm_stage.xml @@ -0,0 +1,51 @@ + + + + + + Stages + fsm.stage + + + + + + + + + Stage + fsm.stage + +
+ + + + + + + + + + + +
+
+
+ + + Stages + fsm.stage + + form + tree,form + +

+ Create a Stage. +

+

+ Module not yet enabled. +

+
+
+ +
diff --git a/fieldservice/views/fsm_tag.xml b/fieldservice/views/fsm_tag.xml new file mode 100644 index 0000000000..64fbda2864 --- /dev/null +++ b/fieldservice/views/fsm_tag.xml @@ -0,0 +1,50 @@ + + + + + + Tags + fsm.tag + + + + + + + + + Tag + fsm.tag + +
+ + + + + + + + + + +
+
+
+ + + Tags + fsm.tag + + form + tree,form + +

+ Create a tag. +

+

+ Module not yet enabled. +

+
+
+ +
diff --git a/fieldservice/views/menu.xml b/fieldservice/views/menu.xml new file mode 100644 index 0000000000..859d68fbc7 --- /dev/null +++ b/fieldservice/views/menu.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fieldservice/views/res_config_settings.xml b/fieldservice/views/res_config_settings.xml new file mode 100644 index 0000000000..1ef9ec3bc8 --- /dev/null +++ b/fieldservice/views/res_config_settings.xml @@ -0,0 +1,64 @@ + + + + + res.config.settings.view.form.fsm + res.config.settings + + + + +
+

Advanced Features

+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+ + + Settings + ir.actions.act_window + res.config.settings + form + inline + {'module': 'fieldservice'} + + +
diff --git a/fieldservice/views/res_partner.xml b/fieldservice/views/res_partner.xml new file mode 100644 index 0000000000..0077919a58 --- /dev/null +++ b/fieldservice/views/res_partner.xml @@ -0,0 +1,38 @@ + + + + + + Customers + res.partner + form + kanban,tree,form + [('customer', '=', True)] + +

+ Create a Customer. +

+
+
+ + + + res.partner.fsm.form + res.partner + + + + + + + + + + + + + + + + +
diff --git a/fieldservice/wizard/__init__.py b/fieldservice/wizard/__init__.py new file mode 100644 index 0000000000..162ddf083c --- /dev/null +++ b/fieldservice/wizard/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import fsm_wizard diff --git a/fieldservice/wizard/fsm_wizard.py b/fieldservice/wizard/fsm_wizard.py new file mode 100644 index 0000000000..97e4504ed9 --- /dev/null +++ b/fieldservice/wizard/fsm_wizard.py @@ -0,0 +1,49 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models, _ +from odoo.exceptions import UserError + + +class FSMWizard(models.TransientModel): + """ + A wizard to convert a res.partner record to a fsm.person or + fsm.location + """ + _name = 'fsm.wizard' + _description = 'FSM Record Conversion' + + fsm_record_type = fields.Selection([('person', 'Person'), + ('location', 'Location')], + 'Record Type') + + @api.multi + def action_convert(self): + self.ensure_one() + partner_id = self._context.get('active_id') + partner = self.env['res.partner'].search([('id', '=', partner_id)]) + if self.fsm_record_type == 'person': + self.action_convert_person(partner) + if self.fsm_record_type == 'location': + self.action_convert_location(partner) + return {'type': 'ir.actions.act_window_close'} + + def action_convert_location(self, partner): + res = self.env['fsm.location'].search_count( + [('partner_id', '=', partner.id)]) + if res == 0: + self.env['fsm.location'].create({'partner_id': partner.id}) + partner.write({'fsm_location': True}) + else: + raise UserError(_('A Field Service Location related to that' + ' partner already exists.')) + + def action_convert_person(self, partner): + res = self.env['fsm.person'].search_count( + [('partner_id', '=', partner.id)]) + if res == 0: + self.env['fsm.person'].create({'partner_id': partner.id}) + partner.write({'fsm_person': True}) + else: + raise UserError(_('A Field Service Person related to that' + ' partner already exists.')) diff --git a/fieldservice/wizard/fsm_wizard.xml b/fieldservice/wizard/fsm_wizard.xml new file mode 100644 index 0000000000..a6d226b3b9 --- /dev/null +++ b/fieldservice/wizard/fsm_wizard.xml @@ -0,0 +1,38 @@ + + + + + + + + + Convert to a FSM Person or Location + fsm.wizard + +
+
+ Select whether you want to convert this record to a Field + Service Person or Location. +
+ + + + + +
+
+
+
+
+ +
From 8d7444ca640e8fcac34f3b7e70b44116365c6fdc Mon Sep 17 00:00:00 2001 From: brian10048 Date: Fri, 12 Oct 2018 09:46:25 -0400 Subject: [PATCH 002/433] [ADD] Vehicle (#23) * [ADD] Vehicle --- fieldservice/__manifest__.py | 1 + fieldservice/models/__init__.py | 1 + fieldservice/models/fsm_route.py | 5 +++ fieldservice/models/fsm_vehicle.py | 16 ++++++++ fieldservice/security/ir.model.access.csv | 2 + fieldservice/views/fsm_route.xml | 1 + fieldservice/views/fsm_vehicle.xml | 48 +++++++++++++++++++++++ fieldservice/views/menu.xml | 7 ++++ 8 files changed, 81 insertions(+) create mode 100644 fieldservice/models/fsm_vehicle.py create mode 100644 fieldservice/views/fsm_vehicle.xml diff --git a/fieldservice/__manifest__.py b/fieldservice/__manifest__.py index beee4c86e6..cf03364090 100644 --- a/fieldservice/__manifest__.py +++ b/fieldservice/__manifest__.py @@ -25,6 +25,7 @@ 'views/res_partner.xml', 'views/fsm_location.xml', 'views/fsm_person.xml', + 'views/fsm_vehicle.xml', 'views/fsm_order.xml', 'views/fsm_route.xml', 'views/menu.xml', diff --git a/fieldservice/models/__init__.py b/fieldservice/models/__init__.py index acb7632eb0..0dfc2aea9e 100644 --- a/fieldservice/models/__init__.py +++ b/fieldservice/models/__init__.py @@ -8,6 +8,7 @@ fsm_tag, fsm_location, fsm_person, + fsm_vehicle, fsm_order, fsm_route, ) diff --git a/fieldservice/models/fsm_route.py b/fieldservice/models/fsm_route.py index b1f4e8ab83..8bfddb688b 100644 --- a/fieldservice/models/fsm_route.py +++ b/fieldservice/models/fsm_route.py @@ -16,12 +16,17 @@ class FSMRoute(models.Model): fsm_person_id = fields.Many2one('fsm.person', string='Assigned To', required=True) + fsm_vehicle_id = fields.Many2one('fsm.vehicle', + string='Assigned Vehicle') date = fields.Date(string='Date', required=True) _sql_constraints = [ ('fsm_route_person_date_uniq', 'unique (fsm_person_id, date)', "You cannot create 2 routes for the same person on the same day!"), + ('fsm_route_vehicle_date_uniq', + 'unique (fsm_vehicle_id, date)', + "You cannot create 2 routes for the same vehicle on the same day!"), ] @api.model diff --git a/fieldservice/models/fsm_vehicle.py b/fieldservice/models/fsm_vehicle.py new file mode 100644 index 0000000000..28b8b1b1fe --- /dev/null +++ b/fieldservice/models/fsm_vehicle.py @@ -0,0 +1,16 @@ +# Copyright (C) 2018 - TODAY, Brian McMaster +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class FSMVehicle(models.Model): + _name = 'fsm.vehicle' + _description = 'Field Service Vehicle' + + name = fields.Char(string='Name', required='True') + fsm_person_id = fields.Many2one('fsm.person', string='Assigned Driver') + + _sql_constraints = [ + ('name_uniq', 'unique (name)', "Vehicle name already exists!"), + ] diff --git a/fieldservice/security/ir.model.access.csv b/fieldservice/security/ir.model.access.csv index b334a0c41c..0ae52da434 100644 --- a/fieldservice/security/ir.model.access.csv +++ b/fieldservice/security/ir.model.access.csv @@ -11,3 +11,5 @@ access_fsm_order_user,fsm.order.user,model_fsm_order,fieldservice.group_fsm_user access_fsm_order_dispatcher,fsm.order.dispatcher,model_fsm_order,fieldservice.group_fsm_dispatcher,1,1,1,0 access_fsm_route_user,fsm.route.user,model_fsm_route,fieldservice.group_fsm_user,1,1,0,0 access_fsm_route_dispatcher,fsm.route.dispatcher,model_fsm_route,fieldservice.group_fsm_dispatcher,1,1,1,1 +access_fsm_vehicle_fsm_user,fsm.vehicle.user,model_fsm_vehicle,fieldservice.group_fsm_user,1,0,0,0 +access_fsm_vehicle_fsm_manager,fsm.vehicle.manager,model_fsm_vehicle,fieldservice.group_fsm_manager,1,1,1,1 diff --git a/fieldservice/views/fsm_route.xml b/fieldservice/views/fsm_route.xml index 0b09429c2f..8654c58254 100644 --- a/fieldservice/views/fsm_route.xml +++ b/fieldservice/views/fsm_route.xml @@ -28,6 +28,7 @@ + diff --git a/fieldservice/views/fsm_vehicle.xml b/fieldservice/views/fsm_vehicle.xml new file mode 100644 index 0000000000..beac8a8092 --- /dev/null +++ b/fieldservice/views/fsm_vehicle.xml @@ -0,0 +1,48 @@ + + + + + + fsm.vehicle.tree + fsm.vehicle + + + + + + + + + + fsm.vehicle.form + fsm.vehicle + +
+ + + + + + + + + + +
+
+
+ + + + Field Service Vehicles + fsm.vehicle + form + tree,form + +

+ Add a Field Service Vehicle here. +

+
+
+ +
diff --git a/fieldservice/views/menu.xml b/fieldservice/views/menu.xml index 859d68fbc7..64423725c8 100644 --- a/fieldservice/views/menu.xml +++ b/fieldservice/views/menu.xml @@ -66,6 +66,13 @@ parent="data" sequence="30"/> + + Date: Sat, 13 Oct 2018 00:42:11 +0530 Subject: [PATCH 003/433] [OCA]12567 Location structure needs to reflect hierarchy of Buildings (#22) * [IMP] Extend fsm.location object --- fieldservice/__manifest__.py | 1 + fieldservice/models/fsm_location.py | 18 ++++++ fieldservice/views/fsm_location.xml | 86 +++++++++++++++++++++++++++-- 3 files changed, 100 insertions(+), 5 deletions(-) diff --git a/fieldservice/__manifest__.py b/fieldservice/__manifest__.py index cf03364090..c064afd129 100644 --- a/fieldservice/__manifest__.py +++ b/fieldservice/__manifest__.py @@ -9,6 +9,7 @@ 'author': 'Open Source Integrators, Odoo Community Association (OCA)', 'website': 'https://github.com/OCA/field-service', 'depends': [ + 'base_geolocalize', 'mail', 'web_timeline', ], diff --git a/fieldservice/models/fsm_location.py b/fieldservice/models/fsm_location.py index b5f0cad13c..7c3f5a8f6d 100644 --- a/fieldservice/models/fsm_location.py +++ b/fieldservice/models/fsm_location.py @@ -13,6 +13,24 @@ class FSMLocation(models.Model): partner_id = fields.Many2one('res.partner', string='Related Partner', required=True, ondelete='restrict', auto_join=True) + owner_id = fields.Many2one('res.partner', string='Related Owner', + required=True, ondelete='restrict', + auto_join=True) + customer_id = fields.Many2one('res.partner', string='Related Customer', + required=True, ondelete='restrict', + auto_join=True) + tag_ids = fields.Many2many('fsm.tag', + string='Tags') + building = fields.Char(string='Building', size=35) + floor = fields.Char(string='Floor', size=35) + unit = fields.Char(string='Unit', size=35) + room = fields.Char(string='Room', size=35) + description = fields.Char(string='Description') + territory = fields.Char(string='Territory', size=35) + branch = fields.Char(string='Branch', size=35) + district = fields.Char(string='District', size=35) + region = fields.Char(string='Region', size=35) + timezone = fields.Char(string='Timezone', size=35) @api.model def create(self, vals): diff --git a/fieldservice/views/fsm_location.xml b/fieldservice/views/fsm_location.xml index 2545459669..949df5a0ab 100644 --- a/fieldservice/views/fsm_location.xml +++ b/fieldservice/views/fsm_location.xml @@ -1,17 +1,26 @@ - + fsm.location.tree fsm.location + + + + + + + + + fsm.location.form fsm.location @@ -21,32 +30,99 @@ + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + +
+ + + fsm.location.search + fsm.location + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Service Locations fsm.location form tree,form +

Create a Service Locations.

-

- Module not yet enabled. -

From 310244229d83acfe997579d402167615f45778b9 Mon Sep 17 00:00:00 2001 From: Maxime Chambreuil Date: Fri, 12 Oct 2018 15:34:23 -0500 Subject: [PATCH 004/433] [FIX] View --- fieldservice/views/fsm_location.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fieldservice/views/fsm_location.xml b/fieldservice/views/fsm_location.xml index 949df5a0ab..0d9c95ed64 100644 --- a/fieldservice/views/fsm_location.xml +++ b/fieldservice/views/fsm_location.xml @@ -30,7 +30,12 @@ - + + From 697c43ab927701214cfc412096ade6f1e2f59103 Mon Sep 17 00:00:00 2001 From: Maxime Chambreuil Date: Thu, 18 Oct 2018 17:43:03 -0500 Subject: [PATCH 005/433] [ADD] location, territory, branch, district, region --- fieldservice/__manifest__.py | 5 +++ fieldservice/models/__init__.py | 5 +++ fieldservice/models/branch.py | 12 ++++++ fieldservice/models/district.py | 12 ++++++ fieldservice/models/fsm_location.py | 36 +++++++++++++----- fieldservice/models/location.py | 32 ++++++++++++++++ fieldservice/models/region.py | 11 ++++++ fieldservice/models/territory.py | 12 ++++++ fieldservice/security/ir.model.access.csv | 10 +++++ fieldservice/views/branch.xml | 40 ++++++++++++++++++++ fieldservice/views/district.xml | 40 ++++++++++++++++++++ fieldservice/views/fsm_location.xml | 45 ++++++++--------------- fieldservice/views/location.xml | 43 ++++++++++++++++++++++ fieldservice/views/menu.xml | 39 +++++++++++++++++++- fieldservice/views/region.xml | 37 +++++++++++++++++++ fieldservice/views/territory.xml | 40 ++++++++++++++++++++ 16 files changed, 379 insertions(+), 40 deletions(-) create mode 100644 fieldservice/models/branch.py create mode 100644 fieldservice/models/district.py create mode 100644 fieldservice/models/location.py create mode 100644 fieldservice/models/region.py create mode 100644 fieldservice/models/territory.py create mode 100644 fieldservice/views/branch.xml create mode 100644 fieldservice/views/district.xml create mode 100644 fieldservice/views/location.xml create mode 100644 fieldservice/views/region.xml create mode 100644 fieldservice/views/territory.xml diff --git a/fieldservice/__manifest__.py b/fieldservice/__manifest__.py index c064afd129..0ee4fd0c2b 100644 --- a/fieldservice/__manifest__.py +++ b/fieldservice/__manifest__.py @@ -21,6 +21,11 @@ 'security/res_groups.xml', 'security/ir.model.access.csv', 'views/res_config_settings.xml', + 'views/location.xml', + 'views/territory.xml', + 'views/branch.xml', + 'views/district.xml', + 'views/region.xml', 'views/fsm_stage.xml', 'views/fsm_tag.xml', 'views/res_partner.xml', diff --git a/fieldservice/models/__init__.py b/fieldservice/models/__init__.py index 0dfc2aea9e..d6caedd59d 100644 --- a/fieldservice/models/__init__.py +++ b/fieldservice/models/__init__.py @@ -3,6 +3,11 @@ from . import ( res_config_settings, + location, + region, + district, + branch, + territory, res_partner, fsm_stage, fsm_tag, diff --git a/fieldservice/models/branch.py b/fieldservice/models/branch.py new file mode 100644 index 0000000000..934e4e8e55 --- /dev/null +++ b/fieldservice/models/branch.py @@ -0,0 +1,12 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class Branch(models.Model): + _name = 'branch' + _description = 'branch' + + name = fields.Char(string='Name') + district_id = fields.Many2one('district', string='District') diff --git a/fieldservice/models/district.py b/fieldservice/models/district.py new file mode 100644 index 0000000000..90836638b4 --- /dev/null +++ b/fieldservice/models/district.py @@ -0,0 +1,12 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class District(models.Model): + _name = 'district' + _description = 'District' + + name = fields.Char(string='Name') + region_id = fields.Many2one('region', string='Region') diff --git a/fieldservice/models/fsm_location.py b/fieldservice/models/fsm_location.py index 7c3f5a8f6d..c5aa55c874 100644 --- a/fieldservice/models/fsm_location.py +++ b/fieldservice/models/fsm_location.py @@ -1,6 +1,8 @@ # Copyright (C) 2018 - TODAY, Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import pytz + from odoo import api, fields, models @@ -9,6 +11,13 @@ class FSMLocation(models.Model): _inherits = {'res.partner': 'partner_id'} _description = 'Field Service Location' + @api.model + def _tz_get(self): + return [(tz, tz) for tz in sorted(pytz.all_timezones, + key=lambda tz: tz + if not tz.startswith('Etc/') + else '_')] + direction = fields.Char(string='Directions') partner_id = fields.Many2one('res.partner', string='Related Partner', required=True, ondelete='restrict', @@ -21,18 +30,27 @@ class FSMLocation(models.Model): auto_join=True) tag_ids = fields.Many2many('fsm.tag', string='Tags') - building = fields.Char(string='Building', size=35) - floor = fields.Char(string='Floor', size=35) - unit = fields.Char(string='Unit', size=35) - room = fields.Char(string='Room', size=35) description = fields.Char(string='Description') - territory = fields.Char(string='Territory', size=35) - branch = fields.Char(string='Branch', size=35) - district = fields.Char(string='District', size=35) - region = fields.Char(string='Region', size=35) - timezone = fields.Char(string='Timezone', size=35) + location_id = fields.Many2one('location', string='Location') + territory_id = fields.Many2one('territory', string='Territory') + branch_id = fields.Many2one('branch', string='Branch') + district_id = fields.Many2one('district', string='District') + region_id = fields.Many2one('region', string='Region') + timezone = fields.Selection(_tz_get, string='Timezone') @api.model def create(self, vals): vals.update({'fsm_location': True}) return super(FSMLocation, self).create(vals) + + @api.onchange('territory_id') + def _onchange_territory_id(self): + self.branch_id = self.territory_id.branch_id + + @api.onchange('branch_id') + def _onchange_branch_id(self): + self.district_id = self.branch_id.district_id + + @api.onchange('district_id') + def _onchange_district_id(self): + self.region_id = self.district_id.region_id diff --git a/fieldservice/models/location.py b/fieldservice/models/location.py new file mode 100644 index 0000000000..a46b3eeab4 --- /dev/null +++ b/fieldservice/models/location.py @@ -0,0 +1,32 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class Location(models.Model): + _name = 'location' + _description = 'Location' + _parent_name = "parent_id" + _parent_store = True + _parent_order = 'name' + _order = 'parent_left' + _rec_name = 'complete_name' + + name = fields.Char(string='Name') + complete_name = fields.Char("Full Location Name", + compute='_compute_complete_name', store=True) + parent_id = fields.Many2one('location', string='Parent Location', + ondelete='restrict') + notes = fields.Html(string='Notes') + parent_left = fields.Integer('Left Parent', index=True) + parent_right = fields.Integer('Right Parent', index=True) + + @api.one + @api.depends('name', 'parent_id.complete_name') + def _compute_complete_name(self): + if self.parent_id.complete_name: + self.complete_name = '%s/%s' % \ + (self.parent_id.complete_name, self.name) + else: + self.complete_name = self.name diff --git a/fieldservice/models/region.py b/fieldservice/models/region.py new file mode 100644 index 0000000000..cca0bb20e1 --- /dev/null +++ b/fieldservice/models/region.py @@ -0,0 +1,11 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class Region(models.Model): + _name = 'region' + _description = 'Region' + + name = fields.Char(string='Name') diff --git a/fieldservice/models/territory.py b/fieldservice/models/territory.py new file mode 100644 index 0000000000..9d23fb39bf --- /dev/null +++ b/fieldservice/models/territory.py @@ -0,0 +1,12 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class Territory(models.Model): + _name = 'territory' + _description = 'Territory' + + name = fields.Char(string='Name') + branch_id = fields.Many2one('branch', string='Branch') diff --git a/fieldservice/security/ir.model.access.csv b/fieldservice/security/ir.model.access.csv index 0ae52da434..b212e2316a 100644 --- a/fieldservice/security/ir.model.access.csv +++ b/fieldservice/security/ir.model.access.csv @@ -13,3 +13,13 @@ access_fsm_route_user,fsm.route.user,model_fsm_route,fieldservice.group_fsm_user access_fsm_route_dispatcher,fsm.route.dispatcher,model_fsm_route,fieldservice.group_fsm_dispatcher,1,1,1,1 access_fsm_vehicle_fsm_user,fsm.vehicle.user,model_fsm_vehicle,fieldservice.group_fsm_user,1,0,0,0 access_fsm_vehicle_fsm_manager,fsm.vehicle.manager,model_fsm_vehicle,fieldservice.group_fsm_manager,1,1,1,1 +access_fsm_location_fsm_user,location.user,model_location,fieldservice.group_fsm_user,1,0,0,0 +access_fsm_location_fsm_manager,location.manager,model_location,fieldservice.group_fsm_manager,1,1,1,1 +access_fsm_territory_user,territory.user,model_territory,fieldservice.group_fsm_user,1,0,0,0 +access_fsm_territory_manager,territory.manager,model_territory,fieldservice.group_fsm_manager,1,1,1,1 +access_fsm_branch_user,branch.user,model_branch,fieldservice.group_fsm_user,1,0,0,0 +access_fsm_branch_manager,branch.manager,model_branch,fieldservice.group_fsm_manager,1,1,1,1 +access_fsm_district_user,district.user,model_district,fieldservice.group_fsm_user,1,0,0,0 +access_fsm_district_manager,district.manager,model_district,fieldservice.group_fsm_manager,1,1,1,1 +access_fsm_region_user,region.user,model_region,fieldservice.group_fsm_user,1,0,0,0 +access_fsm_region_manager,region.manager,model_region,fieldservice.group_fsm_manager,1,1,1,1 diff --git a/fieldservice/views/branch.xml b/fieldservice/views/branch.xml new file mode 100644 index 0000000000..17b69bce82 --- /dev/null +++ b/fieldservice/views/branch.xml @@ -0,0 +1,40 @@ + + + + + + branch.tree + branch + + + + + + + + + + + branch.form + branch + +
+ + +
+
+
+ + + Branches + branch + + form + tree,form + + +
diff --git a/fieldservice/views/district.xml b/fieldservice/views/district.xml new file mode 100644 index 0000000000..030ed26908 --- /dev/null +++ b/fieldservice/views/district.xml @@ -0,0 +1,40 @@ + + + + + + district.tree + district + + + + + + + + + + + district.form + district + +
+ + +
+
+
+ + + Districts + district + + form + tree,form + + +
diff --git a/fieldservice/views/fsm_location.xml b/fieldservice/views/fsm_location.xml index 0d9c95ed64..4921289c7b 100644 --- a/fieldservice/views/fsm_location.xml +++ b/fieldservice/views/fsm_location.xml @@ -10,12 +10,7 @@ - - - - - - + @@ -40,12 +35,7 @@
- - - - - - +
@@ -55,10 +45,15 @@ - - - - + + + + + + + + + @@ -69,11 +64,6 @@ - - - - - @@ -91,16 +81,13 @@ - - - - + - - - - + + + + diff --git a/fieldservice/views/location.xml b/fieldservice/views/location.xml new file mode 100644 index 0000000000..0d0a46f2bd --- /dev/null +++ b/fieldservice/views/location.xml @@ -0,0 +1,43 @@ + + + + + + location.tree + location + + + + + + + + + + + location.form + location + +
+ + +
+
+
+ + + Locations + location + + form + tree,form + + +
diff --git a/fieldservice/views/menu.xml b/fieldservice/views/menu.xml index 64423725c8..0a9c4845e3 100644 --- a/fieldservice/views/menu.xml +++ b/fieldservice/views/menu.xml @@ -54,7 +54,7 @@ parent="data" sequence="10"/> - + + + + + + + + + + diff --git a/fieldservice/views/region.xml b/fieldservice/views/region.xml new file mode 100644 index 0000000000..5758f7779e --- /dev/null +++ b/fieldservice/views/region.xml @@ -0,0 +1,37 @@ + + + + + + region.tree + region + + + + + + + + + + region.form + region + +
+ + +
+
+
+ + + Regions + region + + form + tree,form + + +
diff --git a/fieldservice/views/territory.xml b/fieldservice/views/territory.xml new file mode 100644 index 0000000000..b6f5a465ac --- /dev/null +++ b/fieldservice/views/territory.xml @@ -0,0 +1,40 @@ + + + + + + territory.tree + territory + + + + + + + + + + + territory.form + territory + +
+ + +
+
+
+ + + Territories + territory + + form + tree,form + + +
From fdce3145ef73a3cd3948f42c374db38a76513a68 Mon Sep 17 00:00:00 2001 From: Maxime Chambreuil Date: Mon, 22 Oct 2018 13:39:07 -0500 Subject: [PATCH 006/433] [FIX] PR comments --- fieldservice/models/fsm_location.py | 3 +-- fieldservice/models/fsm_person.py | 3 +-- fieldservice/models/location.py | 3 ++- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/fieldservice/models/fsm_location.py b/fieldservice/models/fsm_location.py index c5aa55c874..de842b3e23 100644 --- a/fieldservice/models/fsm_location.py +++ b/fieldservice/models/fsm_location.py @@ -8,7 +8,6 @@ class FSMLocation(models.Model): _name = 'fsm.location' - _inherits = {'res.partner': 'partner_id'} _description = 'Field Service Location' @api.model @@ -21,7 +20,7 @@ def _tz_get(self): direction = fields.Char(string='Directions') partner_id = fields.Many2one('res.partner', string='Related Partner', required=True, ondelete='restrict', - auto_join=True) + delegate=True, auto_join=True) owner_id = fields.Many2one('res.partner', string='Related Owner', required=True, ondelete='restrict', auto_join=True) diff --git a/fieldservice/models/fsm_person.py b/fieldservice/models/fsm_person.py index 211b08d515..20d7101e6a 100644 --- a/fieldservice/models/fsm_person.py +++ b/fieldservice/models/fsm_person.py @@ -6,12 +6,11 @@ class FSMPerson(models.Model): _name = 'fsm.person' - _inherits = {'res.partner': 'partner_id'} _description = 'Field Service Person' partner_id = fields.Many2one('res.partner', string='Related Partner', required=True, ondelete='restrict', - auto_join=True) + delegate=True, auto_join=True) @api.model def create(self, vals): diff --git a/fieldservice/models/location.py b/fieldservice/models/location.py index a46b3eeab4..d4284173a8 100644 --- a/fieldservice/models/location.py +++ b/fieldservice/models/location.py @@ -15,7 +15,8 @@ class Location(models.Model): name = fields.Char(string='Name') complete_name = fields.Char("Full Location Name", - compute='_compute_complete_name', store=True) + compute='_compute_complete_name', + store=True, index=True) parent_id = fields.Many2one('location', string='Parent Location', ondelete='restrict') notes = fields.Html(string='Notes') From ce00b2f136e5108090abeda76adb5dbc3427c913 Mon Sep 17 00:00:00 2001 From: Bhavesh Odedra Date: Tue, 23 Oct 2018 18:09:48 +0530 Subject: [PATCH 007/433] [ADD] Boolean field in fsm.stage to indicate last stages --- fieldservice/README.rst | 1 + fieldservice/data/fsm_stage.xml | 2 ++ fieldservice/models/fsm_stage.py | 3 +++ fieldservice/readme/CONTRIBUTORS.rst | 1 + fieldservice/static/description/index.html | 1 + fieldservice/views/fsm_stage.xml | 2 ++ 6 files changed, 10 insertions(+) diff --git a/fieldservice/README.rst b/fieldservice/README.rst index 53b4fa715f..2a6238b233 100644 --- a/fieldservice/README.rst +++ b/fieldservice/README.rst @@ -85,6 +85,7 @@ Contributors * Wolfgang Hall * Maxime Chambreuil * Steve Campbell +* Bhavesh Odedra Other credits ~~~~~~~~~~~~~ diff --git a/fieldservice/data/fsm_stage.xml b/fieldservice/data/fsm_stage.xml index f397177a56..f8281ff83a 100644 --- a/fieldservice/data/fsm_stage.xml +++ b/fieldservice/data/fsm_stage.xml @@ -39,11 +39,13 @@ Completed 80 + True Cancelled 100 True + True diff --git a/fieldservice/models/fsm_stage.py b/fieldservice/models/fsm_stage.py index 84bb19daa5..89e497aa9b 100644 --- a/fieldservice/models/fsm_stage.py +++ b/fieldservice/models/fsm_stage.py @@ -29,3 +29,6 @@ class FSMStage(models.Model): fold = fields.Boolean('Folded in Kanban', help='This stage is folded in the kanban view when ' 'there are no record in that stage to display.') + is_closed = fields.Boolean('Is a close stage', + help='Services in this stage are considered ' + 'as closed.') diff --git a/fieldservice/readme/CONTRIBUTORS.rst b/fieldservice/readme/CONTRIBUTORS.rst index db6ccc051b..7bc9bf92b3 100644 --- a/fieldservice/readme/CONTRIBUTORS.rst +++ b/fieldservice/readme/CONTRIBUTORS.rst @@ -1,3 +1,4 @@ * Wolfgang Hall * Maxime Chambreuil * Steve Campbell +* Bhavesh Odedra diff --git a/fieldservice/static/description/index.html b/fieldservice/static/description/index.html index e7c1b37487..697dfc9f98 100644 --- a/fieldservice/static/description/index.html +++ b/fieldservice/static/description/index.html @@ -436,6 +436,7 @@

Contributors

  • Wolfgang Hall <whall@opensourceintegrators.com>
  • Maxime Chambreuil <mchambreuil@opensourceintegrators.com>
  • Steve Campbell <scampbell@opensourceintegrators.com>
  • +
  • Bhavesh Odedra <bodedra@opensourceintegrators.com>
  • diff --git a/fieldservice/views/fsm_stage.xml b/fieldservice/views/fsm_stage.xml index 69e697e34d..50f0760ba8 100644 --- a/fieldservice/views/fsm_stage.xml +++ b/fieldservice/views/fsm_stage.xml @@ -8,6 +8,7 @@ + @@ -25,6 +26,7 @@ + From f54596958b282892d0159bb00485b50124fd0bb3 Mon Sep 17 00:00:00 2001 From: brian10048 Date: Sat, 24 Nov 2018 14:28:34 -0500 Subject: [PATCH 008/433] [ADD] fieldservice_stock 0.0.1 --- fieldservice/models/res_config_settings.py | 4 +++- fieldservice/views/res_config_settings.xml | 21 ++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/fieldservice/models/res_config_settings.py b/fieldservice/models/res_config_settings.py index ffe064f969..3d51f7c243 100644 --- a/fieldservice/models/res_config_settings.py +++ b/fieldservice/models/res_config_settings.py @@ -2,7 +2,6 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import fields, models -# from odoo import apit, _ class ResConfigSettings(models.TransientModel): @@ -14,3 +13,6 @@ class ResConfigSettings(models.TransientModel): group_fsm_vehicle = fields.Boolean( string='Manage Vehicles', implied_group='fieldservice.group_fsm_vehicle') + + module_fieldservice_stock = fields.Boolean( + sting='Use Odoo Stock Logistics') diff --git a/fieldservice/views/res_config_settings.xml b/fieldservice/views/res_config_settings.xml index 1ef9ec3bc8..01fadaaea8 100644 --- a/fieldservice/views/res_config_settings.xml +++ b/fieldservice/views/res_config_settings.xml @@ -40,13 +40,20 @@
    - +

    Integrations

    +
    +
    +
    + +
    +
    +
    +
    +
    From 28e814c601c0cf20d247748253cade8f0f3f26d9 Mon Sep 17 00:00:00 2001 From: jesulo <40969440+jesulo@users.noreply.github.com> Date: Thu, 29 Nov 2018 16:55:06 -0300 Subject: [PATCH 009/433] Update fsm.wizard --- fieldservice/wizard/fsm_wizard.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fieldservice/wizard/fsm_wizard.py b/fieldservice/wizard/fsm_wizard.py index 97e4504ed9..c6d46e07c8 100644 --- a/fieldservice/wizard/fsm_wizard.py +++ b/fieldservice/wizard/fsm_wizard.py @@ -32,7 +32,12 @@ def action_convert_location(self, partner): res = self.env['fsm.location'].search_count( [('partner_id', '=', partner.id)]) if res == 0: - self.env['fsm.location'].create({'partner_id': partner.id}) + vals = { + 'partner_id': partner.id, + 'owner_id': partner.id, + 'customer_id': partner.id, + } + self.env['fsm.location'].create(vals) partner.write({'fsm_location': True}) else: raise UserError(_('A Field Service Location related to that' From 11465503450d02cfda237bf06487e45d00fc8afc Mon Sep 17 00:00:00 2001 From: Sandip Mangukiya Date: Thu, 8 Nov 2018 22:19:01 -0800 Subject: [PATCH 010/433] [IMP] timeline view --- fieldservice/__manifest__.py | 3 + fieldservice/models/fsm_order.py | 12 + fieldservice/models/fsm_person.py | 11 + fieldservice/static/src/js/fsm_gantt.js | 153 ++++++++++++ .../static/src/js/fsm_gantt_person_filter.js | 225 ++++++++++++++++++ .../src/less/fsm_gantt_person_filter.less | 10 + .../src/xml/fsm_gantt_person_filter.xml | 28 +++ fieldservice/views/fsm_order.xml | 38 ++- 8 files changed, 479 insertions(+), 1 deletion(-) create mode 100644 fieldservice/static/src/js/fsm_gantt.js create mode 100644 fieldservice/static/src/js/fsm_gantt_person_filter.js create mode 100644 fieldservice/static/src/less/fsm_gantt_person_filter.less create mode 100644 fieldservice/static/src/xml/fsm_gantt_person_filter.xml diff --git a/fieldservice/__manifest__.py b/fieldservice/__manifest__.py index 0ee4fd0c2b..0d91b84596 100644 --- a/fieldservice/__manifest__.py +++ b/fieldservice/__manifest__.py @@ -37,6 +37,9 @@ 'views/menu.xml', 'wizard/fsm_wizard.xml', ], + 'qweb': [ + 'static/src/xml/*.xml', + ], 'application': True, 'license': 'AGPL-3', 'development_status': 'Beta', diff --git a/fieldservice/models/fsm_order.py b/fieldservice/models/fsm_order.py index 23d283c232..18141eaf8f 100644 --- a/fieldservice/models/fsm_order.py +++ b/fieldservice/models/fsm_order.py @@ -61,6 +61,11 @@ def _default_stage_id(self): date_start = fields.Datetime(string='Actual Start') date_end = fields.Datetime(string='Actual End') + # Location + branch_id = fields.Many2one('branch', string='Branch') + district_id = fields.Many2one('district', string='District') + region_id = fields.Many2one('region', string='Region') + @api.model def _read_group_stage_ids(self, stages, domain, order): stage_ids = self.env['fsm.stage'].search([]) @@ -139,3 +144,10 @@ def onchange_scheduled_duration(self): self.scheduled_date_start) +\ timedelta(hours=self.scheduled_duration) self.scheduled_date_end = str(date_to_with_delta) + + @api.onchange('fsm_location_id') + def onchange_fsm_location_id(self): + if self.fsm_location_id: + self.branch_id = self.fsm_location_id.branch_id or False + self.district_id = self.fsm_location_id.district_id or False + self.region_id = self.fsm_location_id.region_id or False diff --git a/fieldservice/models/fsm_person.py b/fieldservice/models/fsm_person.py index 20d7101e6a..da5385be2b 100644 --- a/fieldservice/models/fsm_person.py +++ b/fieldservice/models/fsm_person.py @@ -16,3 +16,14 @@ class FSMPerson(models.Model): def create(self, vals): vals.update({'fsm_person': True}) return super(FSMPerson, self).create(vals) + + @api.multi + def get_person_information(self, vals): + # get person ids + person_ids = self.search([('id', '!=', 0), ('active', '=', True)]) + person_information_dict = [] + for person in person_ids: + person_information_dict.append({ + 'id': person.id, + 'name': person.name}) + return person_information_dict diff --git a/fieldservice/static/src/js/fsm_gantt.js b/fieldservice/static/src/js/fsm_gantt.js new file mode 100644 index 0000000000..8e4f5971df --- /dev/null +++ b/fieldservice/static/src/js/fsm_gantt.js @@ -0,0 +1,153 @@ +odoo.define('fsm_gantt.fsm_gantt', function (require) { +"use strict"; + + var core = require('web.core'); + var data = require('web.data'); + var time = require('web.time'); + var session = require('web.session'); + var TimelineRenderer = require('web_timeline.TimelineRenderer'); + + var _t = core._t; + var _lt = core._lt; + var QWeb = core.qweb; + +TimelineRenderer.include({ + init: function (parent, state, params) { + var self = this; + this._super.apply(this, arguments); + this.modelName = params.model; + this.mode = params.mode; + this.options = params.options; + this.permissions = params.permissions; + this.timeline = params.timeline; + this.date_start = params.date_start; + this.date_stop = params.date_stop; + this.date_delay = params.date_delay; + this.colors = params.colors; + this.fieldNames = params.fieldNames; + this.dependency_arrow = params.dependency_arrow; + this.view = params.view; + this.modelClass = this.view.model; + self.res_users = []; + self.res_users_ids = []; + + // Find their matching names + this._rpc({ + model: 'fsm.person', + method: 'get_person_information', + args: [[session.uid], {}], + }).then(function (result) { + self.res_users.push(result); + for(var r in result){ + self.res_users_ids.push(result[r]['id']); + } + }); + }, + + on_data_loaded_2: function (events, group_bys, adjust_window) { + var self = this; + var data = []; + var groups = []; + this.grouped_by = group_bys; + _.each(events, function (event) { + if (event[self.date_start]) { + data.push(self.event_data_transform(event)); + } + }); + var groups = self.split_groups(events, group_bys); + if (group_bys[0]=="fsm_person_id"){ + var groups_user_ids = []; + for(var g in groups){ + groups_user_ids.push(groups[g]['id']); + } + for(var u in self.res_users_ids){ + if(!(self.res_users_ids[u] in groups_user_ids) || self.res_users_ids[u] != -1){ + // Get User Name + var user_name = '-'; + for (var n in self.res_users[0]){ + if (self.res_users[0][n]['id'] == self.res_users_ids[u]){ + user_name = self.res_users[0][n]['name']; + } + } + var is_available=false; + for (var i in groups){ + if(groups[i]['id']==self.res_users_ids[u]){ + is_available = true; + } + } + if(!is_available){ + groups.push({id:self.res_users_ids[u], content: _t(user_name)}); + } + } + } + } + this.timeline.setGroups(groups); + this.timeline.setItems(data); + var mode = !this.mode || this.mode === 'fit'; + var adjust = _.isUndefined(adjust_window) || adjust_window; + if (mode && adjust) { + this.timeline.fit(); + } + }, + /* Transform Odoo event object to timeline event object */ + event_data_transform: function (evt) { + var self = this; + var date_start = new moment(); + var date_stop = null; + + var date_delay = evt[this.date_delay] || false, + all_day = this.all_day ? evt[this.all_day] : false; + + if (all_day) { + date_start = time.auto_str_to_date(evt[this.date_start].split(' ')[0], 'start'); + if (this.no_period) { + date_stop = date_start; + } else { + date_stop = this.date_stop ? time.auto_str_to_date(evt[this.date_stop].split(' ')[0], 'stop') : null; + } + } else { + date_start = time.auto_str_to_date(evt[this.date_start]); + date_stop = this.date_stop ? time.auto_str_to_date(evt[this.date_stop]) : null; + } + + if (!date_stop && date_delay) { + date_stop = moment(date_start).add(date_delay, 'hours').toDate(); + } + + var group = evt[self.last_group_bys[0]]; + if (group && group instanceof Array) { + group = _.first(group); + } else { + group = -1; + } + _.each(self.colors, function (color) { + if (eval("'" + evt[color.field] + "' " + color.opt + " '" + color.value + "'")) { + self.color = color.color; + }else if (eval("'" + evt[color.field][1] + "' " + color.opt + " '" + color.value + "'")) { + self.color = color.color; + } + }); + + var content = _.isUndefined(evt.__name) ? evt.display_name : evt.__name; + if (this.arch.children.length) { + content = this.render_timeline_item(evt); + } + + var r = { + 'start': date_start, + 'content': content, + 'id': evt.id, + 'group': group, + 'evt': evt, + 'style': 'background-color: ' + self.color + ';' + }; + // Check if the event is instantaneous, if so, display it with a point on the timeline (no 'end') + if (date_stop && !moment(date_start).isSame(date_stop)) { + r.end = date_stop; + } + self.color = null; + return r; + }, +}); + +}); \ No newline at end of file diff --git a/fieldservice/static/src/js/fsm_gantt_person_filter.js b/fieldservice/static/src/js/fsm_gantt_person_filter.js new file mode 100644 index 0000000000..51dbef72a4 --- /dev/null +++ b/fieldservice/static/src/js/fsm_gantt_person_filter.js @@ -0,0 +1,225 @@ +odoo.define('fsm_gantt_person_filter.fsm_gantt_person_filter', function (require) { + "use strict"; + + var TimelineRenderer = require('web_timeline.TimelineRenderer'); + var session = require('web.session'); + var search_filters = require('web.search_filters'); + var core = require('web.core'); + var _t = core._t; + var _lt = core._lt; + var QWeb = core.qweb; + + TimelineRenderer.include({ + + init: function () { + this._super.apply(this, arguments); + /*Initilaize propositions */ + this.propositions = []; + }, + + do_search: function (domains, contexts, group_bys) { + var self = this; + self.last_domains = domains; + self.last_contexts = contexts; + + // select the group by + var n_group_bys = []; + if (this.arch.attrs.default_group_by) { + n_group_bys = this.arch.attrs.default_group_by.split(','); + } + if (group_bys.length) { + n_group_bys = group_bys; + } + self.last_group_bys = n_group_bys; + // gather the fields to get + var fields = _.compact(_.map(["date_start", "date_delay", "date_stop", "progress"], function(key) { + return self.arch.attrs[key] || ''; + })); + fields = _.uniq(fields.concat(_.pluck(this.colors, "field").concat(n_group_bys))); + return this._rpc({ + model: this.modelName, + method: 'search_read', + fields: fields, + args: [domains], + kwargs: {context: contexts}, + }).then(function (r) { + return self.on_data_loaded(r, n_group_bys, true); + }); + }, + + /*Search Data Related To User Filter. */ + do_search_related_user_filter : function (domains, contexts, group_bys,user_ids) { + var self = this; + // select the group by + var n_group_bys = []; + if (this.arch.attrs.default_group_by) { + n_group_bys = this.arch.attrs.default_group_by.split(','); + } + if (group_bys.length) { + n_group_bys = group_bys; + } + // gather the fields to get + var fields = _.compact(_.map(["date_start", "date_delay", "date_stop", "progress"], function(key) { + return self.arch.attrs[key] || ''; + })); + fields = _.uniq(fields.concat(_.pluck(this.colors, "field").concat(n_group_bys))); + return this._rpc({ + model: this.modelName, + method: 'search_read', + fields: fields, + args: [domains], + kwargs: {context: contexts}, + }).then(function (r) { + return self.on_user_data_loaded(r, n_group_bys, true, user_ids); + }); + }, + + on_user_data_loaded: function (events, group_bys, adjust_window, user_ids) { + var self = this; + var ids = _.pluck(events, "id"); + return this._rpc({ + model: this.modelName, + method: 'name_get', + args: [ + ids, + ], + context: this.getSession().user_context, + }).then(function(names) { + var nevents = _.map(events, function (event) { + return _.extend({ + __name: _.detect(names, function (name) { + return name[0] === event.id; + })[1] + }, event); + }); + return self.on_user_data_loaded_2(nevents, group_bys, adjust_window, user_ids); + }); + }, + + on_user_data_loaded_2: function (events, group_bys, adjust_window, user_ids) { + var self = this; + var data = []; + var groups = []; + this.grouped_by = group_bys; + _.each(events, function (event) { + if (event[self.date_start]) { + data.push(self.event_data_transform(event)); + } + }); + // get the groups + var split_groups = function(events, group_bys) { + if (group_bys.length === 0) + return events; + var groups = []; + groups.push({id:-1, content: _t('-')}) + _.each(events, function(event) { + var group_name = event[_.first(group_bys)]; + if (group_name) { + var group = _.find(groups, function(group) { return _.isEqual(group.id, group_name[0]); }); + if (group === undefined) { + group = {id: group_name[0], content: group_name[1]}; + groups.push(group); + } + } + }); + return groups; + } + var groups = split_groups(events, group_bys); + _.each(user_ids,function(user){ + var group = _.find(groups, function(group) { return _.isEqual(group.id, user.id); }); + if (group === undefined) { + group = {id: user.id, content: user.name}; + groups.push(group); + } + }); + + this.timeline.setGroups(groups); + this.timeline.setItems(data); + var mode = !this.mode || this.mode === 'fit'; + var adjust = _.isUndefined(adjust_window) || adjust_window; + if (mode && adjust) { + this.timeline.fit(); + } + }, + + /*Apply/Clear User Filter. */ + apply_clear_user_filter : function (clear) { + var self = this; + if(clear){ + self.user_domains = false; + self.$el.find("#user_filer .o_searchview_extended_prop_field").val(""); + self.$el.find("#user_filer .o_searchview_extended_prop_field").change(); + self.$el.find("#user_filer .o_searchview_extended_prop_field").val("category_id"); + self.$el.find("#user_filer .o_searchview_extended_prop_field").change(); + self.do_search(self.last_domains, self.last_contexts, self.last_group_bys) + }else{ + var filters = _.invoke(this.propositions, 'get_filter'); + var domain = (filters[0] && filters[0].attrs && filters[0].attrs.domain )? filters[0].attrs.domain : false; + /* new method call improved by Sandip on 2018-09-21*/ + if(domain){ + this._rpc({ + model: 'fsm.person', + method: 'search', + args: [domain], + kwargs: {context: session.user_context}, + }).then(function (user_ids) { + var list_user_ids = []; + $.each(user_ids,function(index, value){ + var id = value; + var name = ''; + for(var i in self.res_users[0]){ + if(self.res_users[0][i]['id'] == id){ + name=self.res_users[0][i]['name']; + } + } + list_user_ids.push({'id':id, 'name':name}); + }); + var ids = user_ids; + ids.push(false); + self.user_domains = ["fsm_person_id","in",ids]; + var temp_domain = []; + if(self.last_domains){ + temp_domain = _.clone(self.last_domains); + } + temp_domain.push(self.user_domains); + self.do_search_related_user_filter(temp_domain, self.last_contexts, self.last_group_bys,list_user_ids); + }); + } + } + }, + + start: function () { + var self = this; + /*Bind User Filter Apply/Clear Click Event*/ + this.$el.find(".oe_timeline_button_apply").click($.proxy(this.on_apply_clicked, this)); + this.$el.find(".oe_timeline_button_clear").click($.proxy(this.on_clear_clicked, this)); + + /*Fetch User Fields And Append To Timeline View.*/ + self._rpc({ + model: 'fsm.person', + method: 'fields_get', + }).then(function (fields) { + self.user_filter = true; + var prop = new search_filters.ExtendedSearchProposition(self, fields); + self.propositions.push(prop); + prop.appendTo(self.$el.find("#user_filer")); + self.$el.find("#user_filer .o_searchview_extended_delete_prop").hide(); + self.$el.find("#user_filer .o_or_filter").hide(); + }); + return this._super(); + }, + + on_apply_clicked: function(){ + /*Call Apply user Filter*/ + this.apply_clear_user_filter(false); + }, + + on_clear_clicked: function(){ + /*Call Clear User Filter*/ + this.apply_clear_user_filter(true); + } + + }); + + +}); diff --git a/fieldservice/static/src/less/fsm_gantt_person_filter.less b/fieldservice/static/src/less/fsm_gantt_person_filter.less new file mode 100644 index 0000000000..ea472f3f9a --- /dev/null +++ b/fieldservice/static/src/less/fsm_gantt_person_filter.less @@ -0,0 +1,10 @@ +/*User Filter Design*/ +#user_filer{ + .o_filter_condition select,.o_searchview_extended_prop_value,.o_datepicker,.o_searchview_extended_prop_value input{ + display: inline !important; + width: 150px !important; + } + li{ + list-style: none; + } +} diff --git a/fieldservice/static/src/xml/fsm_gantt_person_filter.xml b/fieldservice/static/src/xml/fsm_gantt_person_filter.xml new file mode 100644 index 0000000000..de3ab6466b --- /dev/null +++ b/fieldservice/static/src/xml/fsm_gantt_person_filter.xml @@ -0,0 +1,28 @@ + diff --git a/fieldservice/views/fsm_order.xml b/fieldservice/views/fsm_order.xml index 2ac62d400e..9b8ee89be3 100644 --- a/fieldservice/views/fsm_order.xml +++ b/fieldservice/views/fsm_order.xml @@ -219,6 +219,40 @@
    +