Merge 'bemade_fsm' into bemade-addons
This commit is contained in:
commit
377141c3b0
25 changed files with 1096 additions and 0 deletions
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: "[ENH]"
|
||||||
|
labels: enhancement
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen. Ex. As a [type of user], I could click [...] and [...] would happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
1
bemade_fsm/__init__.py
Normal file
1
bemade_fsm/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from . import models
|
||||||
55
bemade_fsm/__manifest__.py
Normal file
55
bemade_fsm/__manifest__.py
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
######################################################################################
|
||||||
|
#
|
||||||
|
# Bemade Inc.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2023-June Bemade Inc. (<https://www.bemade.org>).
|
||||||
|
# Author: Marc Durepos (Contact : mdurepos@durpro.com)
|
||||||
|
#
|
||||||
|
# This program is under the terms of the Odoo Proprietary License v1.0 (OPL-1)
|
||||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies of the Software
|
||||||
|
# or modified copies of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
# DEALINGS IN THE SOFTWARE.
|
||||||
|
#
|
||||||
|
########################################################################################
|
||||||
|
{
|
||||||
|
'name': 'Durpro Field Service Management',
|
||||||
|
'version': '15.0.1.0.0',
|
||||||
|
'summary': 'Adds functionality necessary for managing field service operations at Durpro.',
|
||||||
|
'description': 'Adds functionality necessary for managing field service operations at Durpro.',
|
||||||
|
'category': 'Services/Field Service',
|
||||||
|
'author': 'Bemade Inc.',
|
||||||
|
'website': 'http://www.bemade.org',
|
||||||
|
'license': 'OPL-1',
|
||||||
|
'depends': ['project',
|
||||||
|
'stock',
|
||||||
|
'sale',
|
||||||
|
'sale_project',
|
||||||
|
'sale_stock',
|
||||||
|
'industry_fsm',
|
||||||
|
'industry_fsm_sale',
|
||||||
|
'bemade_partner_root_ancestor',
|
||||||
|
],
|
||||||
|
'data': ['views/task_template_views.xml',
|
||||||
|
'views/equipment.xml',
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
'views/product_views.xml',
|
||||||
|
'views/res_partner.xml',
|
||||||
|
'views/menus.xml',
|
||||||
|
'views/task_views.xml',
|
||||||
|
],
|
||||||
|
'assets': {
|
||||||
|
'web.assets_tests': [
|
||||||
|
'bemade_fsm/static/tests/tours/task_template_tour.js',
|
||||||
|
'bemade_fsm/static/tests/tours/task_equipment_tour.js',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'installable': True,
|
||||||
|
'auto_install': False
|
||||||
|
}
|
||||||
6
bemade_fsm/models/__init__.py
Normal file
6
bemade_fsm/models/__init__.py
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
from . import task_template
|
||||||
|
from . import product_template
|
||||||
|
from . import sale_order
|
||||||
|
from . import equipment
|
||||||
|
from . import task
|
||||||
|
from . import res_partner
|
||||||
90
bemade_fsm/models/equipment.py
Normal file
90
bemade_fsm/models/equipment.py
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class EquipmentTag(models.Model):
|
||||||
|
_name = "bemade_fsm.equipment.tag"
|
||||||
|
_description = 'Field service equipment category'
|
||||||
|
|
||||||
|
name = fields.Char('Name', required=True, translate=True)
|
||||||
|
color = fields.Integer('Color Index', default=10)
|
||||||
|
|
||||||
|
_sql_constraints = [
|
||||||
|
('name_uniq', 'unique (name)', "Tag name already exists !"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class EquipmentType(models.Model):
|
||||||
|
_name = 'bemade_fsm.equipment.type'
|
||||||
|
_description = 'Field service equipment type'
|
||||||
|
_order = 'id'
|
||||||
|
|
||||||
|
name = fields.Char(string='Intervention Name', required=True, translate=True)
|
||||||
|
|
||||||
|
class Equipment(models.Model):
|
||||||
|
_name = 'bemade_fsm.equipment'
|
||||||
|
_rec_name = 'complete_name'
|
||||||
|
_description = 'Field service equipment'
|
||||||
|
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||||
|
|
||||||
|
pid_tag = fields.Char(string="P&ID Tag", tracking=True)
|
||||||
|
|
||||||
|
name = fields.Char(string="Name", tracking=True, required=True)
|
||||||
|
|
||||||
|
complete_name = fields.Char(string="Equipment Name", compute="_compute_complete_name", store=True)
|
||||||
|
|
||||||
|
tag_ids = fields.Many2many('bemade_fsm.equipment.tag',
|
||||||
|
string='Application',
|
||||||
|
tracking=True,
|
||||||
|
help="Classify and analyze your equipment categories like: Boiler, Laboratory, "
|
||||||
|
"Waste water, Pure water")
|
||||||
|
partner_id = fields.Many2one('res.partner',
|
||||||
|
string="Owner",
|
||||||
|
compute="_compute_partner")
|
||||||
|
description = fields.Text(string="Description",
|
||||||
|
tracking=True)
|
||||||
|
|
||||||
|
partner_location_id = fields.Many2one('res.partner',
|
||||||
|
string="Physical Address",
|
||||||
|
tracking=True,)
|
||||||
|
|
||||||
|
location_notes = fields.Text(string="Physical Location Notes",
|
||||||
|
tracking=True)
|
||||||
|
task_ids = fields.One2many(comodel_name='project.task',
|
||||||
|
inverse_name='equipment_id',
|
||||||
|
string='Interventions')
|
||||||
|
|
||||||
|
@api.depends('partner_location_id')
|
||||||
|
def _compute_partner(self):
|
||||||
|
for rec in self:
|
||||||
|
rec.partner_id = rec.partner_location_id and rec.partner_location_id.root_ancestor
|
||||||
|
|
||||||
|
@api.depends('pid_tag', 'name')
|
||||||
|
def _compute_complete_name(self):
|
||||||
|
for rec in self:
|
||||||
|
rec.complete_name = "[%s] %s" % (rec.pid_tag or ' ', rec.name)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def name_search(self, name='', args=None, operator='ilike', limit=100):
|
||||||
|
args = args or []
|
||||||
|
if name:
|
||||||
|
equipments = self.search([
|
||||||
|
'|', '|', '|',
|
||||||
|
('pid_tag', operator, name),
|
||||||
|
('name', operator, name),
|
||||||
|
('partner_id.name', operator, name),
|
||||||
|
('partner_location_id.name', operator, name)],
|
||||||
|
limit=limit)
|
||||||
|
else:
|
||||||
|
equipments = self.search(args, limit=limit)
|
||||||
|
return equipments.name_get()
|
||||||
|
|
||||||
|
def action_view_equipment(self):
|
||||||
|
return {
|
||||||
|
'name': 'Equipment',
|
||||||
|
'view_type': 'form',
|
||||||
|
'view_mode': 'form',
|
||||||
|
'res_id': self.id,
|
||||||
|
'context': self.env.context,
|
||||||
|
'res_model': 'bemade_fsm.equipment',
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
}
|
||||||
7
bemade_fsm/models/product_template.py
Normal file
7
bemade_fsm/models/product_template.py
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
from odoo import fields, models, api
|
||||||
|
|
||||||
|
|
||||||
|
class ProductTemplate(models.Model):
|
||||||
|
_inherit = 'product.template'
|
||||||
|
|
||||||
|
task_template_id = fields.Many2one("project.task.template", string="Task Template", ondelete='restrict')
|
||||||
45
bemade_fsm/models/res_partner.py
Normal file
45
bemade_fsm/models/res_partner.py
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
from odoo import api, fields, models, Command
|
||||||
|
|
||||||
|
class Partner(models.Model):
|
||||||
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
|
equipment_count = fields.Integer(compute='_compute_equipment_count',
|
||||||
|
string='Equipment Count')
|
||||||
|
|
||||||
|
owned_equipment_ids = fields.One2many(comodel_name="bemade_fsm.equipment",
|
||||||
|
inverse_name="partner_id",
|
||||||
|
string="Owned Equipments")
|
||||||
|
|
||||||
|
|
||||||
|
equipment_ids = fields.One2many(comodel_name='bemade_fsm.equipment',
|
||||||
|
inverse_name='partner_location_id',
|
||||||
|
string='Site Equipment')
|
||||||
|
|
||||||
|
is_site_contact = fields.Boolean(string='Is a site contact',
|
||||||
|
compute="_compute_is_site_contact")
|
||||||
|
|
||||||
|
site_ids = fields.Many2many(string='Work Sites',
|
||||||
|
comodel_name='res.partner',
|
||||||
|
relation='res_partner_site_contact_rel',
|
||||||
|
column1='site_contact_id',
|
||||||
|
column2='site_id',
|
||||||
|
tracking=True)
|
||||||
|
|
||||||
|
site_contacts = fields.Many2many(string='Site Contacts',
|
||||||
|
comodel_name='res.partner',
|
||||||
|
relation='res_partner_site_contact_rel',
|
||||||
|
column1='site_id',
|
||||||
|
column2='site_contact_id',
|
||||||
|
domain=[('is_company', '=', False)],
|
||||||
|
tracking=True)
|
||||||
|
|
||||||
|
@api.depends('site_ids')
|
||||||
|
def _compute_is_site_contact(self):
|
||||||
|
for rec in self:
|
||||||
|
rec.is_site_contact = rec.site_ids is not False
|
||||||
|
|
||||||
|
@api.depends('equipment_ids')
|
||||||
|
def _compute_equipment_count(self):
|
||||||
|
for rec in self:
|
||||||
|
all_equipmemt_ids = self.env['bemade_fsm.equipment'].search([('partner_id', '=', rec.id)])
|
||||||
|
rec.equipment_count = len(all_equipmemt_ids)
|
||||||
61
bemade_fsm/models/sale_order.py
Normal file
61
bemade_fsm/models/sale_order.py
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
from odoo import fields, models, api, _, Command
|
||||||
|
|
||||||
|
|
||||||
|
class SaleOrderLine(models.Model):
|
||||||
|
_inherit = 'sale.order.line'
|
||||||
|
|
||||||
|
def _timesheet_create_task(self, project):
|
||||||
|
""" Generate task for the given so line, and link it.
|
||||||
|
:param project: record of project.project in which the task should be created
|
||||||
|
:return task: record of the created task
|
||||||
|
|
||||||
|
Override to add the logic needed to implement task templates."""
|
||||||
|
|
||||||
|
def _create_task_from_template(project, template, parent):
|
||||||
|
""" Recursively generates the task and any subtasks from a project.task.template.
|
||||||
|
|
||||||
|
:param project: project.project record to set on the task's project_id field.
|
||||||
|
:param template: project.task.template to use to create the task.
|
||||||
|
:param parent: project.task to set as the parent to this task.
|
||||||
|
"""
|
||||||
|
values = _timesheet_create_task_prepare_values_from_template(project, template, parent)
|
||||||
|
task = self.env['project.task'].sudo().create(values)
|
||||||
|
subtasks = []
|
||||||
|
for t in template.subtasks:
|
||||||
|
subtask = _create_task_from_template(project, t, task)
|
||||||
|
subtasks.append(subtask)
|
||||||
|
task.write({'child_ids': [Command.set([t.id for t in subtasks])]})
|
||||||
|
# We don't want to see the sub-tasks on the SO
|
||||||
|
task.child_ids.write({'sale_order_id': None, 'sale_line_id': None, })
|
||||||
|
return task
|
||||||
|
|
||||||
|
def _timesheet_create_task_prepare_values_from_template(project, template, parent):
|
||||||
|
""" Copies the values from a project.task.template over to the set of values used to create a project.task.
|
||||||
|
|
||||||
|
:param project: project.project record to set on the task's project_id field.
|
||||||
|
Pass the project.project model or an empty recordset to leave task project_id blank.
|
||||||
|
DO NOT pass False or None as this will cause an error in _timesheet_create_task_prepare_values(project).
|
||||||
|
:param template: project.task.template to use to create the task.
|
||||||
|
:param parent: project.task to set as the parent to this task.
|
||||||
|
"""
|
||||||
|
vals = self._timesheet_create_task_prepare_values(project)
|
||||||
|
vals['name'] = f"{vals['name']} ({template.name})" if not parent else template.name
|
||||||
|
vals['description'] = template.description or vals['description']
|
||||||
|
vals['parent_id'] = parent and parent.id
|
||||||
|
vals['user_ids'] = template.assignees.ids
|
||||||
|
vals['tag_ids'] = template.tags.ids
|
||||||
|
vals['planned_hours'] = template.planned_hours
|
||||||
|
return vals
|
||||||
|
|
||||||
|
tmpl = self.product_id.task_template_id
|
||||||
|
if not tmpl:
|
||||||
|
task = super()._timesheet_create_task(project)
|
||||||
|
else:
|
||||||
|
task = _create_task_from_template(project, tmpl, None)
|
||||||
|
self.write({'task_id': task.id})
|
||||||
|
# post message on task
|
||||||
|
task_msg = _(
|
||||||
|
"This task has been created from: <a href=# data-oe-model=sale.order data-oe-id=%d>%s</a> (%s)") % (
|
||||||
|
self.order_id.id, self.order_id.name, self.product_id.name)
|
||||||
|
task.message_post(body=task_msg)
|
||||||
|
return task
|
||||||
7
bemade_fsm/models/task.py
Normal file
7
bemade_fsm/models/task.py
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
from odoo import fields, models, api
|
||||||
|
|
||||||
|
|
||||||
|
class Task(models.Model):
|
||||||
|
_inherit = "project.task"
|
||||||
|
|
||||||
|
equipment_id = fields.Many2one("bemade_fsm.equipment", string="Target Equipment")
|
||||||
32
bemade_fsm/models/task_template.py
Normal file
32
bemade_fsm/models/task_template.py
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
|
||||||
|
|
||||||
|
class TaskTemplate(models.Model):
|
||||||
|
_name = 'project.task.template'
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _current_company(self):
|
||||||
|
return self.env.company
|
||||||
|
|
||||||
|
name = fields.Char(string="Task Title", required=True)
|
||||||
|
description = fields.Html(string="Description")
|
||||||
|
assignees = fields.Many2many("res.users", string="Default Assignees", help="Employees assigned to tasks created from this template.")
|
||||||
|
customer = fields.Many2one("res.partner", string="Default Customer", help="Default customer for tasks created from this template.")
|
||||||
|
project = fields.Many2one("project.project", string="Default Project", help="Default project for tasks created from this template.")
|
||||||
|
tags = fields.Many2many("project.tags", string="Default Tags", help="Default tags for tasks created from this template.")
|
||||||
|
parent = fields.Many2one("project.task.template", string="Parent Task Template", ondelete='cascade')
|
||||||
|
subtasks = fields.One2many("project.task.template", inverse_name="parent", string="Subtask Templates")
|
||||||
|
sequence = fields.Integer(string="Sequence")
|
||||||
|
company_id = fields.Many2one("res.company", string="Company", index=1, default=_current_company)
|
||||||
|
planned_hours = fields.Float("Initially Planned Hours")
|
||||||
|
|
||||||
|
def action_open_task(self):
|
||||||
|
return {
|
||||||
|
'view_mode': 'form',
|
||||||
|
'res_model': 'project.task.template',
|
||||||
|
'res_id': self.id,
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'context': self._context
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
6
bemade_fsm/security/ir.model.access.csv
Normal file
6
bemade_fsm/security/ir.model.access.csv
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_bemade_fsm_task_template,bemade_fsm_task_template,model_project_task_template,project.group_project_manager,1,1,1,1
|
||||||
|
access_bemade_fsm_task_template,bemade_fsm_task_template,model_project_task_template,project.group_project_user,1,1,1,1
|
||||||
|
access_bemade_fsm_equipment,bemade_fsm_equipment,model_bemade_fsm_equipment,base.group_user,1,1,1,1
|
||||||
|
access_bemade_fsm_equipment_tag,bemade_fsm_equipment_tag,model_bemade_fsm_equipment_tag,base.group_user,1,1,1,1
|
||||||
|
access_bemade_fsm_equipment_type,access_bemade_fsm_equipment_type,model_bemade_fsm_equipment_type,base.group_user,1,0,0,0
|
||||||
|
75
bemade_fsm/static/tests/tours/task_equipment_tour.js
Normal file
75
bemade_fsm/static/tests/tours/task_equipment_tour.js
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import tour from 'web_tour.tour';
|
||||||
|
const TEST_COMPANY = "Test Partner Company";
|
||||||
|
const TEST_EQPT1 = "Test Equipment 1";
|
||||||
|
const TEST_EQPT2 = "Test Equipment 2";
|
||||||
|
tour.register('task_equipment_tour', {
|
||||||
|
test: true,
|
||||||
|
url: '/web',
|
||||||
|
}, /* Create a new "Test Equipment" and link it to "Test Partner Company" */
|
||||||
|
[tour.stepUtils.showAppsMenuItem(), {
|
||||||
|
content: 'Navigate to the Service menu',
|
||||||
|
trigger: '.o_app[data-menu-xmlid="industry_fsm.fsm_menu_root"]',
|
||||||
|
}, {
|
||||||
|
content: 'Navigate to the Clients submenu',
|
||||||
|
trigger: 'button.dropdown-toggle[data-menu-xmlid="bemade_fsm.menu_service_client"]',
|
||||||
|
}, {
|
||||||
|
content: 'Navigate to the Equipment menu',
|
||||||
|
trigger: '.dropdown-item[data-menu-xmlid="bemade_fsm.menu_service_client_equipment"]',
|
||||||
|
}, {
|
||||||
|
content: 'Click the create button',
|
||||||
|
trigger: '.o_list_button_add',
|
||||||
|
extra_trigger: 'li.breadcrumb-item.active:has(span:contains(Equipment))',
|
||||||
|
}, {
|
||||||
|
content: 'Add a tag',
|
||||||
|
trigger: 'input[name="pid_tag"]',
|
||||||
|
run: 'text TestPIDTag',
|
||||||
|
}, {
|
||||||
|
content: 'Set the name',
|
||||||
|
trigger: 'input[name="name"]',
|
||||||
|
run: `text ${TEST_EQPT2}`,
|
||||||
|
}, {
|
||||||
|
content: 'Set the partner',
|
||||||
|
trigger: 'div[name="partner_location_id"] div div input',
|
||||||
|
run: 'text Test Partner Company',
|
||||||
|
}, {
|
||||||
|
content: 'Click the partner in the dropdown',
|
||||||
|
trigger: `li a.dropdown-item:contains(${TEST_COMPANY})`,
|
||||||
|
}, {
|
||||||
|
content: 'Save equipment',
|
||||||
|
trigger: 'button.o_form_button_save',
|
||||||
|
}, {
|
||||||
|
/* Navigate to the client and make sure that there are two equipments saved (one from the Python test case) */
|
||||||
|
content: 'Navigate to the Clients submenu',
|
||||||
|
trigger: 'button.dropdown-toggle[data-menu-xmlid="bemade_fsm.menu_service_client"]',
|
||||||
|
}, {
|
||||||
|
content: 'Click on the clients submenu',
|
||||||
|
trigger: '.dropdown-item[data-menu-xmlid="bemade_fsm.menu_service_client_clients"]',
|
||||||
|
}, {
|
||||||
|
content: 'Search for the Test Partner Company',
|
||||||
|
trigger: 'input.o_searchview_input',
|
||||||
|
extra_trigger: 'li.breadcrumb-item.active:has(span:contains(Customers))',
|
||||||
|
run: `text ${TEST_COMPANY}`
|
||||||
|
}, {
|
||||||
|
content: 'Validate Search',
|
||||||
|
trigger: '.o_menu_item.o_selection_focus',
|
||||||
|
run: 'click',
|
||||||
|
}, {
|
||||||
|
content: 'Open the test client.',
|
||||||
|
trigger: `div.o_kanban_record:has(span:contains(${TEST_COMPANY}))`,
|
||||||
|
}, {
|
||||||
|
content: 'Click the Field Service tab.',
|
||||||
|
trigger: 'a.nav-link:contains(Field Service)',
|
||||||
|
extra_trigger: `h1 span.o_field_partner_autocomplete[name="name"]:contains(${TEST_COMPANY})`,
|
||||||
|
}, {
|
||||||
|
content: 'Make sure we have a first test equipment',
|
||||||
|
/*trigger: `div[name="equipment_ids"]:has(td:contains(${TEST_EQPT1}))`,*/
|
||||||
|
trigger: `td:contains(${TEST_EQPT1})`,
|
||||||
|
run: function() {},
|
||||||
|
}, {
|
||||||
|
content: 'Make sure we have a second test equipment',
|
||||||
|
trigger: `div[name="equipment_ids"]:has(td:contains(${TEST_EQPT2}))`,
|
||||||
|
run: function() {},
|
||||||
|
}
|
||||||
|
])
|
||||||
47
bemade_fsm/static/tests/tours/task_template_tour.js
Normal file
47
bemade_fsm/static/tests/tours/task_template_tour.js
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
/** @odoo-module */
|
||||||
|
|
||||||
|
import tour from 'web_tour.tour';
|
||||||
|
|
||||||
|
tour.register('task_template_tour', {
|
||||||
|
test: true,
|
||||||
|
url: '/web',
|
||||||
|
}, /* Make sure the test task is visible via the Project > Task Templates path */
|
||||||
|
[tour.stepUtils.showAppsMenuItem(), {
|
||||||
|
trigger: '.o_app[data-menu-xmlid="project.menu_main_pm"]',
|
||||||
|
}, {
|
||||||
|
content: 'Open the task templates list view.',
|
||||||
|
trigger: '.o_nav_entry[data-menu-xmlid="bemade_fsm.project_task_template_menu"]',
|
||||||
|
}, {
|
||||||
|
content: 'Open the existing task template.',
|
||||||
|
trigger: '.o_data_cell:contains("Template 1"):parent()',
|
||||||
|
}, {
|
||||||
|
content: 'Confirm that the header contains the task title.',
|
||||||
|
trigger: 'span[name="name"]:contains("Template 1")',
|
||||||
|
}, /* Make sure we can see the task template from the product */
|
||||||
|
tour.stepUtils.toggleHomeMenu(),
|
||||||
|
{
|
||||||
|
content: 'Open the sales menu.',
|
||||||
|
trigger: '.o_app[data-menu-xmlid="sale.sale_menu_root"]',
|
||||||
|
}, {
|
||||||
|
content: 'Open the product dropdown menu.',
|
||||||
|
trigger: 'button[data-menu-xmlid="sale.product_menu_catalog"]',
|
||||||
|
}, {
|
||||||
|
content: 'Click the products menu item.',
|
||||||
|
trigger: 'a.dropdown-item[data-menu-xmlid="sale.menu_product_template_action"]',
|
||||||
|
}, {
|
||||||
|
content: 'Search for the test product.',
|
||||||
|
trigger: 'input.o_searchview_input',
|
||||||
|
extra_trigger: 'li.breadcrumb-item.active:has(span:contains(Products))',
|
||||||
|
run: 'text Test Product 1',
|
||||||
|
}, {
|
||||||
|
trigger: '.o_menu_item.o_selection_focus',
|
||||||
|
content: 'Validate search',
|
||||||
|
run: 'click',
|
||||||
|
}, {
|
||||||
|
content: 'Open the test product.',
|
||||||
|
trigger: 'div.o_kanban_record:has(span:contains(Test Product 1))',
|
||||||
|
}, {
|
||||||
|
content: 'Ensure the product_template_id field is displayed with the "Template 1" mention.',
|
||||||
|
trigger: 'a.o_quick_editable[name="task_template_id"]:first-child:contains("Template 1")',
|
||||||
|
},
|
||||||
|
])
|
||||||
4
bemade_fsm/tests/__init__.py
Normal file
4
bemade_fsm/tests/__init__.py
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
from . import test_bemade_fsm_common
|
||||||
|
from . import test_task_template
|
||||||
|
from . import test_task_template_sale_order
|
||||||
|
from . import test_equipment
|
||||||
37
bemade_fsm/tests/test_bemade_fsm_common.py
Normal file
37
bemade_fsm/tests/test_bemade_fsm_common.py
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
from odoo.tests.common import TransactionCase
|
||||||
|
from odoo import Command
|
||||||
|
|
||||||
|
class FSMManagerUserTransactionCase(TransactionCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
|
user_group_employee = cls.env.ref('base.group_user')
|
||||||
|
user_group_project_user = cls.env.ref('project.group_project_user')
|
||||||
|
user_group_project_manager = cls.env.ref('project.group_project_manager')
|
||||||
|
user_group_fsm_user = cls.env.ref('industry_fsm.group_fsm_user')
|
||||||
|
user_group_fsm_manager = cls.env.ref('industry_fsm.group_fsm_manager')
|
||||||
|
user_group_sales_manager = cls.env.ref('sales_team.group_sale_manager')
|
||||||
|
user_group_sales_user = cls.env.ref('sales_team.group_sale_salesman')
|
||||||
|
user_product_customer = cls.env.ref('customer_product_code.group_product_customer_code_user')
|
||||||
|
|
||||||
|
group_ids = [user_group_employee.id,
|
||||||
|
user_group_project_user.id,
|
||||||
|
user_group_project_manager.id,
|
||||||
|
user_group_fsm_user.id,
|
||||||
|
user_group_fsm_manager.id,
|
||||||
|
user_group_sales_user.id,
|
||||||
|
user_group_sales_manager.id, ]
|
||||||
|
if user_product_customer:
|
||||||
|
group_ids.append(user_product_customer.id)
|
||||||
|
|
||||||
|
# Test user with project access rights for the various tests
|
||||||
|
Users = cls.env['res.users'].with_context({'no_reset_password': True})
|
||||||
|
cls.user = Users.create({
|
||||||
|
'name': 'Project Manager',
|
||||||
|
'login': 'misterpm',
|
||||||
|
'password': 'misterpm',
|
||||||
|
'email': 'mrpm@testco.com',
|
||||||
|
'signature': 'Mr. PM',
|
||||||
|
'groups_id': [Command.set(group_ids)],
|
||||||
|
})
|
||||||
37
bemade_fsm/tests/test_equipment.py
Normal file
37
bemade_fsm/tests/test_equipment.py
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
from odoo.tests.common import HttpCase, tagged
|
||||||
|
from .test_bemade_fsm_common import FSMManagerUserTransactionCase
|
||||||
|
|
||||||
|
class TestEquipmentCommon(FSMManagerUserTransactionCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
|
|
||||||
|
# Set up the test partner
|
||||||
|
cls.partner_company = cls.env['res.partner'].create({
|
||||||
|
'name': 'Test Partner Company',
|
||||||
|
'company_type': 'company',
|
||||||
|
'street': '123 Street St.',
|
||||||
|
'city': 'Montreal',
|
||||||
|
'state_id': cls.env['res.country.state'].search([('name','ilike','Quebec%')]).id,
|
||||||
|
'country_id': cls.env['res.country'].search([('name','=','Canada')]).id
|
||||||
|
})
|
||||||
|
|
||||||
|
cls.partner_contact = cls.env['res.partner'].create({
|
||||||
|
'name': 'Site Contact',
|
||||||
|
'company_type': 'person',
|
||||||
|
'parent_id': cls.partner_company.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
cls.equipment = cls.env['bemade_fsm.equipment'].create({
|
||||||
|
'name': 'Test Equipment 1',
|
||||||
|
'partner_location_id': cls.partner_company.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@tagged('-at_install', 'post_install')
|
||||||
|
class TestEquipmentTour(HttpCase, TestEquipmentCommon):
|
||||||
|
|
||||||
|
def test_equipment_tour(self):
|
||||||
|
self.start_tour('/web', 'task_equipment_tour',
|
||||||
|
login=self.user.login, )
|
||||||
30
bemade_fsm/tests/test_fsm_contact_setting.py
Normal file
30
bemade_fsm/tests/test_fsm_contact_setting.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
from odoo.tests import TransactionCase, HttpCase, tagged
|
||||||
|
from odoo import Command
|
||||||
|
from test_bemade_fsm_common import FSMManagerUserTransactionCase
|
||||||
|
|
||||||
|
class SaleOrderFSMContactsCase(FSMManagerUserTransactionCase):
|
||||||
|
|
||||||
|
def test_default_site_contacts(self):
|
||||||
|
# Make sure the SO pulls the defaults from the partner correctly
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_default_billing_contacts(self):
|
||||||
|
# Make sure the SO pulls the defaults from the partner correctly
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_default_workorder_contacts(self):
|
||||||
|
# Make sure the SO pulls the defaults from the partner correctly
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_change_site_contacts(self):
|
||||||
|
# Make sure the SO contacts can be updated without feeding back to the partner
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_change_workorder_contacts(self):
|
||||||
|
# Make sure the SO contacts can be updated without feeding back to the partner
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_change_billing_contacts(self):
|
||||||
|
# Make sure the SO contacts can be updated without feedin back to the partner
|
||||||
|
pass
|
||||||
|
|
||||||
108
bemade_fsm/tests/test_task_template.py
Normal file
108
bemade_fsm/tests/test_task_template.py
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
from .test_bemade_fsm_common import FSMManagerUserTransactionCase
|
||||||
|
from odoo.tests.common import HttpCase, tagged
|
||||||
|
from odoo.exceptions import MissingError
|
||||||
|
from odoo import Command
|
||||||
|
from psycopg2.errors import ForeignKeyViolation
|
||||||
|
|
||||||
|
|
||||||
|
class TestTaskTemplateCommon(FSMManagerUserTransactionCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
|
cls.PLANNED_HOURS = 6
|
||||||
|
hours_uom = cls.env['uom.uom'].search([('name', '=', 'Hour')]) or False
|
||||||
|
# Test product to use with the various tests
|
||||||
|
cls.task1 = cls.env['project.task.template'].create({
|
||||||
|
'name': 'Template 1',
|
||||||
|
})
|
||||||
|
|
||||||
|
cls.project = cls.env['project.project'].create({
|
||||||
|
'name': 'Test Project',
|
||||||
|
})
|
||||||
|
cls.product_task_global_project = cls.env['product.product'].create({
|
||||||
|
'name': 'Test Product 1',
|
||||||
|
'type': 'service',
|
||||||
|
'service_tracking': 'task_global_project',
|
||||||
|
'project_id': cls.project.id,
|
||||||
|
'task_template_id': cls.task1.id,
|
||||||
|
'uom_id': hours_uom.id,
|
||||||
|
'uom_po_id': hours_uom.id,
|
||||||
|
})
|
||||||
|
cls.project_template = cls.env['project.project'].create({
|
||||||
|
'name': 'Test Project Template',
|
||||||
|
})
|
||||||
|
cls.product_task_in_project = cls.env['product.product'].create({
|
||||||
|
'name': 'Test Product 2',
|
||||||
|
'type': 'service',
|
||||||
|
'service_tracking': 'task_in_project',
|
||||||
|
'task_template_id': cls.task1.id,
|
||||||
|
'project_template_id': cls.project_template.id,
|
||||||
|
'uom_po_id': hours_uom.id,
|
||||||
|
'uom_id': hours_uom.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Set up a task template tree with 2 children and 1 grandchild
|
||||||
|
cls.parent_task = cls.env['project.task.template'].create({
|
||||||
|
'name': 'Parent Template',
|
||||||
|
'planned_hours': cls.PLANNED_HOURS,
|
||||||
|
})
|
||||||
|
cls.child_task_1 = cls.env['project.task.template'].create({
|
||||||
|
'name': 'Child Template 1',
|
||||||
|
'parent': cls.parent_task.id,
|
||||||
|
})
|
||||||
|
cls.child_task_2 = cls.env['project.task.template'].create({
|
||||||
|
'name': 'Child Template 2',
|
||||||
|
'parent': cls.parent_task.id,
|
||||||
|
})
|
||||||
|
cls.parent_task.write({'subtasks': [Command.set([cls.child_task_1.id, cls.child_task_2.id])]})
|
||||||
|
cls.grandchild_task = cls.env['project.task.template'].create({
|
||||||
|
'name': 'Grandchild Template',
|
||||||
|
'parent': cls.child_task_2.id
|
||||||
|
})
|
||||||
|
cls.child_task_2.write({'subtasks': [Command.set([cls.grandchild_task.id])]})
|
||||||
|
|
||||||
|
# Create products using the task tree we just created
|
||||||
|
cls.product_task_tree_global_project = cls.env['product.product'].create({
|
||||||
|
'name': 'Test Product 3',
|
||||||
|
'type': 'service',
|
||||||
|
'service_tracking': 'task_global_project',
|
||||||
|
'project_id': cls.project.id,
|
||||||
|
'task_template_id': cls.parent_task.id,
|
||||||
|
'uom_id': hours_uom.id,
|
||||||
|
'uom_po_id': hours_uom.id,
|
||||||
|
})
|
||||||
|
cls.product_task_tree_in_project = cls.env['product.product'].create({
|
||||||
|
'name': 'Test Product 2',
|
||||||
|
'type': 'service',
|
||||||
|
'service_tracking': 'task_in_project',
|
||||||
|
'task_template_id': cls.parent_task.id,
|
||||||
|
'project_template_id': cls.project_template.id,
|
||||||
|
'uom_po_id': hours_uom.id,
|
||||||
|
'uom_id': hours_uom.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@tagged('-at_install', 'post_install')
|
||||||
|
class TestTaskTemplate(TestTaskTemplateCommon):
|
||||||
|
|
||||||
|
def test_delete_task_template(self):
|
||||||
|
"""User should never be able to delete a task template used on a product"""
|
||||||
|
with self.assertRaises(ForeignKeyViolation):
|
||||||
|
self.task1.unlink()
|
||||||
|
|
||||||
|
def test_delete_subtask_template(self):
|
||||||
|
""" Deletion of a child task should be OK even if the parent is on a product. Children of the deleted
|
||||||
|
subtask should be deleted."""
|
||||||
|
self.child_task_2.unlink()
|
||||||
|
# Reading deleted child's name field should be impossible
|
||||||
|
with self.assertRaises(MissingError):
|
||||||
|
test = self.grandchild_task.name
|
||||||
|
|
||||||
|
|
||||||
|
@tagged('-at_install', 'post_install')
|
||||||
|
class TestTaskTemplateTour(HttpCase, TestTaskTemplateCommon):
|
||||||
|
|
||||||
|
def test_task_template_tour(self):
|
||||||
|
self.start_tour('/web', 'task_template_tour',
|
||||||
|
login='misterpm', )
|
||||||
86
bemade_fsm/tests/test_task_template_sale_order.py
Normal file
86
bemade_fsm/tests/test_task_template_sale_order.py
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
from .test_task_template import TestTaskTemplateCommon
|
||||||
|
from odoo.tests.common import tagged
|
||||||
|
|
||||||
|
|
||||||
|
class TestTaskTemplateSalesOrder(TestTaskTemplateCommon):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
|
cls.partner = cls.env['res.partner'].create({
|
||||||
|
'name': 'Test Partner',
|
||||||
|
})
|
||||||
|
cls.sale_order1 = cls.env['sale.order'].create({
|
||||||
|
'partner_id': cls.partner.id,
|
||||||
|
'client_order_ref': 'TEST ORDER',
|
||||||
|
})
|
||||||
|
cls.sol_serv_order = cls.env['sale.order.line'].create({
|
||||||
|
'name': cls.product_task_global_project.name,
|
||||||
|
'product_id': cls.product_task_global_project.id,
|
||||||
|
'product_uom_qty': 1,
|
||||||
|
'product_uom': cls.product_task_global_project.uom_id.id,
|
||||||
|
'price_unit': 120.0,
|
||||||
|
'order_id': cls.sale_order1.id,
|
||||||
|
'tax_id': False,
|
||||||
|
})
|
||||||
|
cls.sol_serv_order_task_in_project = cls.env['sale.order.line'].create({
|
||||||
|
'name': cls.product_task_in_project.name,
|
||||||
|
'product_id': cls.product_task_in_project.id,
|
||||||
|
'product_uom_qty': 1,
|
||||||
|
'product_uom': cls.product_task_in_project.uom_id.id,
|
||||||
|
'price_unit': 150.0,
|
||||||
|
'order_id': cls.sale_order1.id,
|
||||||
|
'tax_id': False,
|
||||||
|
})
|
||||||
|
cls.sale_order2 = cls.env['sale.order'].create({
|
||||||
|
'partner_id': cls.partner.id,
|
||||||
|
'client_order_ref': 'TEST ORDER',
|
||||||
|
})
|
||||||
|
cls.sol_tree_order = cls.env['sale.order.line'].create({
|
||||||
|
'name': cls.product_task_tree_global_project.name,
|
||||||
|
'product_id': cls.product_task_tree_global_project.id,
|
||||||
|
'product_uom_qty': 1,
|
||||||
|
'product_uom': cls.product_task_tree_global_project.uom_id.id,
|
||||||
|
'price_unit': 120.0,
|
||||||
|
'order_id': cls.sale_order2.id,
|
||||||
|
'tax_id': False,
|
||||||
|
})
|
||||||
|
cls.sol_serv_order_task_in_project = cls.env['sale.order.line'].create({
|
||||||
|
'name': cls.product_task_tree_in_project.name,
|
||||||
|
'product_id': cls.product_task_tree_in_project.id,
|
||||||
|
'product_uom_qty': 1,
|
||||||
|
'product_uom': cls.product_task_tree_in_project.uom_id.id,
|
||||||
|
'price_unit': 150.0,
|
||||||
|
'order_id': cls.sale_order2.id,
|
||||||
|
'tax_id': False,
|
||||||
|
})
|
||||||
|
|
||||||
|
@tagged('-at_install', 'post_install')
|
||||||
|
def test_order_confirmation_simple_template(self):
|
||||||
|
""" Confirming the order should create a task in the global project. """
|
||||||
|
so = self.sale_order1
|
||||||
|
so.action_confirm()
|
||||||
|
sol1 = so.order_line[0]
|
||||||
|
sol2 = so.order_line[1]
|
||||||
|
task1 = sol1.task_id
|
||||||
|
task2 = sol2.task_id
|
||||||
|
self.assertTrue(task1)
|
||||||
|
self.assertTrue(task2)
|
||||||
|
self.assertTrue(self.task1.name in task1.name)
|
||||||
|
self.assertTrue(self.task1.name in task2.name)
|
||||||
|
self.assertTrue(self.task1.planned_hours == task1.planned_hours)
|
||||||
|
|
||||||
|
def test_order_confirmation_tree_template(self):
|
||||||
|
def assert_structure(sol):
|
||||||
|
self.assertTrue(sol.task_id.child_ids and len(sol.task_id.child_ids) == 2)
|
||||||
|
self.assertTrue(self.parent_task.name in sol.task_id.name)
|
||||||
|
self.assertTrue(self.child_task_1.name in sol.task_id.child_ids[0].name)
|
||||||
|
self.assertTrue(self.child_task_2.name in sol.task_id.child_ids[1].name)
|
||||||
|
self.assertTrue(sol.task_id.child_ids[1].child_ids and len(sol.task_id.child_ids.child_ids) == 1)
|
||||||
|
self.assertTrue(self.grandchild_task.name in sol.task_id.child_ids.child_ids[0].name)
|
||||||
|
so = self.sale_order2
|
||||||
|
so.action_confirm()
|
||||||
|
sol1 = so.order_line[0]
|
||||||
|
sol2 = so.order_line[1]
|
||||||
|
assert_structure(sol1)
|
||||||
|
assert_structure(sol2)
|
||||||
|
|
||||||
95
bemade_fsm/views/equipment.xml
Normal file
95
bemade_fsm/views/equipment.xml
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<!-- Equipment Form View -->
|
||||||
|
<record id="equipment_view_form" model="ir.ui.view">
|
||||||
|
<field name="name">bemade_fsm.equipment.form</field>
|
||||||
|
<field name="model">bemade_fsm.equipment</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Equipment">
|
||||||
|
<sheet>
|
||||||
|
<group>
|
||||||
|
<group name="left">
|
||||||
|
<field name="pid_tag"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="description"/>
|
||||||
|
<field name="location_notes"/>
|
||||||
|
</group>
|
||||||
|
<group name="right">
|
||||||
|
<field name="partner_id"/>
|
||||||
|
<field name="partner_location_id"
|
||||||
|
groups="sale.group_delivery_invoice_address"
|
||||||
|
context="{'default_type': 'delivery', 'show_address': 1}"
|
||||||
|
options='{"always_reload": True}'/>
|
||||||
|
<field name="tag_ids" widget="many2many_tags" options="{'no_open': False}"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<!-- <notebook>-->
|
||||||
|
<!-- <page string="Interventions">-->
|
||||||
|
<!-- <field name="intervention_ids" mode="tree">-->
|
||||||
|
<!-- <tree string="Interventions" create="0" edit="0">-->
|
||||||
|
<!-- <field name="name"/>-->
|
||||||
|
<!-- <field name="description"/>-->
|
||||||
|
<!-- <field name="date_planned"/>-->
|
||||||
|
<!-- <field name="state"/>-->
|
||||||
|
<!-- </tree>-->
|
||||||
|
<!-- </field>-->
|
||||||
|
<!-- </page>-->
|
||||||
|
<!-- </notebook>-->
|
||||||
|
|
||||||
|
</sheet>
|
||||||
|
<div class="oe_chatter">
|
||||||
|
<field name="message_follower_ids" widget="mail_followers"/>
|
||||||
|
<field name="activity_ids" widget="mail_activity"/>
|
||||||
|
<field name="message_ids" widget="mail_thread"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="equipment_view_tree" model="ir.ui.view">
|
||||||
|
<field name="name">bemade_fsm.equipment.tree</field>
|
||||||
|
<field name="model">bemade_fsm.equipment</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="Equipment">
|
||||||
|
<field name="pid_tag"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="description"/>
|
||||||
|
<field name="tag_ids" widget="many2many_tags" options="{'no_open': False}"/>
|
||||||
|
<field name="partner_id"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- BV: AN OTHER SEARCH VIEW TO BRING BACK
|
||||||
|
<record id="equipment_view_search" model="ir.ui.view">
|
||||||
|
<field name="name">bemade_fsm.equipment.search</field>
|
||||||
|
<field name="model">bemade_fsm.equipment</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="Equipments">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="partner_id"/>
|
||||||
|
<field name="pid_tag"/>
|
||||||
|
<field name="partner_location_id"/>
|
||||||
|
|
||||||
|
<group expand="0" string="Group By">
|
||||||
|
<filter string="Partner" context="{'group_by':'partner_id'}"/>
|
||||||
|
<filter string="Location" context="{'group_by':'partner_location_id'}"/>
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<record model="ir.actions.act_window" id="action_window_equipment">
|
||||||
|
<field name="name">Equipment</field>
|
||||||
|
<field name="res_model">bemade_fsm.equipment</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.actions.act_window" id="action_window_equipment_tags">
|
||||||
|
<field name="name">Equipment Tag</field>
|
||||||
|
<field name="res_model">bemade_fsm.equipment.tag</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
32
bemade_fsm/views/menus.xml
Normal file
32
bemade_fsm/views/menus.xml
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<menuitem id="project_task_template_menu"
|
||||||
|
name="Task Templates"
|
||||||
|
parent="project.menu_main_pm"
|
||||||
|
action="task_template_act_window"
|
||||||
|
groups="project.group_project_manager,project.group_project_user"/>
|
||||||
|
<menuitem id="service_task_template_meny"
|
||||||
|
name="Task Templates"
|
||||||
|
action="task_template_act_window"
|
||||||
|
parent="industry_fsm.fsm_menu_root"
|
||||||
|
groups="project.group_project_manager,industry_fsm.group_fsm_manager"/>
|
||||||
|
<menuitem id="menu_service_client"
|
||||||
|
name="Clients"
|
||||||
|
sequence="10"
|
||||||
|
parent="industry_fsm.fsm_menu_root"
|
||||||
|
groups="industry_fsm.group_fsm_user"/>
|
||||||
|
<menuitem id="menu_service_client_clients"
|
||||||
|
name="Clients"
|
||||||
|
action="base.action_partner_customer_form"
|
||||||
|
sequence="20"
|
||||||
|
parent="menu_service_client"
|
||||||
|
groups="industry_fsm.group_fsm_user"/>
|
||||||
|
<menuitem id="menu_service_client_equipment"
|
||||||
|
name="Client Equipment"
|
||||||
|
action="action_window_equipment"
|
||||||
|
sequence="21"
|
||||||
|
parent="menu_service_client"
|
||||||
|
groups="industry_fsm.group_fsm_user"/>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
27
bemade_fsm/views/product_views.xml
Normal file
27
bemade_fsm/views/product_views.xml
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<record id="product_template_form_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">bemade_fsm.product.template.form</field>
|
||||||
|
<field name="model">product.template</field>
|
||||||
|
<field name="inherit_id" ref="sale_project.product_template_form_view_invoice_policy_inherit_sale_project"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='project_id']" position="after">
|
||||||
|
<field name="task_template_id"
|
||||||
|
attrs="{'invisible': [('service_tracking', 'not in', ('task_global_project', 'task_in_project'))]}"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="product_search_form_view_inherit_bemade_fsm" model="ir.ui.view">
|
||||||
|
<field name="name">bemade_fsm.product_search_form_view_inherit_bemade_fsm</field>
|
||||||
|
<field name="model">product.product</field>
|
||||||
|
<field name="inherit_id" ref="industry_fsm_sale.product_search_form_view_inherit_fsm_sale"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//searchpanel//field[@name='categ_id']" position="attributes">
|
||||||
|
<attribute name="limit">0</attribute>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
70
bemade_fsm/views/res_partner.xml
Normal file
70
bemade_fsm/views/res_partner.xml
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="act_res_partner_2_equipment" model="ir.actions.act_window">
|
||||||
|
<field name="name">Equipments</field>
|
||||||
|
<field name="res_model">bemade_fsm.equipment</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="context">{'default_partner_id': active_id}</field>
|
||||||
|
<field name="domain">[("partner_id", "=", active_id)]</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="partner_equipment_location_view_form" model="ir.ui.view">
|
||||||
|
<field name="name">bemade_fsm.partner.equipment.location.form</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//page[@name='internal_notes']" position="before">
|
||||||
|
<page name="field_service" string="Field Service">
|
||||||
|
<field name="owned_equipment_ids" invisible="True"/>
|
||||||
|
<group>
|
||||||
|
<field name="site_contacts" attrs="{'invisible': [('company_type','=','person')]}">
|
||||||
|
<tree editable="bottom">
|
||||||
|
<field name="name" widget="res_partner_many2one"/>
|
||||||
|
<field name="email" widget="email"/>
|
||||||
|
<field name="phone" widget="phone"/>
|
||||||
|
<field name="mobile" widget="phone"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
<field name="equipment_ids" attrs="{'invisible': [('company_type','=','person')]}">
|
||||||
|
<tree editable="bottom">
|
||||||
|
<field name="pid_tag"/>
|
||||||
|
<field name="name" />
|
||||||
|
<field name="location_notes"/>
|
||||||
|
<button name="action_view_equipment"
|
||||||
|
type="object"
|
||||||
|
string="Details"
|
||||||
|
icon="fa-external-link"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
<field attrs="{'invisible': ['|', ('company_type','=','person'), ('owned_equipment_ids','=',False)]}"
|
||||||
|
name="owned_equipment_ids">
|
||||||
|
<tree editable="False">
|
||||||
|
<field name="pid_tag"/>
|
||||||
|
<field name="name" />
|
||||||
|
<field name="partner_location_id" widget="res_partner_many2one"/>
|
||||||
|
<field name="location_notes"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
<field name="site_ids"
|
||||||
|
attrs="{'invisible': [('company_type','=','company')]}">
|
||||||
|
<tree editable="bottom">
|
||||||
|
<field name="name" widget="res_partner_many2one" />
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
<div name="button_box" position="inside">
|
||||||
|
<button class="oe_stat_button" type="action" name="%(bemade_fsm.act_res_partner_2_equipment)d"
|
||||||
|
icon="fa-tachometer">
|
||||||
|
<field string="Equipments" name="equipment_count" widget="statinfo"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<field name="lang" position="after">
|
||||||
|
</field>
|
||||||
|
<xpath expr="/form//field[@name='child_ids']/form//field[@name='comment']" position="after">
|
||||||
|
<field name="is_site_contact"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
102
bemade_fsm/views/task_template_views.xml
Normal file
102
bemade_fsm/views/task_template_views.xml
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<record id="task_template_form_view" model="ir.ui.view">
|
||||||
|
<field name="name">bemade_fsm.task_template.form</field>
|
||||||
|
<field name="model">project.task.template</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Task Template">
|
||||||
|
<sheet>
|
||||||
|
<div class="oe_title">
|
||||||
|
<label for="name"/>
|
||||||
|
<h1>
|
||||||
|
<field name="name" placeholder="Title"/>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="project"/>
|
||||||
|
<field name="assignees" widget="many2many_avatar_user"/>
|
||||||
|
<field name="parent"/>
|
||||||
|
<field name="planned_hours"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="customer"/>
|
||||||
|
<field name="tags" widget="many2many_tags"/>
|
||||||
|
<field name="company_id" />
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<notebook>
|
||||||
|
<page name="description_page" string="Description">
|
||||||
|
<field name="description" type="html" options="{'collaborative': true}"/>
|
||||||
|
</page>
|
||||||
|
<page name="subtasks_page" string="Subtasks">
|
||||||
|
<field name="subtasks">
|
||||||
|
<tree editable="bottom">
|
||||||
|
<field name="sequence" widget="handle"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="customer"/>
|
||||||
|
<field name="assignees" widget="many2many_avatar_user"/>
|
||||||
|
<button name="action_open_task" type="object" title="View Task"
|
||||||
|
string="View Task" class="btn btn-link pull-right"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="task_template_tree_view" model="ir.ui.view">
|
||||||
|
<field name="name">project.task_template.tree</field>
|
||||||
|
<field name="model">project.task.template</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="Task Template">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="assignees" widget="many2many_avatar_user"/>
|
||||||
|
<field name="project"/>
|
||||||
|
<field name="parent"/>
|
||||||
|
<field name="planned_hours"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="task_template_search_view" model="ir.ui.view">
|
||||||
|
<field name="name">project.task_template.search</field>
|
||||||
|
<field name="model">project.task.template</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="Task Template">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="project"/>
|
||||||
|
<field name="assignees"/>
|
||||||
|
<field name="parent"/>
|
||||||
|
<field name="subtasks"/>
|
||||||
|
<field name="planned_hours"/>
|
||||||
|
<group expand="1" string="Group By">
|
||||||
|
<filter string="Project" name="groupby_project" domain="[]"
|
||||||
|
context="{'group_by':'project'}"/>
|
||||||
|
<filter string="Parent Task" name="groupby_parent" domain="[]"
|
||||||
|
context="{'group_by':'parent'}"/>
|
||||||
|
<filter string="Customer" name="groupby_customer" domain="[]"
|
||||||
|
context="{'group_by':'customer'}"/>
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="task_template_act_window" model="ir.actions.act_window">
|
||||||
|
<field name="name">Task Template</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">project.task.template</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="oe_view_nocontent_create">
|
||||||
|
There are no task templates, click above to create one.
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
16
bemade_fsm/views/task_views.xml
Normal file
16
bemade_fsm/views/task_views.xml
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="bemade_fsm_project_task_form_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">bemade_fsm.project_task.form</field>
|
||||||
|
<field name="model">project.task</field>
|
||||||
|
<field name="inherit_id" ref="project.view_task_form2"/>
|
||||||
|
<field name="priority" eval="8"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='partner_id']" position="after">
|
||||||
|
<field name="equipment_id" domain="[('partner_location_id','=',partner_id)]"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
Loading…
Reference in a new issue