project modifications
This commit is contained in:
parent
f6bfd46f2c
commit
c2e33753bb
|
|
@ -103,7 +103,7 @@ class ImBus(models.Model):
|
||||||
"""Low-level method to send ``notification_type`` and ``message`` to ``target``.
|
"""Low-level method to send ``notification_type`` and ``message`` to ``target``.
|
||||||
|
|
||||||
Using ``_bus_send()`` from ``bus.listener.mixin`` is recommended for simplicity and
|
Using ``_bus_send()`` from ``bus.listener.mixin`` is recommended for simplicity and
|
||||||
security.
|
security.
|
||||||
|
|
||||||
When using ``_sendone`` directly, ``target`` (if str) should not be guessable by an
|
When using ``_sendone`` directly, ``target`` (if str) should not be guessable by an
|
||||||
attacker.
|
attacker.
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
'version': '1.0',
|
'version': '1.0',
|
||||||
'category': 'Accounting/Localizations/Point of Sale',
|
'category': 'Accounting/Localizations/Point of Sale',
|
||||||
'description': """
|
'description': """
|
||||||
This add-on brings the technical requirements of the French regulation CGI art. 286, I. 3° bis that stipulates certain criteria concerning the inalterability, security, storage and archiving of data related to sales to private individuals (B2C).
|
This add-on brings the technical requirements of the French regulation CGI art. 286, I. 3° bis that stipulates certain criteria concerning the inalterability,security, storage and archiving of data related to sales to private individuals (B2C).
|
||||||
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
Install it if you use the Point of Sale app to sell to individuals.
|
Install it if you use the Point of Sale app to sell to individuals.
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ class Survey(http.Controller):
|
||||||
|
|
||||||
def _check_validity(self, survey_token, answer_token, ensure_token=True, check_partner=True):
|
def _check_validity(self, survey_token, answer_token, ensure_token=True, check_partner=True):
|
||||||
""" Check survey is open and can be taken. This does not checks for
|
""" Check survey is open and can be taken. This does not checks for
|
||||||
security rules, only functional / business rules. It returns a string key
|
security rules, only functional / business rules. It returns a string key
|
||||||
allowing further manipulation of validity issues
|
allowing further manipulation of validity issues
|
||||||
|
|
||||||
* survey_wrong: survey does not exist;
|
* survey_wrong: survey does not exist;
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
},
|
},
|
||||||
'installable': True,
|
'installable': True,
|
||||||
'data': [
|
'data': [
|
||||||
# security.xml first, data.xml need the group to exist (checking it)
|
#security.xml first, data.xml need the group to exist (checking it)
|
||||||
'security/website_security.xml',
|
'security/website_security.xml',
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
'data/image_library.xml',
|
'data/image_library.xml',
|
||||||
|
|
|
||||||
|
|
@ -1,62 +1,62 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
<!--security.xml -->
|
<!--security.xml -->
|
||||||
<odoo>
|
<odoo>
|
||||||
<!--security.xml -->
|
<!--security.xml -->
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<record id="category_employee_appraisal" model="ir.module.category">
|
<record id="category_employee_appraisal" model="ir.module.category">
|
||||||
<field name="name">Appraisal</field>
|
<field name="name">Appraisal</field>
|
||||||
<field name="sequence">50</field>
|
<field name="sequence">50</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<!-- Define the user groups -->
|
<!-- Define the user groups -->
|
||||||
<record id="group_appraisal_officer" model="res.groups">
|
<record id="group_appraisal_officer" model="res.groups">
|
||||||
<field name="name">Appraisal Officer</field>
|
<field name="name">Appraisal Officer</field>
|
||||||
<field name="category_id" ref="category_employee_appraisal"/>
|
<field name="category_id" ref="category_employee_appraisal"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
||||||
<record id="group_appraisal_manager" model="res.groups">
|
<record id="group_appraisal_manager" model="res.groups">
|
||||||
<field name="name">Appraisal HR Manager</field>
|
<field name="name">Appraisal HR Manager</field>
|
||||||
<field name="category_id" ref="category_employee_appraisal"/>
|
<field name="category_id" ref="category_employee_appraisal"/>
|
||||||
<field name="implied_ids" eval="[(4, ref('group_appraisal_officer'))]"/> <!-- Inherit Appraisal User permissions -->
|
<field name="implied_ids" eval="[(4, ref('group_appraisal_officer'))]"/> <!-- Inherit Appraisal User permissions -->
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="group_appraisal_administrator" model="res.groups">
|
<record id="group_appraisal_administrator" model="res.groups">
|
||||||
<field name="name">Appraisal Administrator</field>
|
<field name="name">Appraisal Administrator</field>
|
||||||
<field name="category_id" ref="category_employee_appraisal"/>
|
<field name="category_id" ref="category_employee_appraisal"/>
|
||||||
<field name="implied_ids" eval="[(4, ref('group_appraisal_officer'))]"/> <!-- Inherit Appraisal User permissions -->
|
<field name="implied_ids" eval="[(4, ref('group_appraisal_officer'))]"/> <!-- Inherit Appraisal User permissions -->
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<record id="employee_appraisal_base_user_rule" model="ir.rule">
|
<record id="employee_appraisal_base_user_rule" model="ir.rule">
|
||||||
<field name="name">User can only see his/her own appraisals</field>
|
<field name="name">User can only see his/her own appraisals</field>
|
||||||
<field name="model_id" ref="model_employee_appraisal"/>
|
<field name="model_id" ref="model_employee_appraisal"/>
|
||||||
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
|
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
|
||||||
<field name="domain_force">[('user_id','=',user.id),('state','!=','draft')]</field>
|
<field name="domain_force">[('user_id','=',user.id),('state','!=','draft')]</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="employee_appraisal_officer_rule" model="ir.rule">
|
<record id="employee_appraisal_officer_rule" model="ir.rule">
|
||||||
<field name="name">User can only see the records of people under him/her</field>
|
<field name="name">User can only see the records of people under him/her</field>
|
||||||
<field name="model_id" ref="model_employee_appraisal"/>
|
<field name="model_id" ref="model_employee_appraisal"/>
|
||||||
<field name="groups" eval="[(4, ref('group_appraisal_officer'))]"/>
|
<field name="groups" eval="[(4, ref('group_appraisal_officer'))]"/>
|
||||||
<field name="domain_force">[('reviewers_name.user_id','=',user.id),('state','!=','draft')]</field>
|
<field name="domain_force">[('reviewers_name.user_id','=',user.id),('state','!=','draft')]</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="employee_appraisal_manager_rule" model="ir.rule">
|
<record id="employee_appraisal_manager_rule" model="ir.rule">
|
||||||
<field name="name">User can only see the all the appraisal records where he/she is set as HR</field>
|
<field name="name">User can only see the all the appraisal records where he/she is set as HR</field>
|
||||||
<field name="model_id" ref="model_employee_appraisal"/>
|
<field name="model_id" ref="model_employee_appraisal"/>
|
||||||
<field name="groups" eval="[(4, ref('group_appraisal_manager'))]"/>
|
<field name="groups" eval="[(4, ref('group_appraisal_manager'))]"/>
|
||||||
<field name="domain_force">[('appraisal_hr_id','=',user.id)]</field>
|
<field name="domain_force">[('appraisal_hr_id','=',user.id)]</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="employee_appraisal_md_rule" model="ir.rule">
|
<record id="employee_appraisal_md_rule" model="ir.rule">
|
||||||
<field name="name">User can only see the all the appraisal records where he/she is set as MD</field>
|
<field name="name">User can only see the all the appraisal records where he/she is set as MD</field>
|
||||||
<field name="model_id" ref="model_employee_appraisal"/>
|
<field name="model_id" ref="model_employee_appraisal"/>
|
||||||
<field name="groups" eval="[(4, ref('group_appraisal_administrator'))]"/>
|
<field name="groups" eval="[(4, ref('group_appraisal_administrator'))]"/>
|
||||||
<field name="domain_force">[('appraisal_md_id','=',user.id)]</field>
|
<field name="domain_force">[('appraisal_md_id','=',user.id)]</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
{
|
{
|
||||||
"name": "Custom Module Switcher",
|
"name": "Custom Module Switcher",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"depends": ["web","menu_control_center"],
|
"depends": ["web","menu_control_center"],
|
||||||
'data': [
|
'data': [
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
],
|
],
|
||||||
"assets": {
|
"assets": {
|
||||||
"web.assets_backend": [
|
"web.assets_backend": [
|
||||||
"module_selector_sidebar/static/src/js/module_switcher.js",
|
"module_selector_sidebar/static/src/js/module_switcher.js",
|
||||||
"module_selector_sidebar/static/src/xml/module_switcher.xml",
|
"module_selector_sidebar/static/src/xml/module_switcher.xml",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"installable": True,
|
"installable": True,
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from . import project_kudo_extend
|
from . import project_kudo_extend
|
||||||
from . import task_assignee_domain
|
from . import task_assignee_domain
|
||||||
from . import project_task
|
from . import project_task
|
||||||
from . import account_analytic_line
|
from . import account_analytic_line
|
||||||
|
|
@ -49,9 +49,9 @@ Key Features:
|
||||||
'view/maintenance_support.xml',
|
'view/maintenance_support.xml',
|
||||||
'view/project_closer.xml',
|
'view/project_closer.xml',
|
||||||
'view/project_actual_costings.xml',
|
'view/project_actual_costings.xml',
|
||||||
|
'view/project_task.xml',
|
||||||
'view/project.xml',
|
'view/project.xml',
|
||||||
'view/project_portfolio.xml',
|
'view/project_portfolio.xml',
|
||||||
'view/project_task.xml',
|
|
||||||
'view/timesheets.xml',
|
'view/timesheets.xml',
|
||||||
'view/pro_task_gantt.xml',
|
'view/pro_task_gantt.xml',
|
||||||
'view/user_availability.xml',
|
'view/user_availability.xml',
|
||||||
|
|
@ -61,9 +61,10 @@ Key Features:
|
||||||
'view/stage_approval_wizard.xml',
|
'view/stage_approval_wizard.xml',
|
||||||
],
|
],
|
||||||
'assets': {
|
'assets': {
|
||||||
'web.assets_backend':{
|
'web.assets_backend': [
|
||||||
'project_task_timesheet_extended/static/src/css/delopyment.css'
|
'project_task_timesheet_extended/static/src/css/delopyment.css',
|
||||||
}
|
'project_task_timesheet_extended/static/src/js/involved_assignee_avatar_user_field.js',
|
||||||
|
]
|
||||||
},
|
},
|
||||||
'installable': True,
|
'installable': True,
|
||||||
'application': False,
|
'application': False,
|
||||||
|
|
|
||||||
|
|
@ -263,4 +263,5 @@
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
|
<function model="project.task" name="_sync_all_involved_assignees_from_timelines"/>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
||||||
|
|
@ -120,14 +120,18 @@ def post_init_hook(env):
|
||||||
'|',
|
'|',
|
||||||
'&',
|
'&',
|
||||||
('task_id.is_generic', '=', False),
|
('task_id.is_generic', '=', False),
|
||||||
('user_id', 'in', 'task_id.user_ids'),
|
'|',
|
||||||
|
('user_id', 'in', 'task_id.user_ids'),
|
||||||
|
('user_id', 'in', 'task_id.involved_user_ids'),
|
||||||
'&',
|
'&',
|
||||||
('task_id.is_generic', '=', True),
|
('task_id.is_generic', '=', True),
|
||||||
('user_id.partner_id', 'in', 'project_id.message_partner_ids'),
|
('user_id.partner_id', 'in', 'project_id.message_partner_ids'),
|
||||||
'&', '&',
|
'&', '&',
|
||||||
('project_id.privacy_visibility', '!=', 'followers'),
|
('project_id.privacy_visibility', '!=', 'followers'),
|
||||||
('task_id.is_generic', '=', False),
|
('task_id.is_generic', '=', False),
|
||||||
('user_id', 'in', 'task_id.user_ids')
|
'|',
|
||||||
|
('user_id', 'in', 'task_id.user_ids'),
|
||||||
|
('user_id', 'in', 'task_id.involved_user_ids')
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
})
|
})
|
||||||
|
|
@ -162,12 +166,12 @@ def post_init_hook(env):
|
||||||
project_tasks[task.project_id.id] = []
|
project_tasks[task.project_id.id] = []
|
||||||
project_tasks[task.project_id.id].append(task)
|
project_tasks[task.project_id.id].append(task)
|
||||||
|
|
||||||
# Assign sequence numbers to tasks
|
# Assign sequence numbers to tasks
|
||||||
for project_id, task_list in project_tasks.items():
|
for project_id, task_list in project_tasks.items():
|
||||||
project = env['project.project'].browse(project_id)
|
project = env['project.project'].browse(project_id)
|
||||||
if project.task_sequence_id:
|
if project.task_sequence_id:
|
||||||
for task in task_list:
|
for task in task_list:
|
||||||
task.sequence_name = project.task_sequence_id.next_by_id()
|
task.sequence_name = project.task_sequence_id.next_by_id()
|
||||||
|
|
||||||
# Normalize task stages so each project owns its workflow configuration.
|
# Normalize task stages so each project owns its workflow configuration.
|
||||||
env['project.project'].search([])._ensure_project_owned_task_stages()
|
env['project.project'].search([])._ensure_project_owned_task_stages()
|
||||||
|
|
|
||||||
|
|
@ -333,8 +333,6 @@ class ProjectProject(models.Model):
|
||||||
users_list = list()
|
users_list = list()
|
||||||
if project.assign_approval_flow:
|
if project.assign_approval_flow:
|
||||||
users_list.extend(project.project_stages.involved_users.ids)
|
users_list.extend(project.project_stages.involved_users.ids)
|
||||||
else:
|
|
||||||
users_list.extend(project.showable_stage_ids.user_ids.ids)
|
|
||||||
|
|
||||||
if project.project_sponsor:
|
if project.project_sponsor:
|
||||||
users_list.append(project.project_sponsor.id)
|
users_list.append(project.project_sponsor.id)
|
||||||
|
|
|
||||||
|
|
@ -167,12 +167,12 @@ class projectTask(models.Model):
|
||||||
task.assignee_domain_ids = all_internal_users
|
task.assignee_domain_ids = all_internal_users
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# # GENERIC → all internal
|
# # GENERIC: all internal
|
||||||
# if getattr(task, 'is_generic', False):
|
# if getattr(task, 'is_generic', False):
|
||||||
# task.assignee_domain_ids = all_internal_users
|
# task.assignee_domain_ids = all_internal_users
|
||||||
# continue
|
# continue
|
||||||
|
|
||||||
# PRIVATE → invited users only
|
# PRIVATE: invited users only
|
||||||
if task.project_id.privacy_visibility == 'followers':
|
if task.project_id.privacy_visibility == 'followers':
|
||||||
task.assignee_domain_ids = (
|
task.assignee_domain_ids = (
|
||||||
task.project_id.message_partner_ids
|
task.project_id.message_partner_ids
|
||||||
|
|
@ -196,18 +196,18 @@ class projectTask(models.Model):
|
||||||
for task in self:
|
for task in self:
|
||||||
employees = Employee.browse()
|
employees = Employee.browse()
|
||||||
|
|
||||||
# 1️⃣ GENERIC TASK
|
# GENERIC TASK
|
||||||
if task.is_generic and task.project_id:
|
if task.is_generic and task.project_id:
|
||||||
project = task.project_id
|
project = task.project_id
|
||||||
|
|
||||||
# 🔐 Private → followers only
|
# Private: followers only
|
||||||
if project.privacy_visibility == 'followers':
|
if project.privacy_visibility == 'followers':
|
||||||
users = (
|
users = (
|
||||||
project.message_partner_ids
|
project.message_partner_ids
|
||||||
.mapped('user_ids')
|
.mapped('user_ids')
|
||||||
.filtered(lambda u: u and not u.share)
|
.filtered(lambda u: u and not u.share)
|
||||||
)
|
)
|
||||||
# 🌍 Internal / Public → all internal users
|
# Internal / Public: all internal users
|
||||||
else:
|
else:
|
||||||
users = self.env['res.users'].search([
|
users = self.env['res.users'].search([
|
||||||
('share', '=', False),
|
('share', '=', False),
|
||||||
|
|
@ -216,7 +216,7 @@ class projectTask(models.Model):
|
||||||
|
|
||||||
employees = users.mapped('employee_id').filtered(lambda e: e)
|
employees = users.mapped('employee_id').filtered(lambda e: e)
|
||||||
|
|
||||||
# 2️⃣ NORMAL TASK → task assignees only
|
# NORMAL TASK: assignees and involved collaborators
|
||||||
else:
|
else:
|
||||||
employees = (
|
employees = (
|
||||||
task.user_ids
|
task.user_ids
|
||||||
|
|
@ -393,7 +393,7 @@ class projectTask(models.Model):
|
||||||
if start_dt.tzinfo is None:
|
if start_dt.tzinfo is None:
|
||||||
start_dt = pytz.UTC.localize(start_dt)
|
start_dt = pytz.UTC.localize(start_dt)
|
||||||
|
|
||||||
# Convert UTC → calendar timezone
|
# Convert UTC to calendar timezone
|
||||||
start_dt_tz = start_dt.astimezone(tz)
|
start_dt_tz = start_dt.astimezone(tz)
|
||||||
|
|
||||||
# Call plan_hours
|
# Call plan_hours
|
||||||
|
|
@ -459,8 +459,8 @@ class projectTask(models.Model):
|
||||||
self.env.user.name,
|
self.env.user.name,
|
||||||
task.suggested_deadline.strftime('%Y-%m-%d %H:%M') if task.suggested_deadline else _('Not available')
|
task.suggested_deadline.strftime('%Y-%m-%d %H:%M') if task.suggested_deadline else _('Not available')
|
||||||
))
|
))
|
||||||
|
|
||||||
@api.depends("project_id", "stage_id")
|
@api.depends("project_id")
|
||||||
def _compute_has_supervisor_access(self):
|
def _compute_has_supervisor_access(self):
|
||||||
administrative_users = self.env['project.role'].search([
|
administrative_users = self.env['project.role'].search([
|
||||||
('role_level', '=', 'administrative')
|
('role_level', '=', 'administrative')
|
||||||
|
|
@ -478,26 +478,13 @@ class projectTask(models.Model):
|
||||||
|
|
||||||
stages = project.type_ids.sorted("sequence")
|
stages = project.type_ids.sorted("sequence")
|
||||||
|
|
||||||
if not stages:
|
if first_stage:
|
||||||
continue
|
create_access_users = first_stage.team_id.team_lead + first_stage.involved_user_ids + administrative_users.user_ids
|
||||||
|
else:
|
||||||
|
create_access_users = administrative_users.user_ids
|
||||||
|
|
||||||
first_stage = stages[0]
|
|
||||||
|
|
||||||
create_access_users = (
|
if current_user.has_group("project.group_project_manager") or current_user == task.project_id.user_id or current_user == task.project_id.project_lead or (current_user.id in list(set(create_access_users.ids)) and task.stage_id.id == first_stage.id):
|
||||||
first_stage.team_id.team_lead
|
|
||||||
+ first_stage.involved_user_ids
|
|
||||||
+ administrative_users
|
|
||||||
)
|
|
||||||
|
|
||||||
if (
|
|
||||||
current_user.has_group("project.group_project_manager")
|
|
||||||
or current_user == project.user_id
|
|
||||||
or current_user == project.project_lead
|
|
||||||
or (
|
|
||||||
current_user in create_access_users
|
|
||||||
and task.stage_id == first_stage
|
|
||||||
)
|
|
||||||
):
|
|
||||||
task.has_supervisor_access = True
|
task.has_supervisor_access = True
|
||||||
|
|
||||||
@api.depends('assignees_timelines.estimated_time', 'show_approval_flow')
|
@api.depends('assignees_timelines.estimated_time', 'show_approval_flow')
|
||||||
|
|
@ -637,11 +624,11 @@ class projectTask(models.Model):
|
||||||
task.show_approval_button = True
|
task.show_approval_button = True
|
||||||
task.show_refuse_button = True # both approve & refuse in review state
|
task.show_refuse_button = True # both approve & refuse in review state
|
||||||
|
|
||||||
# b) No assigned user → directly approvable
|
# b) No assigned user: directly approvable
|
||||||
elif not assigned_to and (responsible_lead == user or project_manager == user):
|
elif not assigned_to and (responsible_lead == user or project_manager == user):
|
||||||
task.show_approval_button = True
|
task.show_approval_button = True
|
||||||
|
|
||||||
# c) Assigned_to == responsible_lead → no submission needed, direct approve
|
# c) Assigned_to == responsible_lead: no submission needed, direct approve
|
||||||
elif (
|
elif (
|
||||||
assigned_to
|
assigned_to
|
||||||
and assigned_to == responsible_lead
|
and assigned_to == responsible_lead
|
||||||
|
|
@ -797,7 +784,7 @@ class projectTask(models.Model):
|
||||||
task.stage_id = n_stage
|
task.stage_id = n_stage
|
||||||
task.approval_status = "approved"
|
task.approval_status = "approved"
|
||||||
|
|
||||||
activity_log = "%s: ✅ approved by %s and moved to %s" % (
|
activity_log = "%s: approved by %s and moved to %s" % (
|
||||||
current_stage.name,
|
current_stage.name,
|
||||||
self.env.user.employee_id.name,
|
self.env.user.employee_id.name,
|
||||||
n_stage.name)
|
n_stage.name)
|
||||||
|
|
@ -838,9 +825,9 @@ class projectTask(models.Model):
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
task.approval_status = "approved"
|
task.approval_status = "approved"
|
||||||
notes = "%s: ✅ Task approved and completed by %s" % (task.sequence_name, self.env.user.employee_id.name)
|
notes = "%s: Task approved and completed by %s" % (task.sequence_name, self.env.user.employee_id.name)
|
||||||
|
|
||||||
activity_log = "%s: ✅ approved by %s" % (
|
activity_log = "%s: approved by %s" % (
|
||||||
current_stage.name,
|
current_stage.name,
|
||||||
self.env.user.employee_id.name)
|
self.env.user.employee_id.name)
|
||||||
|
|
||||||
|
|
@ -876,9 +863,9 @@ class projectTask(models.Model):
|
||||||
# Optional: find previous stage if you want to send back
|
# Optional: find previous stage if you want to send back
|
||||||
stage = task.assignees_timelines.filtered(lambda s: s.stage_id == task.stage_id)
|
stage = task.assignees_timelines.filtered(lambda s: s.stage_id == task.stage_id)
|
||||||
|
|
||||||
notes = "%s: ❌ %s rejected by %s" % (task.sequence_name, current_stage.name, self.env.user.employee_id.name)
|
notes = "%s: %s rejected by %s" % (task.sequence_name, current_stage.name, self.env.user.employee_id.name)
|
||||||
|
|
||||||
activity_log = "%s: ❌ rejected by %s: %s" % (
|
activity_log = "%s: rejected by %s: %s" % (
|
||||||
current_stage.name,
|
current_stage.name,
|
||||||
self.env.user.employee_id.name,
|
self.env.user.employee_id.name,
|
||||||
reason)
|
reason)
|
||||||
|
|
@ -1017,19 +1004,27 @@ class projectTask(models.Model):
|
||||||
"You are not allowed to change the stage of this task because stage editing is restricted."
|
"You are not allowed to change the stage of this task because stage editing is restricted."
|
||||||
))
|
))
|
||||||
|
|
||||||
return super(projectTask, self).write(vals)
|
result = super(projectTask, self).write(vals)
|
||||||
|
if any(field in vals for field in ['allocation_start_date', 'allocation_end_date']):
|
||||||
|
self._sync_allocated_hours_from_allocation_dates()
|
||||||
|
if any(field in vals for field in ['user_ids', 'is_generic']):
|
||||||
|
self._sync_involved_assignees_from_timelines()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def button_update_assignees(self):
|
def _sync_involved_assignees_from_timelines(self):
|
||||||
for task in self:
|
for task in self:
|
||||||
if task.assignees_timelines:
|
if task.is_generic:
|
||||||
users_list = list(
|
continue
|
||||||
set(task.assignees_timelines.responsible_lead.ids + task.assignees_timelines.assigned_to.ids + task.assignees_timelines.team_id.team_lead.ids))
|
|
||||||
task.user_ids = [(6, 0, users_list)]
|
|
||||||
|
|
||||||
# Post to project channel about assignee update
|
timeline_user_ids = set(
|
||||||
channel_message = _("Assignees updated for task %s") % (task.sequence_name or task.name)
|
task.assignees_timelines.responsible_lead.ids
|
||||||
task._post_to_project_channel(channel_message)
|
+ task.assignees_timelines.assigned_to.ids
|
||||||
|
+ task.assignees_timelines.team_id.team_lead.ids
|
||||||
|
)
|
||||||
|
existing_user_ids = set(task.involved_user_ids.ids)
|
||||||
|
involved_users = list((existing_user_ids | timeline_user_ids) - set(task.user_ids.ids))
|
||||||
|
task.involved_user_ids = [(6, 0, involved_users)]
|
||||||
|
|
||||||
def _fetch_planning_overlap(self, additional_domain=None):
|
def _fetch_planning_overlap(self, additional_domain=None):
|
||||||
use_timeline_logic = any(
|
use_timeline_logic = any(
|
||||||
|
|
@ -1419,7 +1414,7 @@ class projectTask(models.Model):
|
||||||
FROM project_task T1
|
FROM project_task T1
|
||||||
INNER JOIN project_task T2 ON T1.id <> T2.id
|
INNER JOIN project_task T2 ON T1.id <> T2.id
|
||||||
INNER JOIN project_task_user_rel U1 ON T1.id = U1.task_id
|
INNER JOIN project_task_user_rel U1 ON T1.id = U1.task_id
|
||||||
INNER JOIN project_task_user_rel U2
|
INNER JOIN project_task_user_rel U2
|
||||||
ON T2.id = U2.task_id
|
ON T2.id = U2.task_id
|
||||||
AND U1.user_id = U2.user_id
|
AND U1.user_id = U2.user_id
|
||||||
WHERE
|
WHERE
|
||||||
|
|
|
||||||
|
|
@ -1,161 +1,161 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="group_project_supervisor" model="res.groups">
|
<record id="group_project_supervisor" model="res.groups">
|
||||||
<field name="name">Manager</field>
|
<field name="name">Manager</field>
|
||||||
<field name="category_id" ref="base.module_category_services_project"/>
|
<field name="category_id" ref="base.module_category_services_project"/>
|
||||||
<field name="implied_ids" eval="[(4, ref('project.group_project_user'))]"/>
|
<field name="implied_ids" eval="[(4, ref('project.group_project_user'))]"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="group_project_lead" model="res.groups">
|
<record id="group_project_lead" model="res.groups">
|
||||||
<field name="name">Project Lead</field>
|
<field name="name">Project Lead</field>
|
||||||
<field name="category_id" ref="base.module_category_services_project"/>
|
<field name="category_id" ref="base.module_category_services_project"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
||||||
<record id="project.group_project_manager" model="res.groups">
|
<record id="project.group_project_manager" model="res.groups">
|
||||||
<field name="implied_ids" eval="[(4, ref('project.group_project_user')),(4, ref('group_project_supervisor')), (4, ref('mail.group_mail_canned_response_admin'))]"/>
|
<field name="implied_ids" eval="[(4, ref('project.group_project_user')),(4, ref('group_project_supervisor')), (4, ref('mail.group_mail_canned_response_admin'))]"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<data>
|
<data>
|
||||||
<record id="portfolio_rule_company_projects" model="ir.rule">
|
<record id="portfolio_rule_company_projects" model="ir.rule">
|
||||||
<field name="name">company: Own Company</field>
|
<field name="name">company: Own Company</field>
|
||||||
<field name="model_id" ref="model_project_portfolio"/>
|
<field name="model_id" ref="model_project_portfolio"/>
|
||||||
<field name="domain_force">[('company_id', 'in', company_ids + [False])]</field>
|
<field name="domain_force">[('company_id', 'in', company_ids + [False])]</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="project_rule_manager_own_projects" model="ir.rule">
|
<record id="project_rule_manager_own_projects" model="ir.rule">
|
||||||
<field name="name">Manager: Own Projects</field>
|
<field name="name">Manager: Own Projects</field>
|
||||||
<field name="model_id" ref="project.model_project_project"/>
|
<field name="model_id" ref="project.model_project_project"/>
|
||||||
<field name="groups" eval="[(4, ref('project_task_timesheet_extended.group_project_supervisor'))]"/>
|
<field name="groups" eval="[(4, ref('project_task_timesheet_extended.group_project_supervisor'))]"/>
|
||||||
<field name="domain_force">[('user_id', '=', user.id)]</field>
|
<field name="domain_force">[('user_id', '=', user.id)]</field>
|
||||||
<field name="perm_read" eval="1"/>
|
<field name="perm_read" eval="1"/>
|
||||||
<field name="perm_write" eval="1"/>
|
<field name="perm_write" eval="1"/>
|
||||||
<field name="perm_create" eval="1"/>
|
<field name="perm_create" eval="1"/>
|
||||||
<field name="perm_unlink" eval="0"/>
|
<field name="perm_unlink" eval="0"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.rule" id="project_supervisor_all_project_tasks_rule">
|
<record model="ir.rule" id="project_supervisor_all_project_tasks_rule">
|
||||||
<field name="name">Project/Task: project supervisor: see all tasks linked to his assigned project or its own tasks</field>
|
<field name="name">Project/Task: project supervisor: see all tasks linked to his assigned project or its own tasks</field>
|
||||||
<field name="model_id" ref="project.model_project_task"/>
|
<field name="model_id" ref="project.model_project_task"/>
|
||||||
<field name="domain_force">[
|
<field name="domain_force">[
|
||||||
('project_id.user_id','=',user.id),
|
('project_id.user_id','=',user.id),
|
||||||
'|', ('project_id', '!=', False),
|
'|', ('project_id', '!=', False),
|
||||||
('user_ids', 'in', user.id),
|
('user_ids', 'in', user.id),
|
||||||
]</field>
|
]</field>
|
||||||
<field name="groups" eval="[(4,ref('project_task_timesheet_extended.group_project_supervisor'))]"/>
|
<field name="groups" eval="[(4,ref('project_task_timesheet_extended.group_project_supervisor'))]"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.rule" id="project_users_project_tasks_rule">
|
<record model="ir.rule" id="project_users_project_tasks_rule">
|
||||||
<field name="name">Project/Task: project users: don't see non generic tasks</field>
|
<field name="name">Project/Task: project users: don't see non generic tasks</field>
|
||||||
<field name="model_id" ref="project.model_project_task"/>
|
<field name="model_id" ref="project.model_project_task"/>
|
||||||
<field name="domain_force">[
|
<field name="domain_force">[
|
||||||
'&', '&',
|
'&', '&',
|
||||||
('project_id', '!=', False),
|
('project_id', '!=', False),
|
||||||
('is_generic', '=', False),
|
('is_generic', '=', False),
|
||||||
('user_ids', 'not in', user.id),
|
('user_ids', 'not in', user.id),
|
||||||
]
|
]
|
||||||
</field>
|
</field>
|
||||||
<field name="groups" eval="[(4,ref('base.group_user')),(4,ref('project.group_project_user'))]"/>
|
<field name="groups" eval="[(4,ref('base.group_user')),(4,ref('project.group_project_user'))]"/>
|
||||||
<field name="perm_read" eval="0"/>
|
<field name="perm_read" eval="0"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.rule" id="project_users_project_lead_rule">
|
<record model="ir.rule" id="project_users_project_lead_rule">
|
||||||
<field name="name">Project/Task: project lead: see all tasks</field>
|
<field name="name">Project/Task: project lead: see all tasks</field>
|
||||||
<field name="model_id" ref="project.model_project_task"/>
|
<field name="model_id" ref="project.model_project_task"/>
|
||||||
<field name="domain_force">[
|
<field name="domain_force">[
|
||||||
'&', '&', '&',
|
'&', '&', '&',
|
||||||
('project_id', '!=', False),
|
('project_id', '!=', False),
|
||||||
('project_id.project_lead', '=', user.id),
|
('project_id.project_lead', '=', user.id),
|
||||||
'|', ('is_generic', '=', True), ('is_generic', '=', False),
|
'|', ('is_generic', '=', True), ('is_generic', '=', False),
|
||||||
'|', ('user_ids', 'in', user.id), ('user_ids', 'not in', user.id)
|
'|', ('user_ids', 'in', user.id), ('user_ids', 'not in', user.id)
|
||||||
]
|
]
|
||||||
</field>
|
</field>
|
||||||
<field name="groups" eval="[(4,ref('base.group_user')),(4,ref('project.group_project_user'))]"/>
|
<field name="groups" eval="[(4,ref('base.group_user')),(4,ref('project.group_project_user'))]"/>
|
||||||
<field name="perm_read" eval="1"/>
|
<field name="perm_read" eval="1"/>
|
||||||
<field name="perm_write" eval="1"/>
|
<field name="perm_write" eval="1"/>
|
||||||
<field name="perm_create" eval="1"/>
|
<field name="perm_create" eval="1"/>
|
||||||
<field name="perm_unlink" eval="0"/>
|
<field name="perm_unlink" eval="0"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.rule" id="user_task_availability_project_lead_rule">
|
<record model="ir.rule" id="user_task_availability_project_lead_rule">
|
||||||
<field name="name">Task Availability: project lead: see all user tasks</field>
|
<field name="name">Task Availability: project lead: see all user tasks</field>
|
||||||
<field name="model_id" ref="model_user_task_availability"/>
|
<field name="model_id" ref="model_user_task_availability"/>
|
||||||
<field name="groups" eval="[(4,ref('base.group_user')),(4,ref('project.group_project_user'))]"/>
|
<field name="groups" eval="[(4,ref('base.group_user')),(4,ref('project.group_project_user'))]"/>
|
||||||
<field name="domain_force">[
|
<field name="domain_force">[
|
||||||
'|', '|',
|
'|', '|',
|
||||||
('project_id.project_lead', '=', user.id),
|
('project_id.project_lead', '=', user.id),
|
||||||
('user_id', '=', user.id),
|
('user_id', '=', user.id),
|
||||||
('project_id.user_id', '=', user.id),
|
('project_id.user_id', '=', user.id),
|
||||||
]
|
]
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<!-- <record model="ir.rule" id="timesheet_users_normal_timesheets">-->
|
<!-- <record model="ir.rule" id="timesheet_users_normal_timesheets">-->
|
||||||
<!-- <field name="name">timesheet: users: see own tasks</field>-->
|
<!-- <field name="name">timesheet: users: see own tasks</field>-->
|
||||||
<!-- <field name="model_id" ref="analytic.model_account_analytic_line"/>-->
|
<!-- <field name="model_id" ref="analytic.model_account_analytic_line"/>-->
|
||||||
<!-- <field name="domain_force">[-->
|
<!-- <field name="domain_force">[-->
|
||||||
<!-- '&','&', '&', '&', '&','&',-->
|
<!-- '&','&', '&', '&', '&','&',-->
|
||||||
<!-- ('project_id.privacy_visibility','=','followers'),-->
|
<!-- ('project_id.privacy_visibility','=','followers'),-->
|
||||||
<!-- ('task_id', '!=', False),-->
|
<!-- ('task_id', '!=', False),-->
|
||||||
<!-- ('project_id', '!=', False),-->
|
<!-- ('project_id', '!=', False),-->
|
||||||
<!-- ('project_id.project_lead', '!=', user.id),-->
|
<!-- ('project_id.project_lead', '!=', user.id),-->
|
||||||
<!-- ('project_id.user_id', '!=', user.id),-->
|
<!-- ('project_id.user_id', '!=', user.id),-->
|
||||||
<!-- ('user_id','not in',[user.id]),-->
|
<!-- ('user_id','not in',[user.id]),-->
|
||||||
<!-- '|',-->
|
<!-- '|',-->
|
||||||
<!-- '&',-->
|
<!-- '&',-->
|
||||||
<!-- ('task_id.is_generic', '=', False),-->
|
<!-- ('task_id.is_generic', '=', False),-->
|
||||||
<!-- ('task_id.user_ids', 'not in', [user.id]),-->
|
<!-- ('task_id.user_ids', 'not in', [user.id]),-->
|
||||||
<!-- '&',-->
|
<!-- '&',-->
|
||||||
<!-- ('task_id.is_generic', '=', True),-->
|
<!-- ('task_id.is_generic', '=', True),-->
|
||||||
<!-- ('project_id.message_partner_ids', 'not in', [user.partner_id.id]),-->
|
<!-- ('project_id.message_partner_ids', 'not in', [user.partner_id.id]),-->
|
||||||
<!-- ]</field>-->
|
<!-- ]</field>-->
|
||||||
<!-- <field name="groups" eval="[(4,ref('base.group_user')),(4,ref('project.group_project_user')),(4,ref('project_task_timesheet_extended.group_project_supervisor')),(4,ref('hr_timesheet.group_hr_timesheet_user')),(4,ref('hr_timesheet.group_hr_timesheet_approver'))]"/>-->
|
<!-- <field name="groups" eval="[(4,ref('base.group_user')),(4,ref('project.group_project_user')),(4,ref('project_task_timesheet_extended.group_project_supervisor')),(4,ref('hr_timesheet.group_hr_timesheet_user')),(4,ref('hr_timesheet.group_hr_timesheet_approver'))]"/>-->
|
||||||
<!-- <field name="perm_read" eval="0"/>-->
|
<!-- <field name="perm_read" eval="0"/>-->
|
||||||
<!-- <field name="perm_unlink" eval="1"/>-->
|
<!-- <field name="perm_unlink" eval="1"/>-->
|
||||||
<!-- <field name="perm_write" eval="1"/>-->
|
<!-- <field name="perm_write" eval="1"/>-->
|
||||||
<!-- <field name="perm_create" eval="1"/>-->
|
<!-- <field name="perm_create" eval="1"/>-->
|
||||||
<!-- </record>-->
|
<!-- </record>-->
|
||||||
|
|
||||||
<!-- <record model="ir.rule" id="timesheet_users_non_generic_timesheets">-->
|
<!-- <record model="ir.rule" id="timesheet_users_non_generic_timesheets">-->
|
||||||
<!-- <field name="name">timesheet: users: see own tasks</field>-->
|
<!-- <field name="name">timesheet: users: see own tasks</field>-->
|
||||||
<!-- <field name="model_id" ref="analytic.model_account_analytic_line"/>-->
|
<!-- <field name="model_id" ref="analytic.model_account_analytic_line"/>-->
|
||||||
<!-- <field name="domain_force">[-->
|
<!-- <field name="domain_force">[-->
|
||||||
<!-- ('project_id.privacy_visibility','!=','followers'),-->
|
<!-- ('project_id.privacy_visibility','!=','followers'),-->
|
||||||
<!-- ('task_id', '!=', False),-->
|
<!-- ('task_id', '!=', False),-->
|
||||||
<!-- ('project_id', '!=', False),-->
|
<!-- ('project_id', '!=', False),-->
|
||||||
<!-- ('project_id.project_lead', '!=', user.id),-->
|
<!-- ('project_id.project_lead', '!=', user.id),-->
|
||||||
<!-- ('project_id.user_id', '!=', user.id),-->
|
<!-- ('project_id.user_id', '!=', user.id),-->
|
||||||
<!-- ('task_id.is_generic', '=', False),-->
|
<!-- ('task_id.is_generic', '=', False),-->
|
||||||
<!-- ('task_id.user_ids', 'not in', [user.id]),-->
|
<!-- ('task_id.user_ids', 'not in', [user.id]),-->
|
||||||
<!-- ('user_id','not in',[user.id]),-->
|
<!-- ('user_id','not in',[user.id]),-->
|
||||||
<!-- ]</field>-->
|
<!-- ]</field>-->
|
||||||
<!-- <field name="groups" eval="[(4,ref('base.group_user')),(4,ref('project.group_project_user')),(4,ref('project_task_timesheet_extended.group_project_supervisor')),(4,ref('hr_timesheet.group_hr_timesheet_user')),(4,ref('hr_timesheet.group_hr_timesheet_approver'))]"/>-->
|
<!-- <field name="groups" eval="[(4,ref('base.group_user')),(4,ref('project.group_project_user')),(4,ref('project_task_timesheet_extended.group_project_supervisor')),(4,ref('hr_timesheet.group_hr_timesheet_user')),(4,ref('hr_timesheet.group_hr_timesheet_approver'))]"/>-->
|
||||||
<!-- <field name="perm_read" eval="0"/>-->
|
<!-- <field name="perm_read" eval="0"/>-->
|
||||||
<!-- <field name="perm_unlink" eval="1"/>-->
|
<!-- <field name="perm_unlink" eval="1"/>-->
|
||||||
<!-- <field name="perm_write" eval="1"/>-->
|
<!-- <field name="perm_write" eval="1"/>-->
|
||||||
<!-- <field name="perm_create" eval="1"/>-->
|
<!-- <field name="perm_create" eval="1"/>-->
|
||||||
<!-- </record>-->
|
<!-- </record>-->
|
||||||
|
|
||||||
|
|
||||||
<!-- <record model="ir.rule" id="timesheet_team_lead_normal_timesheets">-->
|
<!-- <record model="ir.rule" id="timesheet_team_lead_normal_timesheets">-->
|
||||||
<!-- <field name="name">timesheet: Lead: see related tasks</field>-->
|
<!-- <field name="name">timesheet: Lead: see related tasks</field>-->
|
||||||
<!-- <field name="model_id" ref="analytic.model_account_analytic_line"/>-->
|
<!-- <field name="model_id" ref="analytic.model_account_analytic_line"/>-->
|
||||||
<!-- <field name="domain_force">[-->
|
<!-- <field name="domain_force">[-->
|
||||||
<!-- '&', '&', '&',-->
|
<!-- '&', '&', '&',-->
|
||||||
<!-- ('project_id', '!=', False),-->
|
<!-- ('project_id', '!=', False),-->
|
||||||
<!-- ('project_id.project_lead', '=', user.id),-->
|
<!-- ('project_id.project_lead', '=', user.id),-->
|
||||||
<!-- '|', ('task_id.is_generic', '=', True), ('task_id.is_generic', '=', False),-->
|
<!-- '|', ('task_id.is_generic', '=', True), ('task_id.is_generic', '=', False),-->
|
||||||
<!-- '|', ('task_id.user_ids', 'in', user.id), ('task_id.user_ids', 'not in', user.id)-->
|
<!-- '|', ('task_id.user_ids', 'in', user.id), ('task_id.user_ids', 'not in', user.id)-->
|
||||||
<!-- ]-->
|
<!-- ]-->
|
||||||
<!-- </field>-->
|
<!-- </field>-->
|
||||||
<!-- <field name="perm_read" eval="1"/>-->
|
<!-- <field name="perm_read" eval="1"/>-->
|
||||||
<!-- <field name="perm_write" eval="1"/>-->
|
<!-- <field name="perm_write" eval="1"/>-->
|
||||||
<!-- <field name="perm_create" eval="1"/>-->
|
<!-- <field name="perm_create" eval="1"/>-->
|
||||||
<!-- <field name="perm_unlink" eval="0"/>-->
|
<!-- <field name="perm_unlink" eval="0"/>-->
|
||||||
<!-- </record>-->
|
<!-- </record>-->
|
||||||
</data>
|
</data>
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import {
|
||||||
|
Many2ManyTagsAvatarUserField,
|
||||||
|
many2ManyTagsAvatarUserField,
|
||||||
|
} from "@mail/views/web/fields/many2many_avatar_user_field/many2many_avatar_user_field";
|
||||||
|
import { registry } from "@web/core/registry";
|
||||||
|
|
||||||
|
export class InvolvedAssigneeAvatarUserField extends Many2ManyTagsAvatarUserField {
|
||||||
|
getDomain() {
|
||||||
|
const involved = this.props.record.data.involved_user_ids;
|
||||||
|
const involvedIds = involved?.records?.map((record) => record.resId).filter(Boolean) || [];
|
||||||
|
if (involvedIds.length) {
|
||||||
|
return [["id", "in", involvedIds]];
|
||||||
|
}
|
||||||
|
return [["id", "=", false]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const involvedAssigneeAvatarUserField = {
|
||||||
|
...many2ManyTagsAvatarUserField,
|
||||||
|
component: InvolvedAssigneeAvatarUserField,
|
||||||
|
extractProps(fieldInfo, dynamicInfo) {
|
||||||
|
const props = many2ManyTagsAvatarUserField.extractProps(fieldInfo, dynamicInfo);
|
||||||
|
return {
|
||||||
|
...props,
|
||||||
|
canCreate: false,
|
||||||
|
canQuickCreate: false,
|
||||||
|
canCreateEdit: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
registry.category("fields").add("involved_assignee_avatar_user", involvedAssigneeAvatarUserField);
|
||||||
|
|
@ -41,29 +41,31 @@
|
||||||
[('id', 'in', parent.allowed_employee_ids)]
|
[('id', 'in', parent.allowed_employee_ids)]
|
||||||
</attribute>
|
</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<!-- <xpath expr="//field[@name='timesheet_ids']//field[@name='stage_id']"-->
|
<!-- <xpath expr="//field[@name='timesheet_ids']//field[@name='stage_id']"-->
|
||||||
<!-- position="attributes">-->
|
<!-- position="attributes">-->
|
||||||
<!-- <attribute name="domain">-->
|
<!-- <attribute name="domain">-->
|
||||||
<!-- [('assigned_user_ids.employee_id', '=', employee_id)]-->
|
<!-- [('assigned_user_ids.employee_id', '=', employee_id)]-->
|
||||||
<!-- </attribute>-->
|
<!-- </attribute>-->
|
||||||
<!-- </xpath>-->
|
<!-- </xpath>-->
|
||||||
<xpath expr="//div[hasclass('oe_title','pe-0')]" position="after">
|
<xpath expr="//div[hasclass('oe_title','pe-0')]" position="after">
|
||||||
<group>
|
<group>
|
||||||
<h1><field name="sequence_name" readonly="1"/></h1>
|
<h1><field name="sequence_name" readonly="1"/></h1>
|
||||||
</group>
|
</group>
|
||||||
</xpath>
|
</xpath>
|
||||||
<!-- <xpath expr="//field[@name='user_ids']" position="before">-->
|
<!-- <xpath expr="//field[@name='user_ids']" position="before">-->
|
||||||
<!-- <field name="assigned_team"/>-->
|
<!-- <field name="assigned_team"/>-->
|
||||||
<!-- </xpath>-->
|
<!-- </xpath>-->
|
||||||
|
|
||||||
|
|
||||||
<xpath expr="//field[@name='user_ids']" position="after">
|
<xpath expr="//field[@name='user_ids']" position="after">
|
||||||
<field name="is_generic" readonly="not has_supervisor_access"/>
|
<field name="is_generic" readonly="not has_supervisor_access"/>
|
||||||
<field name="record_paused" invisible="1"/>
|
<field name="record_paused" invisible="1"/>
|
||||||
<field name="model_id" readonly="not has_supervisor_access" options="{'no_open': True}"/>
|
<field name="model_id" readonly="not has_supervisor_access" options="{'no_open': True}"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<!-- <xpath expr="//field[@name='allocated_hours']" position="after">-->
|
<!-- <xpath expr="//field[@name='allocated_hours']" position="after">-->
|
||||||
<!-- <field name="estimated_hours"/>-->
|
<!-- <field name="estimated_hours"/>-->
|
||||||
<!-- <field name="actual_hours"/>-->
|
<!-- <field name="actual_hours"/>-->
|
||||||
<!-- </xpath>-->
|
<!-- </xpath>-->
|
||||||
|
|
||||||
<xpath expr="//sheet/notebook" position="inside">
|
<xpath expr="//sheet/notebook" position="inside">
|
||||||
<page string="Assignees Timelines" invisible="not show_approval_flow">
|
<page string="Assignees Timelines" invisible="not show_approval_flow">
|
||||||
|
|
@ -132,7 +134,7 @@
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
|
|
||||||
<i class="fa fa-exclamation-triangle text-danger me-2" role="img" title="Deadline Issue"/>
|
<i class="fa fa-exclamation-triangle text-danger me-2" role="img" title="Deadline Issue"/>
|
||||||
Based on the timelines, the deadline can't be met
|
Based on the timelines, the deadline can't be met
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
|
|
@ -143,16 +145,17 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <div>-->
|
<!-- <div>-->
|
||||||
<!-- <field name="suggested_deadline" invisible="not suggested_deadline or not show_approval_flow or not timelines_requested"/>-->
|
<!-- <field name="suggested_deadline" invisible="not suggested_deadline or not show_approval_flow or not timelines_requested"/>-->
|
||||||
<!-- <field name="is_suggested_deadline_warning" invisible="1"/>-->
|
<!-- <field name="is_suggested_deadline_warning" invisible="1"/>-->
|
||||||
<!-- <label for="suggested_deadline"-->
|
<!-- <label for="suggested_deadline"-->
|
||||||
<!-- class="text-warning"-->
|
<!-- class="text-warning"-->
|
||||||
<!-- invisible="not is_suggested_deadline_warning">-->
|
<!-- invisible="not is_suggested_deadline_warning">-->
|
||||||
<!-- ⚠ Based on the timelines, the deadline can't be met-->
|
<!-- Based on the timelines, the deadline can't be met -->
|
||||||
<!-- </label>-->
|
<!-- </label>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<field name="estimated_hours" widget="timesheet_uom_no_toggle" readonly="show_approval_flow and timelines_requested"/>
|
<field name="estimated_hours" widget="timesheet_uom_no_toggle"
|
||||||
|
readonly="show_approval_flow and timelines_requested"/>
|
||||||
<field name="actual_hours" widget="timesheet_uom_no_toggle"/>
|
<field name="actual_hours" widget="timesheet_uom_no_toggle"/>
|
||||||
<field name="is_suggested_deadline_warning" />
|
<field name="is_suggested_deadline_warning" />
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
|
|
||||||
<!-- <div name="project_id">-->
|
<!-- <div name="project_id">-->
|
||||||
<!-- <strong>Project —</strong>-->
|
<!-- <strong>Project —</strong>-->
|
||||||
<!-- <t t-if="project_id" t-esc="project_id[1]"/>-->
|
<!-- <t t-if="project_id" t-esc="project_id[1]"/>-->
|
||||||
<!-- <t t-else="">-->
|
<!-- <t t-else="">-->
|
||||||
<!-- <span class="fst-italic text-muted">-->
|
<!-- <span class="fst-italic text-muted">-->
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
|
|
||||||
<!-- <div t-if="show_approval_flow and current_stage_performance">-->
|
<!-- <div t-if="show_approval_flow and current_stage_performance">-->
|
||||||
<!-- <strong>Performance —</strong>-->
|
<!-- <strong>Performance —</strong>-->
|
||||||
<!-- <t t-if="current_stage_performance == 'good'">-->
|
<!-- <t t-if="current_stage_performance == 'good'">-->
|
||||||
<!-- <span class="text-success">-->
|
<!-- <span class="text-success">-->
|
||||||
<!-- <i class="fa fa-smile-o"></i>-->
|
<!-- <i class="fa fa-smile-o"></i>-->
|
||||||
|
|
@ -67,21 +67,21 @@
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
|
|
||||||
<!-- <div t-if="allow_milestones and milestone_id" groups="project.group_project_milestone">-->
|
<!-- <div t-if="allow_milestones and milestone_id" groups="project.group_project_milestone">-->
|
||||||
<!-- <strong>Milestone —</strong>-->
|
<!-- <strong>Milestone —</strong>-->
|
||||||
<!-- <t t-esc="milestone_id[1]"/>-->
|
<!-- <t t-esc="milestone_id[1]"/>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
|
|
||||||
<!-- <div t-if="user_names">-->
|
<!-- <div t-if="user_names">-->
|
||||||
<!-- <strong>Assignees —</strong>-->
|
<!-- <strong>Assignees —</strong>-->
|
||||||
<!-- <t t-esc="user_names"/>-->
|
<!-- <t t-esc="user_names"/>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- <div t-if="partner_id">-->
|
<!-- <div t-if="partner_id">-->
|
||||||
<!-- <strong>Customer —</strong>-->
|
<!-- <strong>Customer —</strong>-->
|
||||||
<!-- <t t-esc="partner_id[1]"/>-->
|
<!-- <t t-esc="partner_id[1]"/>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
|
|
||||||
<!-- <div t-if="show_approval_flow">-->
|
<!-- <div t-if="show_approval_flow">-->
|
||||||
<!-- <strong>Timeline —</strong>-->
|
<!-- <strong>Timeline —</strong>-->
|
||||||
<!-- <t t-esc="estimated_hours"/>-->
|
<!-- <t t-esc="estimated_hours"/>-->
|
||||||
<!-- hours estimated /-->
|
<!-- hours estimated /-->
|
||||||
<!-- <t t-esc="actual_hours"/>-->
|
<!-- <t t-esc="actual_hours"/>-->
|
||||||
|
|
@ -89,7 +89,7 @@
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
|
|
||||||
<!-- <div t-if="project_id" name="allocated_hours">-->
|
<!-- <div t-if="project_id" name="allocated_hours">-->
|
||||||
<!-- <strong>Allocated Time —</strong>-->
|
<!-- <strong>Allocated Time —</strong>-->
|
||||||
<!-- <t t-esc="allocated_hours"/>-->
|
<!-- <t t-esc="allocated_hours"/>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
|
|
||||||
|
|
@ -201,7 +201,7 @@
|
||||||
|
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<div name="project_id">
|
<div name="project_id">
|
||||||
<strong>Project —</strong>
|
<strong>Project —</strong>
|
||||||
<t t-if="project_id" t-esc="project_id[1]"/>
|
<t t-if="project_id" t-esc="project_id[1]"/>
|
||||||
<t t-else="">
|
<t t-else="">
|
||||||
<span class="fst-italic text-muted">
|
<span class="fst-italic text-muted">
|
||||||
|
|
@ -211,7 +211,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div t-if="show_approval_flow and current_stage_performance and not is_completed" class="mt-1">
|
<div t-if="show_approval_flow and current_stage_performance and not is_completed" class="mt-1">
|
||||||
<strong>Performance —</strong>
|
<strong>Performance —</strong>
|
||||||
<t t-if="current_stage_performance == 'good'">
|
<t t-if="current_stage_performance == 'good'">
|
||||||
<span class="text-success">
|
<span class="text-success">
|
||||||
<i class="fa fa-smile-o"></i> Good (Actual < Estimated)
|
<i class="fa fa-smile-o"></i> Good (Actual < Estimated)
|
||||||
|
|
@ -230,24 +230,24 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div t-if="allow_milestones and milestone_id" groups="project.group_project_milestone" class="mt-1">
|
<div t-if="allow_milestones and milestone_id" groups="project.group_project_milestone" class="mt-1">
|
||||||
<strong>Milestone —</strong> <t t-esc="milestone_id[1]"/>
|
<strong>Milestone —</strong> <t t-esc="milestone_id[1]"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div t-if="user_names" class="mt-1">
|
<div t-if="user_names" class="mt-1">
|
||||||
<strong>Assignees —</strong> <t t-esc="user_names"/>
|
<strong>Assignees —</strong> <t t-esc="user_names"/>
|
||||||
</div>
|
</div>
|
||||||
<div t-if="partner_id" class="mt-1">
|
<div t-if="partner_id" class="mt-1">
|
||||||
<strong>Customer —</strong> <t t-esc="partner_id[1]"/>
|
<strong>Customer —</strong> <t t-esc="partner_id[1]"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div t-if="show_approval_flow and not is_completed" class="mt-1">
|
<div t-if="show_approval_flow and not is_completed" class="mt-1">
|
||||||
<strong>Timeline —</strong>
|
<strong>Timeline —</strong>
|
||||||
<t t-esc="estimated_hours"/> hours estimated /
|
<t t-esc="estimated_hours"/> hours estimated /
|
||||||
<t t-esc="actual_hours"/> hours actual
|
<t t-esc="actual_hours"/> hours actual
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div t-if="project_id" name="allocated_hours" class="mt-1">
|
<div t-if="project_id" name="allocated_hours" class="mt-1">
|
||||||
<strong>Allocated Time —</strong> <t t-esc="allocated_hours"/>
|
<strong>Allocated Time —</strong> <t t-esc="allocated_hours"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
|
|
@ -302,7 +302,6 @@
|
||||||
|
|
||||||
<footer class="mt-3">
|
<footer class="mt-3">
|
||||||
<button name="action_unschedule_task" type="object" string="Unschedule" class="btn btn-sm btn-secondary"/>
|
<button name="action_unschedule_task" type="object" string="Unschedule" class="btn btn-sm btn-secondary"/>
|
||||||
<button name="button_update_assignees" type="object" string="Update Assignees" class="btn btn-sm btn-primary"/>
|
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -412,7 +412,7 @@ token.QWEB = token.NT_OFFSET - 1
|
||||||
token.tok_name[token.QWEB] = 'QWEB'
|
token.tok_name[token.QWEB] = 'QWEB'
|
||||||
|
|
||||||
|
|
||||||
# security safe eval opcodes for generated expression validation, used in `_compile_expr`
|
#security safe eval opcodes for generated expression validation, used in `_compile_expr`
|
||||||
_SAFE_QWEB_OPCODES = _EXPR_OPCODES.union(to_opcodes([
|
_SAFE_QWEB_OPCODES = _EXPR_OPCODES.union(to_opcodes([
|
||||||
'MAKE_FUNCTION', 'CALL_FUNCTION', 'CALL_FUNCTION_KW', 'CALL_FUNCTION_EX',
|
'MAKE_FUNCTION', 'CALL_FUNCTION', 'CALL_FUNCTION_KW', 'CALL_FUNCTION_EX',
|
||||||
'CALL_METHOD', 'LOAD_METHOD',
|
'CALL_METHOD', 'LOAD_METHOD',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue