Major changes for applications and equipment.

Add the notion of application specification keys to allow administrators
to specify the names of specification items that can be used on an
application. This aims to homogenize specifications and to later provide
a sort of checklist when creating a new application for a customer.

Added groups for partner applications (user and admin). Administrators
have the right to create specification keys while users can only read
them (and use them to create specifications on an application).

Various view improvements across the affected modules.
This commit is contained in:
Marc Durepos 2024-10-17 15:28:07 -04:00
parent c304035edd
commit 87a1cb8860
20 changed files with 375 additions and 99 deletions

View file

@ -27,6 +27,7 @@
"license": "LGPL-3",
"depends": ["contacts", "incrementing_sequence_mixin"],
"data": [
"security/groups.xml",
"security/ir.model.access.csv",
"data/menus_actions.xml",
"views/application_type_views.xml",

View file

@ -34,7 +34,10 @@
</record>
<menuitem id="application_types"
parent="contacts.res_partner_menu_config"
groups="base.group_partner_manager"
groups="group_applications_admin"
action="view_application_types"
/>
<record id="contacts.res_partner_menu_config" model="ir.ui.menu">
<field name="sequence" eval="99"/>
</record>
</odoo>

View file

@ -2,3 +2,4 @@ from . import application
from . import application_specification
from . import application_type
from . import res_partner
from . import application_specification_key

View file

@ -18,6 +18,7 @@ class Application(models.Model):
comodel_name="partner.application.type",
required=True,
tracking=4,
ondelete="restrict",
)
specification_ids = fields.One2many(
comodel_name="partner.application.specification",

View file

@ -1,4 +1,4 @@
from odoo import models, fields
from odoo import models, fields, api
class PartnerApplicationSpecification(models.Model):
@ -7,14 +7,25 @@ class PartnerApplicationSpecification(models.Model):
_inherit = ["mail.thread", "mail.activity.mixin", "incrementing.sequence.mixin"]
_sequence_group = "application_id"
name = fields.Char(
tracking=2,
key_id = fields.Many2one(
comodel_name="partner.application.specification.key",
tracking=1,
ondelete="restrict",
domain="[('id', 'in', allowed_specification_keys)]",
string="Name",
required=True,
)
name = fields.Char(
related="key_id.name",
)
value = fields.Text(
tracking=2,
)
application_id = fields.Many2one(
comodel_name="partner.application",
tracking=1,
ondelete="cascade",
)
allowed_specification_keys = fields.Many2many(
related="application_id.application_type_id.allowed_specification_keys",
)

View file

@ -0,0 +1,11 @@
from odoo import models, fields, api
class ApplicationSpecificationKey(models.Model):
_name = "partner.application.specification.key"
_description = "Application Specification Key"
name = fields.Char(required=True, index="trigram")
_sql_constraints = [
("name_uniq", "unique (name)", "Specification key name must be unique."),
]

View file

@ -31,6 +31,12 @@ class PartnerApplicationType(models.Model):
string="Partners",
readonly=True,
)
allowed_specification_keys = fields.Many2many(
comodel_name="partner.application.specification.key",
relation="application_specification_key_application_type_rel",
column1="application_type_id",
column2="application_specification_key_id",
)
@api.depends("application_ids", "application_ids.partner_id")
def _compute_partner_ids(self):

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<record id="group_applications_admin" model="res.groups">
<field name="name">Applications Admin</field>
<field name="implied_ids" eval="[ref('group_applications_user')]"/>
</record>
<record id="group_applications_user" model="res.groups">
<field name="name">Applications User</field>
</record>
</data>
</odoo>

View file

@ -1,5 +1,7 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_partner_application_user,access.partner.application.user,model_partner_application,base.group_user,1,1,1,1
access_partner_application_type_user,access.partner.application.type.user,model_partner_application_type,base.group_user,1,0,0,0
access_partner_application_type_manager,access.partner.application.type.manager,model_partner_application_type,base.group_partner_manager,1,1,1,1
access_partner_application_specification_user,access.partner.application.specification.user,model_partner_application_specification,base.group_user,1,1,1,1
access_partner_application_user,access.partner.application.user,model_partner_application,group_applications_user,1,1,1,1
access_partner_application_type_user,access.partner.application.type.user,model_partner_application_type,group_applications_user,1,0,0,0
access_partner_application_type_manager,access.partner.application.type.manager,model_partner_application_type,group_applications_admin,1,1,1,1
access_partner_application_specification_user,access.partner.application.specification.user,model_partner_application_specification,group_applications_user,1,1,1,1
access_partner_application_specification_key_user,access.partner.application.specification.key.user,model_partner_application_specification_key,group_applications_user,1,0,0,0
access_partner_application_specification_key_admin,access.partner.application.specification.key.admin,model_partner_application_specification_key,group_applications_admin,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_partner_application_user access.partner.application.user model_partner_application base.group_user group_applications_user 1 1 1 1
3 access_partner_application_type_user access.partner.application.type.user model_partner_application_type base.group_user group_applications_user 1 0 0 0
4 access_partner_application_type_manager access.partner.application.type.manager model_partner_application_type base.group_partner_manager group_applications_admin 1 1 1 1
5 access_partner_application_specification_user access.partner.application.specification.user model_partner_application_specification base.group_user group_applications_user 1 1 1 1
6 access_partner_application_specification_key_user access.partner.application.specification.key.user model_partner_application_specification_key group_applications_user 1 0 0 0
7 access_partner_application_specification_key_admin access.partner.application.specification.key.admin model_partner_application_specification_key group_applications_admin 1 1 1 1

View file

@ -4,7 +4,7 @@
<field name="name">partner.application.type.list</field>
<field name="model">partner.application.type</field>
<field name="arch" type="xml">
<tree multi_edit="True" editable="bottom">
<tree multi_edit="True">
<field name="name"/>
<field name="description"/>
<field name="color" widget="color_picker"/>
@ -17,21 +17,45 @@
<field name="model">partner.application.type</field>
<field name="arch" type="xml">
<form>
<group>
<field name="name"/>
<header>
<header/>
<sheet>
<div name="button_box" class="oe_button_box">
<button name="%(action_view_partner_applications_by_type)d"
type="action"
class="oe_stat_button"
context="{'default_application_type_id': active_id}"
context="{'application_type_id': active_id}"
icon="fa-gears"
>
<field name="applications_count" string="Applications" widget="statinfo"/>
<field name="color" widget="color_picker"/>
<field name="applications_count" string="Applications"
widget="statinfo"/>
</button>
</header>
<field name="description"/>
</group>
</div>
<div name="title" class="oe_title">
<div class="oe_title">
<div class="oe_edit_only">
Application Type
</div>
<h1>
<field name="name"/>
</h1>
</div>
</div>
<group>
<field name="description"/>
<field name="color" widget="color_picker"/>
</group>
<notebook>
<page string="Allowed Specifications"
groups="customer_applications.group_applications_admin">
<field name="allowed_specification_keys" widget="many2many_tags"/>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
<field name="activity_ids"/>
</div>
</form>
</field>
</record>

View file

@ -6,34 +6,44 @@
<field name="arch" type="xml">
<form string="Partner Application">
<header/>
<div name="title" class="oe_title">
<div class="oe_title">
<div class="oe_edit_only">
Application
</div>
<h1>
<field name="name" />
</h1>
</div>
</div>
<div name="button_box" class="oe_button_box"/>
<sheet>
<div name="button_box" class="oe_button_box"/>
<div name="title" class="oe_title">
<div class="oe_title">
<div class="oe_edit_only">
Application
</div>
<h1>
<field name="name"/>
</h1>
</div>
</div>
<group>
<field name="description"/>
<field name="application_type_id"/>
<field name="partner_id"/>
<group>
<field name="partner_id"/>
</group>
<group>
<field name="application_type_id"/>
<field name="description"/>
</group>
</group>
<notebook>
<page string="Specifications">
<field name="specification_ids">
<tree editable="bottom">
<field name="name"/>
<field name="allowed_specification_keys" column_invisible="1"/>
<field name="key_id" />
<field name="value"/>
</tree>
</field>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_ids"/>
<field name="message_follower_ids"/>
<field name="activity_ids"/>
</div>
</form>
</field>
</record>

View file

@ -25,7 +25,7 @@
"author": "Bemade Inc.",
"website": "http://www.bemade.org",
"license": "LGPL-3",
"depends": ["industry_fsm", "account"],
"depends": ["industry_fsm", "account", "contacts"],
"data": [
"security/ir.model.access.csv",
"views/equipment_views.xml",

View file

@ -9,68 +9,67 @@
<sheet>
<group>
<group name="left">
<field name="code" />
<field name="name" />
<field name="description" />
<field name="location_notes" />
<field name="code"/>
<field name="name"/>
<field name="description"/>
<field name="location_notes"/>
</group>
<group name="right">
<field
name="partner_id"
groups="account.group_delivery_invoice_address"
context="{
name="partner_id"
context="{
'default_type': 'delivery',
'show_address': 1
}"
options='{"always_reload": True}'
/>
options='{"always_reload": True}'
/>
<field
name="tag_ids"
widget="many2many_tags"
options="{'no_open': False}"
/>
name="tag_ids"
widget="many2many_tags"
options="{'no_open': False}"
/>
</group>
</group>
<notebook>
<page string="Components">
<field name="equipment_component_ids">
<tree editable="bottom">
<field name="sequence" widget="handle" />
<field name="name" />
<field name="product_id" />
<field name="purpose_id" />
<field name="note" />
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="product_id"/>
<field name="purpose_id"/>
<field name="note"/>
</tree>
</field>
</page>
<page string="Interventions">
<field name="task_ids" groupby="root_ancestor" />
<field name="task_ids" groupby="root_ancestor"/>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" />
<field name="activity_ids" />
<field name="message_ids" />
<field name="message_follower_ids"/>
<field name="activity_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
<record id="equipment_view_tree" model="ir.ui.view">
<field name="name">fsm.equipment.tree</field>
<field name="model">fsm.equipment</field>
<field name="arch" type="xml">
<tree>
<field name="code" />
<field name="name" />
<field name="description" />
<field name="code"/>
<field name="name"/>
<field name="description"/>
<field
name="tag_ids"
widget="many2many_tags"
options="{'no_open': False}"
/>
<field name="partner_id" />
name="tag_ids"
widget="many2many_tags"
options="{'no_open': False}"
/>
<field name="partner_id"/>
</tree>
</field>
</record>
@ -80,21 +79,21 @@
<field name="model">fsm.equipment</field>
<field name="arch" type="xml">
<tree editable="bottom">
<field name="code" />
<field name="name" />
<field name="description" />
<field name="code"/>
<field name="name"/>
<field name="description"/>
<field
name="tag_ids"
widget="many2many_tags"
options="{'no_open': False}"
/>
<field name="partner_id" />
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"
/>
name="action_view_equipment"
type="object"
string="Details"
icon="fa-external-link"
/>
</tree>
</field>
</record>
@ -105,32 +104,61 @@
<field name="view_mode">tree,form</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="code"/>
<field name="name"/>
<field name="description"/>
<field name="tag_ids"/>
<field name="partner_id"/>
<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">tree,form</field>
</record>
<menuitem
id="menu_service_client"
name="Clients"
sequence="10"
parent="industry_fsm.fsm_menu_root"
groups="industry_fsm.group_fsm_user"
/>
id="menu_contact_equipment"
name="Customer Equipment"
action="action_window_equipment"
sequence="3"
parent="contacts.menu_contacts"/>
<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"
/>
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_equipment"
name="Client Equipment"
action="action_window_equipment"
sequence="21"
parent="menu_service_client"
groups="industry_fsm.group_fsm_user"
/>
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>

View file

@ -0,0 +1 @@
from . import models

View file

@ -0,0 +1,36 @@
#
# 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": "Partner Application Equipment",
"version": "17.0.1.0.0",
"summary": "Make the link between customer_applications and fsm_equipment.",
"category": "Services/Field Service",
"author": "Bemade Inc.",
"website": "http://www.bemade.org",
"license": "LGPL-3",
"depends": ["fsm_equipment", "customer_applications"],
"data": [
"views/equipment_views.xml",
"views/application_views.xml",
],
"assets": {},
"installable": True,
"auto_install": True,
}

