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