[MIG] fsm_equipment to 18.0
This commit is contained in:
parent
bb8c1f2f35
commit
c7d0a1be2b
11 changed files with 537 additions and 0 deletions
1
fsm_equipment/__init__.py
Normal file
1
fsm_equipment/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import models
|
||||
38
fsm_equipment/__manifest__.py
Normal file
38
fsm_equipment/__manifest__.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#
|
||||
# Bemade Inc.
|
||||
#
|
||||
# Copyright (C) 2023-June Bemade Inc. (<https://www.bemade.org>).
|
||||
# Author: Marc Durepos (Contact : marc@bemade.org)
|
||||
#
|
||||
# This program is under the terms of the GNU Lesser General Public License,
|
||||
# version 3.
|
||||
#
|
||||
# For full license details, see https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
#
|
||||
# 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": "FSM Equipment",
|
||||
"version": "18.0.0.2.0",
|
||||
"summary": "Add the notion of client equipment for Field Service",
|
||||
"category": "Services/Field Service",
|
||||
"author": "Bemade Inc.",
|
||||
"website": "http://www.bemade.org",
|
||||
"license": "LGPL-3",
|
||||
"depends": ["industry_fsm", "account", "contacts", "incrementing_sequence_mixin"],
|
||||
"data": [
|
||||
"security/ir.model.access.csv",
|
||||
"views/equipment_views.xml",
|
||||
"views/res_partner_views.xml",
|
||||
"views/project_task_views.xml",
|
||||
],
|
||||
"assets": {},
|
||||
"installable": True,
|
||||
"auto_install": False,
|
||||
}
|
||||
4
fsm_equipment/models/__init__.py
Normal file
4
fsm_equipment/models/__init__.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
from . import equipment_tag
|
||||
from . import equipment
|
||||
from . import res_partner
|
||||
from . import task
|
||||
144
fsm_equipment/models/equipment.py
Normal file
144
fsm_equipment/models/equipment.py
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
from odoo import models, fields, api, _
|
||||
from odoo.osv import expression
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class Equipment(models.Model):
|
||||
_name = "fsm.equipment"
|
||||
_description = "Partner-Owned Equipment"
|
||||
_inherit = ["mail.thread", "mail.activity.mixin", "incrementing.sequence.mixin"]
|
||||
_sequence_group = "parent_id"
|
||||
|
||||
code = fields.Char(
|
||||
tracking=True,
|
||||
)
|
||||
name = fields.Char(
|
||||
tracking=True,
|
||||
required=True,
|
||||
)
|
||||
|
||||
tag_ids = fields.Many2many(
|
||||
comodel_name="fsm.equipment.tag",
|
||||
string="Tags",
|
||||
)
|
||||
|
||||
description = fields.Text(tracking=True)
|
||||
|
||||
partner_id = fields.Many2one(
|
||||
comodel_name="res.partner",
|
||||
string="Physical Address",
|
||||
tracking=True,
|
||||
ondelete="restrict",
|
||||
required=False,
|
||||
)
|
||||
|
||||
location_notes = fields.Text(
|
||||
string="Physical Location Notes",
|
||||
tracking=True,
|
||||
help="Any useful information about physical location "
|
||||
"(door codes, directions, etc.).",
|
||||
)
|
||||
|
||||
task_ids = fields.Many2many(
|
||||
comodel_name="project.task",
|
||||
relation="fsm_task_equipment_rel",
|
||||
column1="equipment_id",
|
||||
column2="task_id",
|
||||
string="Interventions",
|
||||
)
|
||||
|
||||
active = fields.Boolean(
|
||||
default=True,
|
||||
tracking=True,
|
||||
)
|
||||
|
||||
parent_id = fields.Many2one(
|
||||
"fsm.equipment",
|
||||
tracking=True,
|
||||
)
|
||||
inherited_partner_id = fields.Many2one(
|
||||
comodel_name="res.partner",
|
||||
string="Main Equipment Location",
|
||||
readonly=True,
|
||||
compute="_compute_inherited_partner_id",
|
||||
recursive=True,
|
||||
)
|
||||
|
||||
child_ids = fields.One2many(
|
||||
"fsm.equipment",
|
||||
inverse_name="parent_id",
|
||||
string="Components",
|
||||
tracking=True,
|
||||
)
|
||||
|
||||
product_id = fields.Many2one(
|
||||
"product.product",
|
||||
ondelete="restrict",
|
||||
help="The product that represents this equipment, if any.",
|
||||
)
|
||||
|
||||
@api.depends("parent_id", "parent_id.partner_id")
|
||||
def _compute_inherited_partner_id(self):
|
||||
for rec in self:
|
||||
if not rec.parent_id:
|
||||
rec.inherited_partner_id = False
|
||||
continue
|
||||
rec.inherited_partner_id = (
|
||||
rec.parent_id.partner_id or rec.parent_id.inherited_partner_id
|
||||
)
|
||||
|
||||
@api.constrains("product_id", "partner_id")
|
||||
def _constrain_only_root_has_partner(self):
|
||||
for rec in self:
|
||||
if rec.partner_id and rec.parent_id:
|
||||
raise ValidationError(
|
||||
_("Only top-level (root) equipments can be linked to a partner.")
|
||||
)
|
||||
if not rec.parent_id and not rec.partner_id:
|
||||
raise ValidationError(
|
||||
_("Top-level (root) equipments must be linked to a partner.")
|
||||
)
|
||||
|
||||
@api.model
|
||||
def name_search(self, name="", args=None, operator="ilike", limit=100):
|
||||
|
||||
args = args or []
|
||||
domain = expression.AND(
|
||||
[
|
||||
args,
|
||||
[
|
||||
"|",
|
||||
"|",
|
||||
("code", operator, name),
|
||||
("name", operator, name),
|
||||
("partner_id.name", operator, name),
|
||||
],
|
||||
]
|
||||
)
|
||||
|
||||
if name:
|
||||
equipments = self.search(
|
||||
domain,
|
||||
limit=limit,
|
||||
)
|
||||
else:
|
||||
equipments = self.search(args, limit=limit)
|
||||
return [(equipment.id, equipment.display_name) for equipment in equipments]
|
||||
|
||||
def action_view_equipment(self):
|
||||
return {
|
||||
"name": "Equipment",
|
||||
"view_type": "form",
|
||||
"view_mode": "form",
|
||||
"res_id": self.id,
|
||||
"context": self.env.context,
|
||||
"res_model": "fsm.equipment",
|
||||
"type": "ir.actions.act_window",
|
||||
}
|
||||
|
||||
@api.depends("code", "name")
|
||||
def _compute_display_name(self):
|
||||
for rec in self:
|
||||
tag_part = "[%s] " % rec.code if rec.code else ""
|
||||
name = rec.name or ""
|
||||
rec.display_name = tag_part + name
|
||||
19
fsm_equipment/models/equipment_tag.py
Normal file
19
fsm_equipment/models/equipment_tag.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
from odoo import fields, models
|
||||
|
||||
|
||||
class EquipmentTag(models.Model):
|
||||
_name = "fsm.equipment.tag"
|
||||
_description = "Field service equipment category"
|
||||
|
||||
name = fields.Char(
|
||||
required=True,
|
||||
translate=True,
|
||||
)
|
||||
color = fields.Integer(
|
||||
"Color Index",
|
||||
default=10,
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
("name_uniq", "unique (name)", "Tag name already exists !"),
|
||||
]
|
||||
51
fsm_equipment/models/res_partner.py
Normal file
51
fsm_equipment/models/res_partner.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
from odoo import models, fields, api
|
||||
from odoo.osv import expression
|
||||
|
||||
|
||||
class Partner(models.Model):
|
||||
_inherit = "res.partner"
|
||||
|
||||
equipment_count = fields.Integer(compute="_compute_equipment_count")
|
||||
|
||||
owned_equipment_ids = fields.One2many(
|
||||
comodel_name="fsm.equipment",
|
||||
compute="_compute_owned_equipment_ids",
|
||||
string="Owned Equipments",
|
||||
)
|
||||
|
||||
equipment_ids = fields.One2many(
|
||||
comodel_name="fsm.equipment",
|
||||
inverse_name="partner_id",
|
||||
string="Site Equipment",
|
||||
)
|
||||
|
||||
is_service_site = fields.Boolean(
|
||||
compute="_compute_is_service_site",
|
||||
help="A partner is a service site if it has one or more equipments.",
|
||||
store=True,
|
||||
compute_sudo=True,
|
||||
)
|
||||
|
||||
@api.depends(
|
||||
"equipment_ids",
|
||||
"child_ids",
|
||||
"child_ids.company_type",
|
||||
"child_ids.equipment_ids",
|
||||
)
|
||||
def _compute_owned_equipment_ids(self):
|
||||
for rec in self:
|
||||
ids = rec.equipment_ids | rec.child_ids.mapped("equipment_ids")
|
||||
rec.owned_equipment_ids = ids or False
|
||||
|
||||
@api.depends("equipment_ids")
|
||||
def _compute_equipment_count(self):
|
||||
for rec in self:
|
||||
all_equipment_ids = self.env["fsm.equipment"].search(
|
||||
[("partner_id", "=", rec.id), ("parent_id", "=", False)]
|
||||
)
|
||||
rec.equipment_count = len(all_equipment_ids)
|
||||
|
||||
@api.depends("equipment_ids")
|
||||
def _compute_is_service_site(self):
|
||||
for rec in self:
|
||||
rec.is_service_site = bool(rec.equipment_ids)
|
||||
14
fsm_equipment/models/task.py
Normal file
14
fsm_equipment/models/task.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
from odoo import fields, models
|
||||
|
||||
|
||||
class Task(models.Model):
|
||||
_inherit = "project.task"
|
||||
|
||||
equipment_ids = fields.Many2many(
|
||||
comodel_name="fsm.equipment",
|
||||
relation="fsm_task_equipment_rel",
|
||||
column1="task_id",
|
||||
column2="equipment_id",
|
||||
string="Equipment to Service",
|
||||
tracking=True,
|
||||
)
|
||||
3
fsm_equipment/security/ir.model.access.csv
Normal file
3
fsm_equipment/security/ir.model.access.csv
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_fsm_equipment,fsm_equipment,model_fsm_equipment,base.group_user,1,1,1,1
|
||||
access_fsm_equipment_tag,fsm_equipment_tag,model_fsm_equipment_tag,base.group_user,1,1,1,1
|
||||
|
192
fsm_equipment/views/equipment_views.xml
Normal file
192
fsm_equipment/views/equipment_views.xml
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<!-- Equipment Form View -->
|
||||
<record id="equipment_view_form" model="ir.ui.view">
|
||||
<field name="name">fsm.equipment.form</field>
|
||||
<field name="model">fsm.equipment</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<group name="left">
|
||||
<field name="code"/>
|
||||
<field name="name"/>
|
||||
<field name="parent_id" invisible="partner_id"/>
|
||||
<field name="description"/>
|
||||
<field name="location_notes"/>
|
||||
</group>
|
||||
<group name="right">
|
||||
<field
|
||||
name="partner_id"
|
||||
context="{
|
||||
'default_type': 'delivery',
|
||||
'show_address': 1
|
||||
}"
|
||||
options='{"always_reload": True}'
|
||||
invisible="parent_id"
|
||||
required="not parent_id"
|
||||
/>
|
||||
<field
|
||||
name="inherited_partner_id"
|
||||
context="{
|
||||
'default_type': 'delivery',
|
||||
'show_address': 1
|
||||
}"
|
||||
options='{"always_reload": True}'
|
||||
invisible="partner_id or parent_id"
|
||||
/>
|
||||
<field
|
||||
name="tag_ids"
|
||||
widget="many2many_tags"
|
||||
options="{'no_open': False}"
|
||||
/>
|
||||
<field name="product_id"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Components">
|
||||
<field name="child_ids">
|
||||
<list editable="bottom" open_form_view="True">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="description"/>
|
||||
<field name="product_id"/>
|
||||
<field name="tag_ids" widget="many2many_tags"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Interventions">
|
||||
<field name="task_ids" readonly="True">
|
||||
<list open_form_view="True" default_order="date_deadline desc">
|
||||
<field name="date_deadline" widget="date" string="Date"/>
|
||||
<field name="name"/>
|
||||
<field name="user_ids" widget="many2many_avatar_user"/>
|
||||
<field name="description" class="text-truncate"/>
|
||||
<field name="effective_hours" optional="show"/>
|
||||
<field name="stage_id" optional="show"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids"/>
|
||||
<field name="activity_ids"/>
|
||||
<field name="message_ids"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="equipment_view_list" model="ir.ui.view">
|
||||
<field name="name">fsm.equipment.list</field>
|
||||
<field name="model">fsm.equipment</field>
|
||||
<field name="arch" type="xml">
|
||||
<list>
|
||||
<field name="code"/>
|
||||
<field name="name"/>
|
||||
<field name="description"/>
|
||||
<field
|
||||
name="tag_ids"
|
||||
widget="many2many_tags"
|
||||
options="{'no_open': False}"
|
||||
/>
|
||||
<field name="partner_id"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="equipment_view_list_editable" model="ir.ui.view">
|
||||
<field name="name">fsm.equipment.list.editable</field>
|
||||
<field name="model">fsm.equipment</field>
|
||||
<field name="arch" type="xml">
|
||||
<list editable="bottom">
|
||||
<field name="code"/>
|
||||
<field name="name"/>
|
||||
<field name="description"/>
|
||||
<field
|
||||
name="tag_ids"
|
||||
widget="many2many_tags"
|
||||
options="{'no_open': False}"
|
||||
/>
|
||||
<field name="partner_id"/>
|
||||
<button
|
||||
name="action_view_equipment"
|
||||
type="object"
|
||||
string="Details"
|
||||
icon="fa-external-link"
|
||||
/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_window_equipment">
|
||||
<field name="name">Equipment</field>
|
||||
<field name="res_model">fsm.equipment</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="context">
|
||||
{'search_default_parent_only': True}
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="equipment_view_search" model="ir.ui.view">
|
||||
<field name="name">fsm.equipment.search</field>
|
||||
<field name="model">fsm.equipment</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="parent_id"/>
|
||||
<field name="code"/>
|
||||
<field name="name"/>
|
||||
<field name="description"/>
|
||||
<field name="tag_ids"/>
|
||||
<field name="partner_id"/>
|
||||
<filter string="Parent Only" name="parent_only"
|
||||
domain="[('parent_id', '=', False)]"/>
|
||||
<filter string="Has Description" name="has_description"
|
||||
domain="[('description', '!=', False)]"/>
|
||||
<filter string="No Description" name="no_description"
|
||||
domain="[('description', '=', False)]"/>
|
||||
|
||||
<filter string="Partner" name="groupby_partner"
|
||||
context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Tags" name="groupby_tags"
|
||||
context="{'group_by':'tag_ids'}"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.actions.act_window" id="action_window_equipment_tags">
|
||||
<field name="name">Equipment Tag</field>
|
||||
<field name="res_model">fsm.equipment.tag</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="menu_contact_equipment"
|
||||
name="Customer Equipment"
|
||||
action="action_window_equipment"
|
||||
sequence="3"
|
||||
parent="contacts.menu_contacts"/>
|
||||
<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"
|
||||
/>
|
||||
</odoo>
|
||||
14
fsm_equipment/views/project_task_views.xml
Normal file
14
fsm_equipment/views/project_task_views.xml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="project_task_view_list_equipment" model="ir.ui.view">
|
||||
<field name="name">project.task.view.list.equipment</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="industry_fsm.project_task_view_list_fsm"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="partner_id" position="after">
|
||||
<field name="equipment_ids" widget="many2many_tags"
|
||||
string="Equipment" optional="show"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
57
fsm_equipment/views/res_partner_views.xml
Normal file
57
fsm_equipment/views/res_partner_views.xml
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="act_res_partner_2_equipment" model="ir.actions.act_window">
|
||||
<field name="name">Partner Equipment</field>
|
||||
<field name="res_model">fsm.equipment</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="context">{'default_partner_location_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="equipment" string="Equipment">
|
||||
<label for="equipment_ids" />
|
||||
<field
|
||||
name="equipment_ids"
|
||||
context="{'list_view_ref': 'fsm_equipment.fsm_equipment_view_list_editable_no_partner'}"
|
||||
readonly="False"
|
||||
/>
|
||||
<label for="owned_equipment_ids" />
|
||||
<field
|
||||
name="owned_equipment_ids"
|
||||
context="{'list_view_ref': 'fsm_equipment.equipment_view_list'}"
|
||||
readonly="True"
|
||||
/>
|
||||
</page>
|
||||
</xpath>
|
||||
<div name="button_box" position="inside">
|
||||
<button
|
||||
class="oe_stat_button"
|
||||
type="action"
|
||||
name="%(fsm_equipment.act_res_partner_2_equipment)d"
|
||||
icon="fa-tachometer"
|
||||
>
|
||||
<field
|
||||
string="Equipments"
|
||||
name="equipment_count"
|
||||
widget="statinfo"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</field>
|
||||
</record>
|
||||
<record id="fsm_equipment_view_list_editable_no_partner" model="ir.ui.view">
|
||||
<field name="name">fsm.equipment.view.list.editable.no.partner</field>
|
||||
<field name="inherit_id" ref="fsm_equipment.equipment_view_list_editable" />
|
||||
<field name="model">fsm.equipment</field>
|
||||
<field name="arch" type="xml">
|
||||
<field name="partner_id" position="attributes">
|
||||
<attribute name="column_invisible">True</attribute>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Loading…
Reference in a new issue