View file

@ -0,0 +1,2 @@
from . import application
from . import equipment

View file

@ -0,0 +1,29 @@
from odoo import models, fields, api
from odoo.exceptions import ValidationError
class PartnerApplication(models.Model):
_inherit = "partner.application"
equipment_ids = fields.One2many(
comodel_name="fsm.equipment",
inverse_name="application_id",
string="Equipment",
)
equipment_count = fields.Integer(
compute="_compute_equipment_count",
string="Equipment Count",
)
@api.constrains("equipment_ids")
def _check_equipment_ids(self):
for application in self:
if len(application.equipment_ids.partner_id) > 1:
raise ValidationError(
"An application can only be linked to one partner."
)
@api.depends("equipment_ids")
def _compute_equipment_count(self):
for application in self:
application.equipment_count = len(application.equipment_ids)

View file

@ -0,0 +1,17 @@
from odoo import models, fields
class Equipment(models.Model):
_inherit = "fsm.equipment"
application_id = fields.Many2one(
comodel_name="partner.application",
tracking=1,
domain="[('partner_id', '=', partner_id)]",
ondelete="restrict",
)
application_type_id = fields.Many2one(
comodel_name="partner.application.type",
related="application_id.application_type_id",
readonly=True,
)

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="action_view_application_equipment" model="ir.actions.act_window">
<field name="name">Partner Equipments</field>
<field name="res_model">fsm.equipment</field>
<field name="view_mode">tree,form</field>
<field name="context">{'default_application_id': active_id}</field>
<field name="domain">[('application_id', '=', active_id)]</field>
</record>
<record id="view_partner_application_search" model="ir.ui.view">
<field name="name">partner.application.search</field>
<field name="model">partner.application</field>
<field name="inherit_id" ref="customer_applications.view_partner_application_search"/>
<field name="arch" type="xml">
<field name="application_type_id" position="after">
<field name="equipment_ids"/>
</field>
</field>
</record>
<record id="view_partner_application_form" model="ir.ui.view">
<field name="name">partner.application.form</field>
<field name="model">partner.application</field>
<field name="inherit_id" ref="customer_applications.view_partner_application_form"/>
<field name="arch" type="xml">
<div name="button_box">
<button name="%(action_view_application_equipment)d"
type="action"
class="oe_stat_button"
icon="fa-tachometer"
context="{'application_id': active_id}"
>
<field string="Equipments" name="equipment_count" widget="statinfo"/>
</button>
</div>
</field>
</record>
</odoo>

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_search_fsm_equipment" model="ir.ui.view">
<field name="name">fsm.equipment.view.search</field>
<field name="model">fsm.equipment</field>
<field name="inherit_id" ref="fsm_equipment.equipment_view_search"/>
<field name="arch" type="xml">
<field name="partner_id" position="after">
<field name="application_id"/>
<field name="application_type_id"/>
<filter name="groupby_application_id" string="Application"
context="{'group_by':'application_id'}"/>
<filter name="groupby_application_type_id"
string="Application Type"
context="{'group_by':'application_type_id'}"/>
</field>
</field>
</record>
<record id="view_tree_fsm_equipment" model="ir.ui.view">
<field name="name">fsm.equipment.view.tree</field>
<field name="model">fsm.equipment</field>
<field name="inherit_id" ref="fsm_equipment.equipment_view_tree"/>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="application_id" optional="show"/>
<field name="application_type_id" optional="show"/>
</field>
</field>
</record>
<record id="view_form_fsm_equipment" model="ir.ui.view">
<field name="name">fsm.equipment.view.form</field>
<field name="model">fsm.equipment</field>
<field name="inherit_id" ref="fsm_equipment.equipment_view_form"/>
<field name="arch" type="xml">
<field name="partner_id" position="after">
<field name="application_id"/>
</field>
</field>
</record>
</odoo>