19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:31:56 +01:00
parent a2f74aefd8
commit 4a4d12c333
844 changed files with 212348 additions and 270090 deletions

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="account_analytic_account_view_form_inherit" model="ir.ui.view">
<field name="name">account.analytic.account.form.inherit</field>
<field name="model">account.analytic.account</field>
<field name="inherit_id" ref="analytic.view_account_analytic_account_form"/>
<field eval="40" name="priority"/>
<field name="arch" type="xml">
<xpath expr="//button[@name='%(analytic.account_analytic_line_action)d']" position="before">
<button class="oe_stat_button" type="object" name="action_view_projects"
icon="fa-puzzle-piece" invisible="project_count == 0">
<field string="Projects" name="project_count" widget="statinfo"/>
</button>
</xpath>
<xpath expr="//form" position="attributes">
<attribute name="js_class">analytic_account_form_view</attribute>
</xpath>
</field>
</record>
<record id="view_account_analytic_account_list_inherit" model="ir.ui.view">
<field name="name">account.analytic.account.list.inherit</field>
<field name="model">account.analytic.account</field>
<field name="inherit_id" ref="analytic.view_account_analytic_account_list"/>
<field name="priority">40</field>
<field name="arch" type="xml">
<xpath expr="//list" position="attributes">
<attribute name="js_class">analytic_account_list_view</attribute>
</xpath>
<xpath expr="//list" position="inside">
<field name="project_count" column_invisible="1"/>
</xpath>
</field>
</record>
</odoo>

View file

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!--
Analytic Accounts with project
-->
<record id="account_analytic_account_view_form_inherit" model="ir.ui.view">
<field name="name">account.analytic.account.form.inherit</field>
<field name="model">account.analytic.account</field>
<field name="inherit_id" ref="analytic.view_account_analytic_account_form"/>
<field eval="40" name="priority"/>
<field name="arch" type="xml">
<xpath expr="//button[@name='%(analytic.account_analytic_line_action)d']" position="before">
<button class="oe_stat_button" type="object" name="action_view_projects"
icon="fa-puzzle-piece" attrs="{'invisible': [('project_count', '=', 0)]}">
<field string="Projects" name="project_count" widget="statinfo"/>
</button>
</xpath>
</field>
</record>
</odoo>

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="mail_activity_plan_view_form_project_and_task" model="ir.ui.view">
<field name="name">mail.activity.plan.view.form.project.and.task</field>
<field name="model">mail.activity.plan</field>
<field name="mode">primary</field>
<field name="priority">32</field>
<field name="inherit_id" ref="mail.mail_activity_plan_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='res_model']" position="attributes">
<attribute name="widget">filterable_selection</attribute>
<attribute name="options">{'whitelisted_values': ['project.project', 'project.task']}</attribute>
</xpath>
<xpath expr="//field[@name='template_ids']/list" position="attributes">
<attribute name="editable">bottom</attribute>
</xpath>
</field>
</record>
<record id="mail_activity_plan_action_config_project_task_plan" model="ir.actions.act_window">
<field name="name">Activity Plans</field>
<field name="res_model">mail.activity.plan</field>
<field name="path">project-activity-plans</field>
<field name="view_mode">list,kanban,form</field>
<field name="search_view_id" ref="mail.mail_activity_plan_view_search"/>
<field name="context">{'default_res_model': 'project.task'}</field>
<field name="domain">[('res_model', 'in', ('project.project', 'project.task'))]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a Task Activity Plan
</p>
<p>
Activity plans are used to assign a list of activities in just a few clicks
(e.g. "Progress Report", "Stand-up Meeting", ...)
</p>
</field>
</record>
<record id="mail_activity_plan_action_project_task_view_tree" model="ir.actions.act_window.view">
<field name="sequence">1</field>
<field name="view_mode">list</field>
<field name="view_id" ref="mail.mail_activity_plan_view_tree"/>
<field name="act_window_id" ref="project.mail_activity_plan_action_config_project_task_plan"/>
</record>
<!-- Force the project view that allows to modify the target models of the plan to project or task. -->
<record id="mail_activity_plan_action_project_task_view_form" model="ir.actions.act_window.view">
<field name="sequence">2</field>
<field name="view_mode">form</field>
<field name="view_id" ref="project.mail_activity_plan_view_form_project_and_task"/>
<field name="act_window_id" ref="project.mail_activity_plan_action_config_project_task_plan"/>
</record>
</data>
</odoo>

View file

@ -1,10 +1,10 @@
<?xml version="1.0"?>
<odoo>
<!-- Activity types config -->
<record id="mail_activity_type_action_config_project_types" model="ir.actions.act_window">
<field name="name">Activity Types</field>
<field name="res_model">mail.activity.type</field>
<field name="view_mode">tree,form</field>
<field name="path">project-activity-types</field>
<field name="view_mode">list,kanban,form</field>
<field name="domain">['|', ('res_model', '=', False), ('res_model', '=', 'project.task')]</field>
<field name="context">{'default_res_model': 'project.task'}</field>
<field name="help" type="html">
@ -15,7 +15,4 @@
</p>
</field>
</record>
<menuitem id="project_menu_config_activity_type"
action="mail_activity_type_action_config_project_types"
parent="menu_project_config"/>
</odoo>
</odoo>

View file

@ -1,46 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="project_sharing_access_view_tree" model="ir.ui.view">
<field name="name">project.collaborator.view.tree</field>
<field name="model">project.collaborator</field>
<field name="arch" type="xml">
<tree string="Project Collaborators" create="0">
<field name="partner_id" options="{'no_create': True}"/>
<field name="partner_email"/>
</tree>
</field>
</record>
<record id="project_collaborator_view_search" model="ir.ui.view">
<field name="name">project.collaborator.view.search</field>
<field name="model">project.collaborator</field>
<field name="arch" type="xml">
<search>
<field name="partner_id" />
<field name="project_id" />
<group expand="0" string="Group By">
<filter name="project" string="Project" context="{'group_by': 'project_id'}" />
<filter name="collaborator" string="Collaborator" context="{'group_by': 'partner_id'}" />
</group>
</search>
</field>
</record>
<record id="project_collaborator_action" model="ir.actions.act_window">
<field name="name">Project Collaborators</field>
<field name="res_model">project.collaborator</field>
<field name="view_mode">tree</field>
<field name="domain">[('project_id', '=', active_id)]</field>
<field name="search_view_id" ref="project_collaborator_view_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No collaborators found
</p>
<p>
Collaborate efficiently with key stakeholders by sharing with them the Kanban view of your tasks. Collaborators will be able to edit parts of tasks and send messages.
</p>
</field>
</record>
</odoo>

View file

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<menuitem
name="Project"
id="menu_main_pm"
groups="group_project_manager,group_project_user"
web_icon="project,static/description/icon.png"
sequence="70"
>
<menuitem
name="Projects"
id="menu_projects"
action="open_view_project_all"
sequence="1"
/>
<menuitem
name="Projects"
id="menu_projects_group_stage"
action="open_view_project_all_group_stage"
groups="project.group_project_stages"
sequence="1"
/>
<menuitem
name="Tasks"
id="menu_project_management"
sequence="2"
>
<menuitem
name="My Tasks"
id="menu_project_management_my_tasks"
action="action_view_my_task"
sequence="1"
/>
<menuitem
name="All Tasks"
id="menu_project_management_all_tasks"
action="action_view_all_task"
sequence="2"
/>
</menuitem>
<menuitem
name="Reporting"
id="menu_project_report"
sequence="99"
>
<menuitem
name="Tasks Analysis"
id="menu_project_report_task_analysis"
action="project.action_project_task_user_tree"
sequence="10"
/>
<menuitem
name="Customer Ratings"
id="rating_rating_menu_project"
action="rating_rating_action_project_report"
sequence="51"
/>
</menuitem>
<menuitem
name="Configuration"
id="menu_project_config"
groups="project.group_project_manager"
sequence="100"
>
<menuitem
name="Settings"
id="project_config_settings_menu_action"
action="project_config_settings_action"
groups="base.group_system"
sequence="0"
/>
<menuitem
name="Projects"
id="menu_projects_config_group_stage"
action="open_view_project_all_config_group_stage"
groups="project.group_project_stages"
sequence="5"
/>
<menuitem
name="Projects"
id="menu_projects_config"
action="open_view_project_all_config"
sequence="5"
/>
<menuitem
name="Project Stages"
id="menu_project_config_project_stage"
action="project_project_stage_configure"
groups="project.group_project_stages"
sequence="9"
/>
<menuitem
name="Task Stages"
id="menu_project_config_project"
action="open_task_type_form"
groups="base.group_no_one"
sequence="10"
/>
<menuitem
name="Tags"
id="menu_project_tags_act"
action="project_tags_action"
/>
<menuitem
name="Project Roles"
id="project_menu_config_project_roles"
action="project_roles_action"
/>
<menuitem
name="Activity Types"
id="project_menu_config_activity_type"
action="mail_activity_type_action_config_project_types"
/>
<menuitem
name="Activity Plans"
id="mail_activity_plan_menu_config_project"
action="mail_activity_plan_action_config_project_task_plan"
/>
</menuitem>
</menuitem>
</odoo>

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="project_milestone_view_form" model="ir.ui.view">
<field name="name">project.milestone.view.form</field>
<field name="model">project.milestone</field>
@ -12,18 +11,20 @@
type="action"
class="oe_stat_button"
icon="fa-tasks"
attrs="{'invisible': [('task_count', '=', 0)]}"
context="{'default_project_id': project_id}"
groups="project.group_project_milestone"
close="1"
invisible="not project_allow_milestones"
>
<!-- TODO: Remove me in master -->
<field name="task_count" string="Tasks" widget="statinfo" invisible="1"/>
<div class="o_form_field o_stat_info">
<span class="o_stat_value">
<field name="task_count" widget="statinfo" nolabel="1"/>
<field name="task_count" nolabel="1"/>
<span class="fw-normal"> Tasks</span>
</span>
<span class="o_stat_value" invisible="done_task_count == 0">
<field name="done_task_count" nolabel="1"/>
<span class="fw-normal"> Done</span>
</span>
<span class="o_stat_text">Tasks</span>
</div>
</button>
</div>
@ -41,26 +42,104 @@
</record>
<record id="project_milestone_view_tree" model="ir.ui.view">
<field name="name">project.milestone.view.tree</field>
<field name="name">project.milestone.view.list</field>
<field name="model">project.milestone</field>
<field name="arch" type="xml">
<tree decoration-success="can_be_marked_as_done" decoration-danger="is_deadline_exceeded and not can_be_marked_as_done" decoration-muted="is_reached" editable="bottom" sample="1">
<list decoration-success="can_be_marked_as_done" decoration-danger="is_deadline_exceeded and not can_be_marked_as_done" decoration-muted="is_reached" editable="bottom" sample="1" default_order="sequence, deadline, is_reached desc, name">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="deadline" optional="show"/>
<field name="project_id" column_invisible="1"/>
<field name="is_reached" optional="show"/>
<field name="is_deadline_exceeded" invisible="1"/>
<field name="task_count" invisible="1" />
<field name="can_be_marked_as_done" invisible="1"/>
<button name="action_view_tasks"
type="object"
title="View Tasks"
string="View Tasks"
class="btn btn-link float-end"
attrs="{'invisible': [('task_count', '=', 0)]}"
groups="project.group_project_milestone"
/>
</tree>
invisible="task_count == 0 or not project_allow_milestones"
groups="project.group_project_milestone"/>
</list>
</field>
</record>
<record id="project_milestone_view_kanban" model="ir.ui.view">
<field name="name">project.milestone.view.kanban</field>
<field name="model">project.milestone</field>
<field name="arch" type="xml">
<kanban class="o_kanban_mobile">
<field name="can_be_marked_as_done"/>
<field name="is_deadline_exceeded"/>
<templates>
<t t-name="card">
<div class="d-flex">
<field name="is_reached" widget="task_done_checkmark" class="oe_inline"/>
<strong>
<field name="name" class="oe_inline"/>
<t t-set="badge" t-value="'text-success'" t-if="record.can_be_marked_as_done.raw_value"/>
<t t-set="badge" t-value="'text-danger'" t-if="record.is_deadline_exceeded.raw_value"/>
<t t-if="record.deadline.raw_value">
- <span t-attf-class="{{ badge }}"><field name="deadline"/></span>
</t>
</strong>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<record id="project_milestone_action" model="ir.actions.act_window">
<field name="name">Milestones</field>
<field name="res_model">project.milestone</field>
<field name="view_mode">list,kanban,form</field>
<field name="domain">[('project_id', '=', active_id)]</field>
<field name="context">{'default_project_id': active_id}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No milestones found. Let's create one!
</p>
<p>Track major progress points that must be reached to achieve success.</p>
</field>
</record>
<record id="action_view_project_milestone_list" model="ir.actions.act_window.view">
<field name="act_window_id" ref="project_milestone_action"/>
<field name="sequence" eval="10"/>
<field name="view_mode">list</field>
<field name="view_id" ref="project.project_milestone_view_tree"/>
</record>
<record id="action_view_project_milestone_kanban" model="ir.actions.act_window.view">
<field name="act_window_id" ref="project_milestone_action"/>
<field name="sequence" eval="20"/>
<field name="view_mode">kanban</field>
<field name="view_id" ref="project_milestone_view_kanban"/>
</record>
<record id="action_view_project_milestone_form" model="ir.actions.act_window.view">
<field name="act_window_id" ref="project_milestone_action"/>
<field name="sequence" eval="30"/>
<field name="view_mode">form</field>
<field name="view_id" ref="project_milestone_view_form"/>
</record>
<record id="project_embedded_action_project_milestones" model="ir.embedded.actions">
<field name="parent_res_model">project.project</field>
<field name="sequence">10</field>
<field name="name">Milestones</field>
<field name="parent_action_id" ref="project.act_project_project_2_project_task_all"/>
<field name="action_id" ref="project.project_milestone_action"/>
<field name="domain">[('allow_milestones', '=', True)]</field>
<field name="groups_ids" eval="[(4, ref('project.group_project_milestone'))]" />
</record>
<record id="project_embedded_action_update_milestones" model="ir.embedded.actions">
<field name="parent_res_model">project.project</field>
<field name="sequence">25</field>
<field name="name">Milestones</field>
<field name="parent_action_id" ref="project.project_update_all_action"/>
<field name="action_id" ref="project.project_milestone_action"/>
<field name="domain">[('allow_milestones', '=', True)]</field>
<field name="groups_ids" eval="[(4, ref('project.group_project_milestone'))]" />
</record>
</odoo>

View file

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="portal_layout" name="Portal layout: project menu entry" inherit_id="portal.portal_breadcrumbs" priority="40">
<xpath expr="//ol[hasclass('o_portal_submenu')]" position="inside">
<li t-if="page_name == 'project' or project" class="col-lg-2" t-attf-class="breadcrumb-item #{'active ' if not project else ''}">
<a t-if="project" t-attf-href="/my/projects">Projects</a>
<t t-else="">Projects</t>
</li>
<li t-if="page_name in ['project_task', 'project_subtasks', 'project_recurrent_tasks'] and project" class="breadcrumb-item active">
<a t-if="project" t-attf-href="/my/projects/{{ project.id }}?{{ keep_query() }}"><t t-esc="project.name"/></a>
</li>
<li t-elif="project" t-attf-class="breadcrumb-item #{'active ' if not project else ''} text-truncate col-8 col-lg-10">
<t t-esc="project.name"/>
</li>
<li t-if="page_name == 'task' or (task and not project)" t-attf-class="breadcrumb-item #{'active ' if not task else ''}">
<a t-if="task" t-attf-href="/my/tasks?{{ keep_query() }}">Tasks</a>
<t t-else="">Tasks</t>
</li>
<li t-if="page_name == 'project_subtasks' and task and project" class="breadcrumb-item active">
<a t-attf-href="/my/projects/{{ project.id }}/task/{{ task.id }}"><t t-esc="task.name"/></a>
</li>
<li t-elif="page_name == 'project_recurrent_tasks' and task and project" class="breadcrumb-item active text-truncate">
<a t-attf-href="/my/projects/{{ project.id }}/task/{{ task.id }}?{{ keep_query() }}"><t t-esc="task.name"/></a>
</li>
<li t-elif="task" class="breadcrumb-item active text-break">
<span t-field="task.name"/>
</li>
<li t-if="page_name == 'project_subtasks' or (task and subtask and project)" t-attf-class="breadcrumb-item text-truncate #{'active ' if not subtask else ''}">
<a t-if="subtask" t-attf-href="/my/tasks/{{ task.id }}/subtasks?{{ keep_query() }}">Sub-tasks</a>
<t t-else="">Sub-tasks</t>
</li>
<li t-elif="page_name == 'project_recurrent_tasks' and task and project" t-attf-class="breadcrumb-item text-truncate">
Recurrent tasks
</li>
<li t-if="subtask" class="breadcrumb-item active text-break">
<span t-field="subtask.name"/>
</li>
</xpath>
</template>
<template id="portal_my_home" name="Projects / Tasks" customize_show="True" inherit_id="portal.portal_my_home" priority="40">
<xpath expr="//div[hasclass('o_portal_docs')]" position="before">
<t t-set="portal_service_category_enable" t-value="True"/>
</xpath>
<div id="portal_service_category" position="inside">
<t t-call="portal.portal_docs_entry">
<t t-set="icon" t-value="'/web/static/img/folder.svg'"/>
<t t-set="title">Projects</t>
<t t-set="url" t-value="'/my/projects'"/>
<t t-set="text">Follow the evolution of your projects</t>
<t t-set="placeholder_count" t-value="'project_count'"/>
</t>
<t t-call="portal.portal_docs_entry">
<t t-set="icon" t-value="'/project/static/src/img/tasks.svg'"/>
<t t-set="title">Tasks</t>
<t t-set="url" t-value="'/my/tasks'"/>
<t t-set="text">Follow and comment on tasks in your projects</t>
<t t-set="placeholder_count" t-value="'task_count'"/>
</t>
</div>
</template>
<template id="portal_my_projects" name="My Projects">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="True"/>
<t t-call="portal.portal_searchbar">
<t t-set="title">Projects</t>
</t>
<t t-if="not projects">
<div class="alert alert-warning" role="alert">
There are no projects.
</div>
</t>
<t t-if="projects" t-call="portal.portal_table">
<tbody>
<tr t-foreach="projects" t-as="project">
<td>
<a t-attf-href="/my/projects/#{project.id}"><span t-field="project.name"/></a>
</td>
<td class="text-end">
<t t-out="project.task_count" />
<t t-out="project.label_tasks" />
</td>
</tr>
</tbody>
</t>
</t>
</template>
<template id="portal_my_project" name="My Project">
<t t-call="portal.portal_layout">
<t t-set="title" t-value="project.name"/>
<t t-set="o_portal_fullwidth_alert" groups="project.group_project_user">
<t t-call="portal.portal_back_in_edit_mode">
<t t-set="backend_url" t-value="'/odoo/project.project/%s' % (project.id)"/>
</t>
</t>
<t t-call="portal.portal_searchbar">
<t t-set="title">Tasks</t>
</t>
<t t-if="not grouped_tasks">
<div class="alert alert-warning" role="alert">
There are no tasks.
</div>
</t>
<t t-call="project.portal_tasks_list"/>
</t>
</template>
</odoo>

View file

@ -0,0 +1,325 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="portal_my_tasks_priority_widget_template" name="Priority Widget Template">
<t t-set="priority_label" t-value="priority_labels[task.priority]"/>
<span t-attf-class="o_priority_star d-inline-block fa fa-star#{' text-warning' if task.priority != '0' else '-o opacity-75'} #{classes if classes else ''}" t-att-title="priority_label"/>
<span t-attf-class="o_priority_star d-inline-block fa fa-star#{' text-warning' if task.priority in ['2', '3'] else '-o opacity-75'} #{classes if classes else ''}" t-att-title="priority_label"/>
<span t-attf-class="o_priority_star d-inline-block fa fa-star#{' text-warning' if task.priority == '3' else '-o opacity-75'} #{classes if classes else ''}" t-att-title="priority_label"/>
</template>
<template id="portal_my_tasks_state_widget_template" name="Status Widget Template">
<span
t-att-title="dict(task.fields_get(allfields=['state'])['state']['selection'])[task.state]"
t-attf-class="#{'fa' if task.state in ['1_done','1_canceled','04_waiting_normal'] else 'o_status rounded-circle' } #{'fa-check-circle text-success' if task.state == '1_done' else 'fa-times-circle text-danger' if task.state == '1_canceled' else 'bg-warning' if task.state == '02_changes_requested' else 'bg-success' if task.state == '03_approved' else 'fa-hourglass-o' if task.state == '04_waiting_normal' else ''}"
/>
</template>
<template id="portal_my_tasks_badge_widget_template" name="badge Widget Template">
<span t-attf-class="badge rounded-pill o_tag o_badge_color_#{task.stage_id_color}"
t-attf-title="#{task.stage_id.name}" t-out="task.stage_id.name"
/>
</template>
<template id="portal_tasks_list" name="Tasks List">
<t t-if="grouped_tasks">
<t t-call="portal.portal_table">
<thead>
<tr>
<!-- Allows overrides in modules -->
<t t-set="group_by_in_header_list" t-value="['priority', 'milestone_id']"></t>
<t t-if="multiple_projects" t-set="group_by_in_header_list" t-value="group_by_in_header_list + ['project_id']"/>
<!-- Computes the right colspan once and use it everywhere -->
<t t-set="grouped_tasks_colspan" t-value="5 if groupby in group_by_in_header_list else 6"></t>
<t t-set="grouped_tasks_colspan" t-if="not allow_milestone" t-value="grouped_tasks_colspan - 1"></t>
<t t-set="grouped_tasks_colspan" t-if="not multiple_projects" t-value="grouped_tasks_colspan - 1"/>
<th t-attf-colspan="{{2 if groupby != 'priority' else 1}}"/>
<th>Name</th>
<th>Assignees</th>
<th t-if="groupby != 'project_id' and multiple_projects">Project</th>
<th t-if="groupby != 'milestone_id' and allow_milestone" name="project_portal_milestones">Milestone</th>
<th t-if="groupby != 'state'"/>
<th t-if="groupby != 'stage_id'" class="text-end">Stage</th>
</tr>
</thead>
<t t-foreach="grouped_tasks" t-as="tasks">
<tbody t-if="tasks">
<tr name="grouped_tasks_groupby_columns" t-if="groupby != 'none'" class="table-light">
<th name="groupby_name_col" t-att-colspan="grouped_tasks_colspan">
<t t-if="groupby == 'project_id' and multiple_projects">
<!-- This div is necessary for documents_project_sale -->
<div name="project_name" class="d-flex w-100 align-items-center">
<span t-if="tasks[0].sudo().project_id" t-field="tasks[0].sudo().project_id.name"/>
<span t-else="">No Project</span>
</div>
</t>
<t t-elif="groupby == 'milestone_id'">
<span t-if="tasks[0].sudo().milestone_id and tasks[0].sudo().allow_milestones"
class="text-truncate"
t-field="tasks[0].sudo().milestone_id.name"/>
<span t-else="">No Milestone</span>
</t>
<t t-if="groupby == 'stage_id'">
<!-- This div is necessary for documents_project_sale -->
<div name="stage_name" class="d-flex w-100 align-items-center">
<span class="text-truncate" t-field="tasks[0].sudo().stage_id.name"/>
</div>
</t>
<t t-if="groupby == 'priority'">
<span class="text-truncate" t-field="tasks[0].sudo().priority"/>
</t>
<t t-if="groupby == 'state'">
<span class="text-truncate" t-field="tasks[0].sudo().state"/>
</t>
<t t-if="groupby == 'partner_id'">
<span t-if="tasks[0].sudo().partner_id"
class="text-truncate"
t-field="tasks[0].sudo().partner_id.name"/>
<span t-else="">No Customer</span>
</t>
</th>
<th name="state_col" t-if="groupby != 'state'"/>
<th name="stage_id_col" t-if="groupby != 'stage_id'"/>
</tr>
</tbody>
<tbody t-if="tasks">
<t t-foreach="tasks" t-as="task">
<tr>
<td class="text-start">
#<span t-esc="task.id"/>
</td>
<td t-if="groupby != 'priority'" class="text-end">
<t t-call="project.portal_my_tasks_priority_widget_template"/>
</td>
<td>
<a t-attf-href="/my/#{task_url}/#{task.id}?{{ keep_query() }}"><span t-att-title="task.name" t-field="task.name"/></a>
</td>
<td>
<t t-set="assignees" t-value="task.sudo().user_ids"/>
<div t-if="assignees" class="flex-nowrap ps-3">
<img class="o_avatar rounded" t-attf-src="#{image_data_uri(assignees[:1].avatar_128)}" alt="User"/>
<span class="align-middle" t-att-title="'\n'.join(assignees.mapped('name'))">
<span t-field="assignees[:1].name"/><span t-if="len(assignees) &gt; 1" class="badge ms-1 rounded-pill bg-light"> +<span t-out="len(assignees) - 1"/></span>
</span>
</div>
</td>
<td t-if="groupby != 'project_id' and multiple_projects">
<span title="Current project of the task" t-out="task.project_id.name" />
</td>
<td t-if="groupby != 'milestone_id' and allow_milestone" name="project_portal_milestones">
<t t-if="task.milestone_id and task.allow_milestones">
<span t-esc="task.milestone_id.name" />
</t>
</td>
<td t-if="groupby != 'state'" align="right" class="align-middle">
<t t-call="project.portal_my_tasks_state_widget_template">
<t t-set="path" t-value="'tasks'"/>
</t>
</td>
<td t-if="groupby != 'stage_id'" class="text-end lh-1">
<t t-call="project.portal_my_tasks_badge_widget_template"/>
</td>
</tr>
</t>
</tbody>
</t>
</t>
</t>
</template>
<template id="portal_my_tasks" name="My Tasks">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="True"/>
<t t-call="portal.portal_searchbar">
<t t-set="title">Tasks</t>
</t>
<t t-if="not grouped_tasks">
<div class="alert alert-warning" role="alert">
There are no tasks.
</div>
</t>
<t t-call="project.portal_tasks_list"/>
</t>
</template>
<template id="task_link_preview_front_end" inherit_id="portal.frontend_layout" primary="True">
<xpath expr="//t[@t-if='not_uses_default_logo'][1]" position="before">
<t t-if="preview_object.displayed_image_id">
<meta property="og:image" t-attf-content="/web/image/{{ preview_object.displayed_image_id.id }}/300x200?access_token={{ preview_object.displayed_image_id.generate_access_token()[0] }}"/>
</t>
</xpath>
<xpath expr="//t[@t-if='not_uses_default_logo'][2]" position="before">
<t t-if="preview_object.displayed_image_id">
<meta property="twitter:image" t-attf-content="/web/image/{{ preview_object.displayed_image_id.id }}/300x200?access_token={{ preview_object.displayed_image_id.generate_access_token()[0] }}"/>
</t>
</xpath>
</template>
<template id="task_link_preview_portal_layout" inherit_id="portal.portal_layout" primary="True">
<xpath expr="//t[@t-call='portal.frontend_layout']" position="attributes">
<attribute name="t-call">project.task_link_preview_front_end</attribute>
</xpath>
</template>
<template id="portal_my_task" name="My Task" inherit_id="portal.portal_sidebar" primary="True">
<xpath expr="//t[@t-call='portal.portal_layout']" position="attributes">
<attribute name="t-call">project.task_link_preview_portal_layout</attribute>
</xpath>
<xpath expr="//t[@t-call='project.task_link_preview_portal_layout']" position="before">
<t t-set="o_portal_fullwidth_alert" groups="project.group_project_user">
<t t-call="portal.portal_back_in_edit_mode">
<t t-set="backend_url" t-value="'/odoo/action-project.action_view_my_task/%s' % (task.id)"/>
</t>
</t>
</xpath>
<xpath expr="//div[hasclass('o_portal_sidebar')]" position="inside">
<t t-set="title" t-value="task.name"/>
<div class="row o_project_portal_sidebar">
<t t-call="portal.portal_record_sidebar">
<t t-set="classes" t-value="'col-lg-4 col-xxl-3 d-print-none'"/>
<t t-set="entries">
<div class="d-flex flex-wrap flex-column gap-4">
<div id="task-nav" class="d-flex align-items-center flex-grow-1 p-0" t-ignore="true" role="complementary">
<ul class="nav flex-column">
<li class="nav-item" id="nav-header">
<a class="nav-link p-0" href="#card_header">
Task
</a>
</li>
<li class="nav-item" id="nav-chat">
<a class="nav-link p-0" href="#task_chat">
History
</a>
</li>
</ul>
</div>
<div id="task-links" t-if="task_link_section" class="d-flex align-items-center flex-grow-1 ps-0" t-ignore="true" role="complementary">
<ul class="nav flex-column">
<t t-foreach="task_link_section" t-as="task_link">
<li class="nav-item">
<a class="nav-link p-0" t-att-href="task_link['access_url']" t-att-target="task_link.get('target', '_self')">
<t t-out="task_link['title']"/>
</a>
</li>
</t>
</ul>
</div>
<div t-if="task.user_ids or task.partner_id" class="d-flex flex-column gap-4 mt-3">
<div t-if="task.user_ids">
<h6 class="flex-basis-100"><small class="text-muted">Assignees</small></h6>
<t t-foreach="task.user_ids" t-as="user">
<t t-call="portal.portal_my_contact">
<t t-set="_spacingClass" t-value="'mb-3' if len(task.user_ids) > 1 else ''"/>
<t t-set="_contactAvatar" t-value="image_data_uri(user.avatar_128)"/>
<t t-set="_contactName" t-value="user.name"/>
<div t-out="user" t-options='{"widget": "contact", "fields": ["email", "phone"]}'/>
</t>
</t>
</div>
<div class="col-12 d-flex flex-column" t-if="task.partner_id">
<h6><small class="text-muted">Customer</small></h6>
<t t-if="task.partner_id">
<t t-call="portal.portal_my_contact">
<t t-set="_contactAvatar" t-value="image_data_uri(task.partner_id.avatar_512)"/>
<t t-set="_contactName" t-value="task.partner_id.display_name"/>
<div t-field="task.partner_id" t-options='{"widget": "contact", "fields": ["email", "phone"]}'/>
</t>
</t>
</div>
</div>
</div>
</t>
</t>
<div id="task_content" class="o_portal_content col-12 col-lg-8 col-xxl-9">
<div id="card">
<div id="card_header" data-anchor="true">
<div class="row justify-content-between mb-3">
<div class="col-12 col-md-9">
<div class="d-flex gap-2">
<h3 class="my-0">
<span t-field="task.name"/>
<small class="text-muted d-none d-md-inline">(#<span t-field="task.id"/>)</small>
</h3>
</div>
</div>
<div class="col-auto">
<small class="text-end">Stage:</small>
<t t-call="project.portal_my_tasks_badge_widget_template"/>
</div>
</div>
</div>
<div id="card_body">
<div class="float-end">
<t t-call="project.portal_my_tasks_state_widget_template">
<t t-set="path" t-value="'task'"/>
</t>
</div>
<div class="row mb-4 container">
<div class="col-12 col-md-6 flex-grow-1">
<div t-if="project_accessible"><strong>Project:</strong> <a t-attf-href="/my/projects/#{task.project_id.id}" t-field="task.project_id"/></div>
<div t-else=""><strong>Project:</strong> <a t-field="task.project_id"/></div>
<div t-if="task.milestone_id and task.allow_milestones"><strong>Milestone:</strong> <span t-field="task.milestone_id"/></div>
<div>
<strong>Priority:</strong>
<t t-call="project.portal_my_tasks_priority_widget_template"/>
</div>
<div t-if="task.date_deadline"><strong>Deadline:</strong> <span t-field="task.date_deadline" t-options='{"widget": "datetime"}'/></div>
<div name="portal_my_task_allocated_hours">
<strong t-if="task.allocated_hours > 0">Allocated Time:</strong>
<t t-call="project.portal_my_task_allocated_hours_template"/>
</div>
</div>
<div class="col-12 col-md-6 d-empty-none" name="portal_my_task_second_column"></div>
</div>
<div class="row" t-if="task.description or task.attachment_ids">
<div t-if="not is_html_empty(task.description)" class="mb-4 mb-md-0">
<h5 class="mb-1">Description</h5>
<hr class="mt-1 mb-2"/>
<div class="py-1 px-2 bg-100 small overflow-auto table-responsive mb-4" t-field="task.description"/>
</div>
<div t-if="task.attachment_ids" class="o_project_portal_attachments">
<h5 class="mb-1">Attachments</h5>
<hr class="mt-1 mb-2"/>
<div class="row">
<t t-foreach="task.attachment_ids" t-as="attachment">
<div class="col-md-6">
<ul class="list-unstyled">
<li class="oe_attachments">
<a t-attf-href="/web/content/#{attachment.id}?download=true&amp;access_token=#{attachment.access_token}"
target="_blank" data-no-post-process="" class="d-flex align-items-center rounded bg-light p-2">
<div class='oe_attachment_embedded o_image o_image_small me-2 me-lg-3'
t-att-title="attachment.name" t-att-data-mimetype="attachment.mimetype"
t-attf-data-src="/web/image/#{attachment.id}/50x40?access_token=#{attachment.access_token}">
</div>
<div class='oe_attachment_name text-truncate'><t t-esc='attachment.name'/></div>
</a>
</li>
</ul>
</div>
</t>
</div>
</div>
</div>
</div>
</div>
<hr/>
<div id="task_chat" data-anchor="true">
<h3>Communication history</h3>
<t t-call="portal.message_thread">
<t t-set="token" t-value="task.access_token"/>
</t>
</div>
</div>
</div>
</xpath>
</template>
<template id="portal_my_task_allocated_hours_template">
<strong t-if="task.allocated_hours > 0" class="d-none">Allocated Time:</strong>
<span t-out="task.allocated_hours" t-options='{"widget": "float_time"}'/>
</template>
</odoo>

View file

@ -1,366 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="portal_layout" name="Portal layout: project menu entry" inherit_id="portal.portal_breadcrumbs" priority="40">
<xpath expr="//ol[hasclass('o_portal_submenu')]" position="inside">
<li t-if="page_name == 'project' or project" class="col-lg-2" t-attf-class="breadcrumb-item #{'active ' if not project else ''}">
<a t-if="project" t-attf-href="/my/projects?{{ keep_query() }}">Projects</a>
<t t-else="">Projects</t>
</li>
<li t-if="page_name in ['project_task', 'project_subtasks'] and project" class="breadcrumb-item active">
<a t-if="project" t-attf-href="/my/projects/{{ project.id }}?{{ keep_query() }}"><t t-esc="project.name"/></a>
</li>
<li t-elif="project" t-attf-class="breadcrumb-item #{'active ' if not project else ''} text-truncate col-8 col-lg-10">
<t t-esc="project.name"/>
</li>
<li t-if="page_name == 'task' or (task and not project)" t-attf-class="breadcrumb-item #{'active ' if not task else ''}">
<a t-if="task" t-attf-href="/my/tasks?{{ keep_query() }}">Tasks</a>
<t t-else="">Tasks</t>
</li>
<li t-if="page_name == 'project_subtasks' and task and project" class="breadcrumb-item active">
<a t-attf-href="/my/projects/{{ project.id }}/task/{{ task.id }}?{{ keep_query() }}"><t t-esc="task.name"/></a>
</li>
<li t-elif="task" class="breadcrumb-item active text-truncate">
<span t-field="task.name"/>
</li>
<li t-if="page_name == 'project_subtasks' or (task and subtask and project)" t-attf-class="breadcrumb-item text-truncate #{'active ' if not subtask else ''}">
<a t-if="subtask" t-attf-href="/my/tasks/{{ task.id }}/subtasks?{{ keep_query() }}">Sub-tasks</a>
<t t-else="">Sub-tasks</t>
</li>
<li t-if="subtask" class="breadcrumb-item active text-truncate">
<span t-field="subtask.name"/>
</li>
</xpath>
</template>
<template id="portal_my_tasks_priority_widget_template" name="Priority Widget Template">
<span t-attf-class="o_priority_star fa fa-star#{'' if task.priority == '1' else '-o'}" t-attf-title="Priority: {{'Important' if task.priority == '1' else 'Normal'}}"/>
</template>
<template id="portal_my_tasks_state_widget_template" name="Status Widget Template">
<span t-att-title="task.kanban_state_label" t-attf-class="o_status rounded-circle #{'bg-success' if task.kanban_state == 'done' else 'bg-danger' if task.kanban_state == 'blocked' else ''}"/>
</template>
<template id="portal_my_home" name="Show Projects / Tasks" customize_show="True" inherit_id="portal.portal_my_home" priority="40">
<xpath expr="//div[hasclass('o_portal_docs')]" position="inside">
<t t-call="portal.portal_docs_entry">
<t t-set="title">Projects</t>
<t t-set="url" t-value="'/my/projects'"/>
<t t-set="placeholder_count" t-value="'project_count'"/>
</t>
<t t-call="portal.portal_docs_entry">
<t t-set="title">Tasks</t>
<t t-set="url" t-value="'/my/tasks'"/>
<t t-set="placeholder_count" t-value="'task_count'"/>
</t>
</xpath>
</template>
<template id="portal_my_projects" name="My Projects">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="True"/>
<t t-call="portal.portal_searchbar">
<t t-set="title">Projects</t>
</t>
<t t-if="not projects">
<div class="alert alert-warning mt8" role="alert">
There are no projects.
</div>
</t>
<t t-if="projects" t-call="portal.portal_table">
<tbody>
<tr t-foreach="projects" t-as="project">
<td>
<a t-attf-href="/my/projects/#{project.id}?{{ keep_query() }}"><span t-field="project.name"/></a>
</td>
<td class="text-end">
<t t-out="project.task_count_with_subtasks" />
<t t-out="project.label_tasks" />
</td>
</tr>
</tbody>
</t>
</t>
</template>
<template id="portal_my_project" name="My Project">
<t t-call="portal.portal_layout">
<t t-set="o_portal_fullwidth_alert" groups="project.group_project_user">
<t t-call="portal.portal_back_in_edit_mode">
<t t-set="backend_url" t-value="'/web#model=project.project&amp;id=%s&amp;view_type=kanban' % (project.id)"/>
</t>
</t>
<t t-call="portal.portal_searchbar">
<t t-set="title">Tasks</t>
</t>
<t t-if="not grouped_tasks">
<div class="alert alert-warning mt8" role="alert">
There are no tasks.
</div>
</t>
<t t-call="project.portal_tasks_list"/>
</t>
</template>
<template id="portal_tasks_list" name="Tasks List">
<t t-if="grouped_tasks">
<t t-call="portal.portal_table">
<thead>
<tr>
<!-- Allows overrides in modules -->
<t t-set="group_by_in_header_list" t-value="['priority', 'status', 'project', 'stage', 'milestone']"></t>
<t t-set="number_of_header" t-value="8"></t>
<!-- Computes the right colspan once and use it everywhere -->
<t t-set="grouped_tasks_colspan" t-value="number_of_header - 1 if groupby in group_by_in_header_list else number_of_header"></t>
<t t-set="grouped_tasks_colspan" t-value="grouped_tasks_colspan if allow_milestone else grouped_tasks_colspan - 1"></t>
<th t-attf-colspan="{{2 if groupby != 'priority' else 1}}"/>
<th>Name</th>
<th>Assignees</th>
<th t-if="groupby != 'milestone' and allow_milestone" name="project_portal_milestones">Milestone</th>
<th t-if="groupby != 'status'"/>
<th t-if="groupby != 'project'">Project</th>
<th t-if="groupby != 'stage'" class="text-end">Stage</th>
</tr>
</thead>
<t t-foreach="grouped_tasks" t-as="tasks">
<tbody t-if="tasks">
<tr t-if="not groupby == 'none'" class="table-light">
<th t-if="groupby == 'project'" t-attf-colspan="{{grouped_tasks_colspan}}">
<!-- This div is necessary for documents_project_sale -->
<div name="project_name" class="d-flex w-100 align-items-center">
<span t-field="tasks[0].sudo().project_id.name"/>
</div>
</th>
<th t-if="groupby == 'milestone'" t-attf-colspan="{{grouped_tasks_colspan}}">
<span t-if="tasks[0].sudo().milestone_id and tasks[0].sudo().allow_milestones"
class="text-truncate"
t-field="tasks[0].sudo().milestone_id.name"/>
<span t-else="">No Milestone</span>
</th>
<th t-if="groupby == 'stage'" t-attf-colspan="{{grouped_tasks_colspan}}">
<span class="text-truncate" t-field="tasks[0].sudo().stage_id.name"/></th>
<th t-if="groupby == 'priority'" t-attf-colspan="{{grouped_tasks_colspan}}">
<span class="text-truncate" t-field="tasks[0].sudo().priority"/></th>
<th t-if="groupby == 'status'" t-attf-colspan="{{grouped_tasks_colspan}}">
<span class="text-truncate" t-field="tasks[0].sudo().kanban_state"/></th>
<th t-if="groupby == 'customer'" t-attf-colspan="{{grouped_tasks_colspan}}">
<span t-if="tasks[0].sudo().partner_id"
class="text-truncate"
t-field="tasks[0].sudo().partner_id.name"/>
<span t-else="">No Customer</span>
</th>
</tr>
</tbody>
<tbody t-if="tasks">
<t t-foreach="tasks" t-as="task">
<tr>
<td class="text-start">
#<span t-esc="task.id"/>
</td>
<td t-if="groupby != 'priority'" class="text-end">
<t t-call="project.portal_my_tasks_priority_widget_template"/>
</td>
<td>
<a t-attf-href="/my/#{task_url}/#{task.id}?{{ keep_query() }}"><span t-field="task.name"/></a>
</td>
<td>
<t t-set="assignees" t-value="task.sudo().user_ids"/>
<div t-if="assignees" class="row flex-nowrap ps-3">
<img class="rounded-circle o_portal_contact_img me-2 px-0" t-attf-src="#{image_data_uri(assignees[:1].avatar_128)}" alt="User" style="width: 20px; height: 20px;"/>
<span t-out="'%s%s' % (assignees[:1].name, ' + %s others' % len(assignees[1:]) if len(assignees.user_ids) > 1 else '')" t-att-title="'\n'.join(assignees.mapped('name'))"/>
</div>
</td>
<td t-if="groupby != 'milestone' and allow_milestone" name="project_portal_milestones">
<t t-if="task.milestone_id and task.allow_milestones">
<span t-esc="task.milestone_id.name" />
</t>
</td>
<td t-if="groupby != 'status'" align="right">
<t t-call="project.portal_my_tasks_state_widget_template">
<t t-set="path" t-value="'tasks'"/>
</t>
</td>
<td t-if="groupby != 'project'">
<span class="badge rounded-pill text-bg-info mw-100 text-truncate" title="Current project of the task" t-esc="task.project_id.name" />
</td>
<td t-if="groupby != 'stage'" class="text-end">
<span t-attf-class="badge #{'text-bg-primary' if task.stage_id.fold else 'text-bg-light'}" title="Current stage of the task" t-esc="task.stage_id.name"/>
</td>
</tr>
</t>
</tbody>
</t>
</t>
</t>
</template>
<template id="portal_my_tasks" name="My Tasks">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="True"/>
<t t-call="portal.portal_searchbar">
<t t-set="title">Tasks</t>
</t>
<t t-if="not grouped_tasks">
<div class="alert alert-warning mt8" role="alert">
There are no tasks.
</div>
</t>
<t t-call="project.portal_tasks_list"/>
</t>
</template>
<template id="portal_my_task" name="My Task" inherit_id="portal.portal_sidebar" primary="True">
<xpath expr="//div[hasclass('o_portal_sidebar')]" position="inside">
<t t-set="o_portal_fullwidth_alert" groups="project.group_project_user">
<t t-call="portal.portal_back_in_edit_mode">
<t t-set="backend_url" t-value="'/web#model=project.task&amp;id=%s&amp;action=%s&amp;view_type=form' % (task.id, task.env.ref('project.action_view_all_task').id)"/>
</t>
</t>
<div class="row mt16 o_project_portal_sidebar">
<t t-call="portal.portal_record_sidebar">
<t t-set="classes" t-value="'col-lg-3 d-print-none'"/>
<t t-set="entries">
<ul class="list-group list-group-flush flex-wrap flex-row flex-lg-column">
<li id="task-nav" class="list-group-item ps-0 flex-grow-1 d-flex align-items-center" t-ignore="true" role="complementary">
<ul class="nav flex-column">
<li class="nav-item" id="nav-header">
<a class="nav-link ps-3" href="#card_header" style="max-width: 200px;">
Task
</a>
</li>
<li class="nav-item" id="nav-chat">
<a class="nav-link ps-3" href="#task_chat">
History
</a>
</li>
</ul>
</li>
<li id="task-links" t-if="task_link_section" class="list-group-item ps-0 flex-grow-1 d-flex align-items-center" t-ignore="true" role="complementary">
<ul class="nav flex-column">
<t t-foreach="task_link_section" t-as="task_link">
<li class="nav-item">
<a class="nav-link ps-3" t-att-href="task_link['access_url']">
<t t-out="task_link['title']"/>
</a>
</li>
</t>
</ul>
</li>
<li t-if="task.user_ids or task.partner_id" class="list-group-item flex-grow-1">
<div class="col-12 col-md-12 pb-2" t-if="task.user_ids">
<strong>Assignees</strong>
<t t-foreach="task.user_ids" t-as="user">
<div class="d-flex mb-3 flex-nowrap mt-1">
<img class="rounded-circle o_portal_contact_img" t-att-src="image_data_uri(user.avatar_128)" alt="Contact"/>
<div class="ms-2">
<div t-esc="user" t-options='{"widget": "contact", "fields": ["name"]}'/>
<a t-attf-href="tel:{{user.phone}}" t-if="user.phone"><div t-esc="user" t-options='{"widget": "contact", "fields": ["phone"]}'/></a>
<a t-if="user.email" class="text-break" t-attf-href="mailto:{{user.email}}">
<div t-out="user" t-options='{"widget": "contact", "fields": ["email"]}'/>
</a>
</div>
</div>
</t>
</div>
<div class="col-12 col-md-12 pb-2" t-if="task.partner_id">
<strong>Customer</strong>
<div class="d-flex flex-nowrap mt-1">
<img class="rounded-circle o_portal_contact_img" t-att-src="image_data_uri(task.partner_id.avatar_128)" alt="Contact"/>
<div class="ms-2">
<div t-field="task.partner_id" t-options='{"widget": "contact", "fields": ["name"]}'/>
<a t-attf-href="tel:{{task.partner_id.phone}}" t-if="task.partner_id.phone"><div t-field="task.partner_id" t-options='{"widget": "contact", "fields": ["phone"]}'/></a>
<a t-if="task.partner_id.email" class="text-break" t-attf-href="mailto:{{task.partner_id.email}}">
<div t-field="task.partner_id" t-options='{"widget": "contact", "fields": ["email"]}'/>
</a>
</div>
</div>
</div>
</li>
</ul>
</t>
</t>
<div id="task_content" class="col-lg-9 justify-content-end">
<div id="card" class="card">
<div id="card_header" class="card-header bg-white" data-anchor="true">
<div class="row g-0">
<div class="col-12">
<h5 class="d-flex mb-1 mb-md-0 row">
<div class="col-9">
<t t-call="project.portal_my_tasks_priority_widget_template"/>
<span t-field="task.name" class="text-truncate"/>
<small class="text-muted d-none d-md-inline"> (#<span t-field="task.id"/>)</small>
</div>
<div class="col-3 text-end">
<small class="text-end">Stage:</small>
<span t-field="task.stage_id.name" class=" badge rounded-pill text-bg-info" title="Current stage of this task"/>
</div>
</h5>
</div>
</div>
</div>
<div id="card_body" class="card-body">
<div class="float-end">
<t t-call="project.portal_my_tasks_state_widget_template">
<t t-set="path" t-value="'task'"/>
</t>
</div>
<div class="row mb-4 container">
<div class="col-12 col-md-6">
<div t-if="project_accessible"><strong>Project:</strong> <a t-attf-href="/my/projects/#{task.project_id.id}" t-field="task.project_id"/></div>
<div t-else=""><strong>Project:</strong> <a t-field="task.project_id"/></div>
<div t-if="task.date_deadline"><strong>Deadline:</strong> <span t-field="task.date_deadline" t-options='{"widget": "date"}'/></div>
<div t-if="task.milestone_id and task.allow_milestones"><strong>Milestone:</strong> <span t-field="task.milestone_id"/></div>
<div name="portal_my_task_planned_hours">
<t t-call="project.portal_my_task_planned_hours_template"/>
</div>
</div>
<div class="col-12 col-md-6" name="portal_my_task_second_column"></div>
</div>
<div class="row" t-if="task.description or task.attachment_ids">
<div t-if="not is_html_empty(task.description)" t-attf-class="col-12 col-lg-7 mb-4 mb-md-0 {{'col-lg-7' if task.attachment_ids else 'col-lg-12'}}">
<hr class="mb-1"/>
<div class="d-flex my-2">
<strong>Description</strong>
</div>
<div class="py-1 px-2 bg-100 small table-responsive" t-field="task.description"/>
</div>
<div t-if="task.attachment_ids" t-attf-class="col-12 col-lg-5 o_project_portal_attachments {{'col-lg-5' if task.description else 'col-lg-12'}}">
<hr class="mb-1 d-none d-lg-block"/>
<strong class="d-block mb-2">Attachments</strong>
<div class="row">
<div t-attf-class="col {{'col-lg-6' if not task.description else 'col-lg-12'}}">
<ul class="list-group">
<a class="list-group-item list-group-item-action d-flex align-items-center oe_attachments py-1 px-2" t-foreach='task.attachment_ids' t-as='attachment' t-attf-href="/web/content/#{attachment.id}?download=true&amp;access_token=#{attachment.access_token}" target="_blank" data-no-post-process="">
<div class='oe_attachment_embedded o_image o_image_small me-2 me-lg-3' t-att-title="attachment.name" t-att-data-mimetype="attachment.mimetype" t-attf-data-src="/web/image/#{attachment.id}/50x40?access_token=#{attachment.access_token}"/>
<div class='oe_attachment_name text-truncate'><t t-esc='attachment.name'/></div>
</a>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="mt32" id="task_chat" data-anchor="true">
<h4><strong>Message and communication history</strong></h4>
<t t-call="portal.message_thread">
<t t-set="token" t-value="task.access_token"/>
</t>
</div>
</div>
</div>
</xpath>
</template>
<template id="portal_my_task_planned_hours_template">
<strong>Allocated Hours:</strong> <span t-esc="task.planned_hours" t-options='{"widget": "float_time"}'/>
</template>
</odoo>

View file

@ -1,15 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="project_project_stage_view_tree" model="ir.ui.view">
<field name="name">project.project.stage.view.tree</field>
<field name="name">project.project.stage.view.list</field>
<field name="model">project.project.stage</field>
<field name="arch" type="xml">
<tree editable="bottom" sample="1">
<list editable="bottom" sample="1" delete="0">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="name" placeholder="e.g. To Do"/>
<field name="mail_template_id" optional="hide" context="{'default_model': 'project.project'}"/>
<field name="company_id" optional="hide" groups="base.group_multi_company" options="{'no_create': True}"/>
<field name="color" optional="hide" widget="color_picker"/>
<field name="fold" optional="show"/>
</tree>
</list>
</field>
</record>
@ -19,7 +21,7 @@
<field name="arch" type="xml">
<form>
<group>
<field name="name"/>
<field name="name" placeholder="e.g. To Do"/>
<field name="mail_template_id"/>
<field name="fold"/>
</group>
@ -31,18 +33,20 @@
<field name="name">project.project.stage.view.form</field>
<field name="model">project.project.stage</field>
<field name="arch" type="xml">
<form>
<form delete="0">
<sheet>
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
<h1><field name="name" placeholder="New"/></h1>
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
<h1><field name="name" placeholder="e.g. To Do"/></h1>
<group>
<group>
<field name="active" invisible="1"/>
<field name="mail_template_id" context="{'default_model': 'project.project'}"/>
<field name="color" widget="color_picker"/>
<field name="sequence" groups="base.group_no_one"/>
</group>
<group>
<field name="fold"/>
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}" placeholder="Visible to all"/>
</group>
</group>
</sheet>
@ -54,16 +58,15 @@
<field name="name">project.project.stage.view.kanban</field>
<field name="model">project.project.stage</field>
<field name="arch" type="xml">
<kanban class="o_kanban_mobile" sample="1" quick_create_view="project.project_project_stage_view_form_quick_create">
<field name="name"/>
<field name="mail_template_id"/>
<kanban highlight_color="color" class="o_kanban_mobile" sample="1" quick_create_view="project.project_project_stage_view_form_quick_create">
<templates>
<t t-name="kanban-box">
<div class="o_kanban_record oe_kanban_global_click">
<strong><field name="name"/></strong>
<br/>
<span class="text-muted"><field name="mail_template_id"/></span>
</div>
<t t-name="menu" t-if="!selection_mode" groups="base.group_user">
<field name="color" widget="kanban_color_picker"/>
</t>
<t t-name="card">
<field name="name" class="fw-bolder mb-4"/>
<field name="mail_template_id" class="text-muted mb-2" invisible="not mail_template_id"/>
<field name="company_id" groups="base.group_multi_company" invisible="not company_id" class="mb-2"/>
</t>
</templates>
</kanban>
@ -77,7 +80,11 @@
<search>
<field name="name"/>
<field name="mail_template_id"/>
<field name="company_id" groups="base.group_multi_company"/>
<filter string="Archived" name="archived" domain="[('active', '=', False)]"/>
<group>
<filter string="Company" name="company" context="{'group_by': 'company_id'}" groups="base.group_multi_company"/>
</group>
</search>
</field>
</record>
@ -85,7 +92,8 @@
<record id="project_project_stage_configure" model="ir.actions.act_window">
<field name="name">Project Stages</field>
<field name="res_model">project.project.stage</field>
<field name="view_mode">tree,kanban,form</field>
<field name="path">project-stages</field>
<field name="view_mode">list,kanban,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No stages found. Let's create one!
@ -97,7 +105,7 @@
<record id="project_project_stage_configure_view_tree" model="ir.actions.act_window.view">
<field name="sequence" eval="1"/>
<field name="view_mode">tree</field>
<field name="view_mode">list</field>
<field name="view_id" ref="project_project_stage_view_tree"/>
<field name="act_window_id" ref="project_project_stage_configure"/>
</record>
@ -115,4 +123,13 @@
<field name="view_id" ref="project_project_stage_view_form"/>
<field name="act_window_id" ref="project_project_stage_configure"/>
</record>
<record id="unlink_project_stage_action" model="ir.actions.server">
<field name="name">Delete</field>
<field name="model_id" ref="project.model_project_project_stage"/>
<field name="binding_model_id" ref="project.model_project_project_stage"/>
<field name="binding_view_types">form,list,kanban</field>
<field name="state">code</field>
<field name="code">action = records.unlink_wizard(stage_view=True)</field>
</record>
</odoo>

View file

@ -0,0 +1,730 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="project_project_view_activity" model="ir.ui.view">
<field name="name">project.project.view.activity</field>
<field name="model">project.project</field>
<field name="arch" type="xml">
<activity string="Project" js_class="project_project_activity">
<templates>
<div t-name="activity-box" class="d-flex">
<field name="user_id" widget="many2one_avatar_user"/>
<field name="name" string="Project Name" class="flex-grow-1 o_text_block"/>
</div>
</templates>
</activity>
</field>
</record>
<record id="action_send_mail_project_project" model="ir.actions.act_window">
<field name="name">Send Email</field>
<field name="res_model">mail.compose.message</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="group_ids" eval="[(4, ref('project.group_project_manager'))]"/>
<field name="context">{
'default_composition_mode': 'mass_mail',
}</field>
<field name="binding_model_id" ref="project.model_project_project"/>
<field name="binding_view_types">list,kanban</field>
</record>
<record id="edit_project" model="ir.ui.view">
<field name="name">project.project.form</field>
<field name="model">project.project</field>
<field name="arch" type="xml">
<form string="Project" class="o_form_project_project" js_class="project_project_form">
<field name="company_id" invisible="1"/>
<header>
<button name="action_open_share_project_wizard" string="Share Project" type="object" class="oe_highlight" groups="project.group_project_manager"
invisible="privacy_visibility in ['followers', 'employees'] or is_template" context="{'default_access_mode': 'read'}" data-hotkey="r"/>
<field name="stage_id" widget="statusbar_duration" options="{'clickable': '1', 'fold_field': 'fold'}" groups="project.group_project_stages" domain="[('company_id', 'in', (company_id, False))]"/>
</header>
<sheet string="Project">
<div class="oe_button_box" name="button_box" groups="base.group_user">
<button class="oe_stat_button" type="object" name="action_view_tasks" icon="fa-check">
<div class="o_field_widget o_stat_info">
<field name="label_tasks" readonly="1"/>
<span class="o_stat_value">
<field name="closed_task_count"/> / <field name="task_count"/>
(<field name="task_completion_percentage" widget="percentage" options="{'digits': [1, 0]}"/>)
</span>
</div>
</button>
<button class="oe_stat_button" name="project_update_all_action" type="object" groups="project.group_project_user" context="{'active_id': id}" invisible="is_template">
<field name="last_update_color" invisible="1"/>
<div class="o_stat_info">
<field name="update_count" invisible="1"/>
<field name="last_update_status" readonly="1" widget="status_with_color" status_label="Dashboard" hideStatusName="True"/>
</div>
</button>
</div>
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
<widget name="web_ribbon" title="Template" bg_color="text-bg-info" invisible="not (is_template and active)"/>
<div class="oe_title">
<h1 class="d-flex flex-row">
<field name="is_favorite" nolabel="1" widget="project_is_favorite" class="me-2" options="{'autosave': False}"/>
<field name="name" options="{'line_breaks': False}" widget="text" class="o_text_overflow" placeholder="e.g. Office Party"/>
</h1>
</div>
<group>
<group>
<field name="label_tasks" string="Name of the Tasks" placeholder="e.g. Tasks"/>
<field name="partner_id" widget="res_partner_many2one"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}" placeholder="Visible to all"/>
</group>
<group>
<field name="active" invisible="1"/>
<field name="user_id" string="Project Manager" widget="many2one_avatar_user" readonly="not active" domain="[('share', '=', False)]"/>
<field name="date_start" string="Planned Date" widget="daterange" options='{"end_date_field": "date", "always_range": "1"}' required="date_start or date" />
<field name="date" invisible="1" required="date_start"/>
</group>
</group>
<notebook>
<page name="description" string="Description">
<field name="description" options="{'resizable': false}" placeholder="Project description..."/>
</page>
<page name="settings" string="Settings">
<group>
<group>
<!-- Always display the whole alias in edit mode. It depends in read only -->
<!-- Need to add alias_id in view for getting alias_domain_id by default -->
<field name="alias_id" invisible="1"/>
<label for="alias_name" string="Email Alias" invisible="is_template"/>
<field name="alias_email" widget="email" readonly="1" nolabel="1" groups="!project.group_project_manager" invisible="is_template"/>
<div class="d-inline-flex" groups="project.group_project_manager" invisible="is_template">
<field name="alias_name" placeholder="alias"/>@
<field name="alias_domain_id" placeholder="e.g. mycompany.com"
options="{'no_create': True, 'no_open': True}"/>
</div>
<!-- the alias contact must appear when the user start typing and it must disappear
when the string is deleted. -->
<field name="alias_contact" class="oe_inline" string="Accept Emails From"
invisible="not alias_email"/>
<field name="privacy_visibility" widget="radio"/>
<span colspan="2" class="text-muted o_row ps-1" invisible="access_instruction_message == ''">
<i class="fa fa-lightbulb-o pe-2" title="Info"/><field class="d-inline" name="access_instruction_message" nolabel="1"/>
</span>
<span colspan="2" class="text-muted o_row ps-1" invisible="privacy_visibility_warning == ''">
<i class="fa fa-warning pe-2" title="Warning"/><field class="d-inline" name="privacy_visibility_warning" nolabel="1"/>
</span>
</group>
<group name="analytic" groups="analytic.group_analytic_accounting">
<div class="o_flexible_margin_top o_horizontal_separator text-uppercase fw-bolder small mb-3" colspan="2">analytic</div>
<field name="account_id"/>
</group>
<group name="extra_settings">
</group>
</group>
<group>
<group name="group_tasks_managment" string="Tasks Management" col="1" class="row mt16 o_settings_container">
<div>
<setting class="col-lg-12" id="recurring_tasks_setting" help="Auto-generate tasks for regular activities">
<field name="allow_recurring_tasks"/>
</setting>
<setting class="col-lg-12" id="task_dependencies_setting" help="Determine the order in which to perform tasks">
<field name="allow_task_dependencies"/>
</setting>
<setting class="col-lg-12" id="project_milestone_setting" help="Track major progress points that must be reached to achieve success">
<field name="allow_milestones"/>
</setting>
</div>
</group>
<group name="group_time_managment" string="Time Management" invisible="1" col="1" class="row mt16 o_settings_container"/>
</group>
</page>
</notebook>
</sheet>
<chatter reload_on_follower="True"/>
</form>
</field>
</record>
<record id="view_project_project_filter" model="ir.ui.view">
<field name="name">project.project.select</field>
<field name="model">project.project</field>
<field name="arch" type="xml">
<search string="Search Project">
<field name="name" string="Project"/>
<field name="tag_ids"/>
<field name="user_id" string="Project Manager"/>
<field name="stage_id" groups="project.group_project_stages"/>
<field name="partner_id" string="Customer" filter_domain="[('partner_id', 'child_of', self)]" invisible="context.get('default_is_template')"/>
<field name="activity_user_id" string="Activities of"/>
<field name="activity_type_id" string="Activity type"/>
<filter string="My Projects" name="own_projects" domain="[('user_id', '=', uid)]" invisible="context.get('default_is_template')"/>
<filter string="My Favorites" name="my_projects" domain="[('favorite_user_ids', 'in', uid)]"/>
<filter string="Unassigned" name="unassigned_projects" domain="[('user_id', '=', False)]"/>
<separator invisible="context.get('default_is_template')"/>
<filter string="Late Milestones" name="late_milestones" domain="[('is_milestone_exceeded', '=', True)]" groups="project.group_project_milestone" invisible="context.get('default_is_template') or not context.get('allow_milestones', True)"/>
<separator invisible="context.get('default_is_template')"/>
<filter string="Start Date" name="start_date" date="date_start" end_month="1" end_year="1"/>
<filter string="End Date" name="end_date" date="date" end_month="1" end_year="1"/>
<separator/>
<filter string="Templates" context="{'render_project_templates': True}" name="templates" domain="[('is_template', '=', True)]"
invisible="not context.get('display_milestone_deadline')"/>
<separator/>
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
<separator invisible="1"/>
<filter invisible="1" string="My Activities" name="filter_activities_my"
domain="[('activity_user_id', '=', uid)]"/>
<separator/>
<filter invisible="1" string="Late Activities" name="activities_overdue"
domain="[('my_activity_date_deadline', '&lt;', 'today')]"
help="Show all records whose next activity date is past"/>
<filter invisible="1" string="Today Activities" name="activities_today"
domain="[('my_activity_date_deadline', '=', 'today')]"/>
<filter invisible="1" string="Future Activities" name="activities_upcoming_all"
domain="[('my_activity_date_deadline', '&gt;', 'today')]"/>
<group>
<filter string="Project Manager" name="Manager" context="{'group_by': 'user_id'}"/>
<filter string="Stage" name="groupby_stage" context="{'group_by': 'stage_id'}" groups="project.group_project_stages"/>
<filter string="Status" name="status" context="{'group_by': 'last_update_status'}" invisible="context.get('default_is_template')"/>
<filter string="Tags" name="tags" context="{'group_by': 'tag_ids'}"/>
<filter string="Company" name="company" context="{'group_by': 'company_id'}" groups="base.group_multi_company"/>
</group>
</search>
</field>
</record>
<record id="view_project" model="ir.ui.view">
<field name="name">project.project.list</field>
<field name="model">project.project</field>
<field name="arch" type="xml">
<list decoration-muted="active == False" string="Projects" multi_edit="1" sample="1" default_order="is_favorite desc, sequence, name, id" js_class="project_project_list">
<field name="sequence" column_invisible="True"/>
<field name="message_needaction" column_invisible="True"/>
<field name="active" column_invisible="True"/>
<field name="is_milestone_exceeded" column_invisible="True"/>
<field name="can_mark_milestone_as_done" column_invisible="True"/>
<field name="is_milestone_deadline_exceeded" column_invisible="1"/>
<field name="allow_milestones" column_invisible="True"/>
<field name="is_favorite" string="Favorite" nolabel="1" widget="project_is_favorite" optional="hide"/>
<field name="name" class="fw-bold"/>
<field name="partner_id" optional="show" string="Customer" invisible="is_template"/>
<field name="company_id" optional="show" groups="base.group_multi_company" options="{'no_create': True, 'no_open': True}"/>
<field name="company_id" column_invisible="True"/>
<field name="date_start" string="Planned Date" widget="daterange" options="{'end_date_field': 'date', 'always_range': '1'}" optional="hide"/>
<field name="date" column_invisible="True" />
<field name="milestone_progress" widget="progressbar"
invisible="milestone_progress == 0 or not allow_milestones or is_template"
optional="hide"
/>
<field name="next_milestone_id"
decoration-danger="is_milestone_deadline_exceeded" decoration-success="can_mark_milestone_as_done"
optional="hide"
invisible="not allow_milestones or is_template"
/>
<field name="user_id" optional="show" string="Project Manager" widget="many2one_avatar_user" options="{'no_open':True, 'no_create': True}"/>
<field name="last_update_color" column_invisible="True"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="hide"/>
<field name="last_update_status" string="Status" nolabel="1" width="20px" optional="show" widget="project_state_selection" invisible="is_template"/>
<field name="stage_id_color" column_invisible="1"/>
<field name="stage_id" domain="[('company_id', 'in', (company_id, False))]" optional="show" widget="badge" options="{'no_open': True, 'color_field': 'stage_id_color'}" />
<button string="View Tasks" name="action_view_tasks" type="object"/>
</list>
</field>
</record>
<record id="project_list_view_group_stage" model="ir.ui.view">
<field name="name">project.project.list.group.stage</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="view_project"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<list position="attributes">
<attribute name="default_group_by">stage_id</attribute>
</list>
</field>
</record>
<record id="view_project_config" model="ir.ui.view">
<field name="name">project.project.list.config</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.view_project"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<xpath expr="//list" position="attributes">
<attribute name="default_order">sequence, name, id</attribute>
</xpath>
<field name="sequence" position="attributes">
<attribute name="column_invisible">0</attribute>
<attribute name="widget">handle</attribute>
</field>
</field>
</record>
<record id="view_project_config_group_stage" model="ir.ui.view">
<field name="name">project.project.list.config.group.stage</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="view_project_config"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<list position="attributes">
<attribute name="default_group_by">stage_id</attribute>
</list>
</field>
</record>
<record id="quick_create_project_form" model="ir.ui.view">
<field name="name">project.form.quick_create</field>
<field name="model">project.project</field>
<field name="priority">1000</field>
<field name="arch" type="xml">
<form class="o_form_project_project">
<group>
<field name="name" string="Project Title" placeholder="e.g. Office Party"/>
</group>
</form>
</field>
</record>
<record id="project_view_kanban" model="ir.ui.view">
<field name="name">project.project.kanban</field>
<field name="model">project.project</field>
<field name="arch" type="xml">
<kanban class="o_kanban_mobile">
<templates>
<t t-name="card">
<field name="name" class="fw-bolder" string="Project Name"/>
<div class="d-flex">
<field name="partner_id" string="Contact"/>
<field name="user_id" class="ms-auto" widget="many2one_avatar_user"/>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<record id="project_project_view_form_simplified" model="ir.ui.view">
<field name="name">project.project.view.form.simplified</field>
<field name="model">project.project</field>
<field name="arch" type="xml">
<form string="Project">
<div class="oe_title mb-lg-3 mb-md-2">
<label for="name" string="Name"/>
<h1>
<field name="name" class="o_project_name" placeholder="e.g. Office Party"/>
</h1>
</div>
<field name="user_id" invisible="1"/>
<div class="row o_settings_container"/>
<div name="alias_def" class="mt-2" colspan="2">
<label for="alias_name" string="Create tasks by email:"/>
<span>
<field name="alias_id" invisible="1"/>
<field name="alias_name" placeholder="e.g. office-party"/>@
<field name="alias_domain_id" class="oe_inline" placeholder="e.g. mycompany.com"
options="{'no_create': True, 'no_open': True}"/>
</span>
</div>
</form>
</field>
</record>
<record id="project_project_view_form_simplified_footer" model="ir.ui.view">
<field name="name">project.project.view.form.simplified</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.project_project_view_form_simplified"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<xpath expr="//div[@name='alias_def']" position="after">
<footer>
<button string="Create project" name="action_view_tasks" type="object" class="btn-primary o_open_tasks" data-hotkey="q"/>
<button string="Discard" class="btn-secondary" special="cancel" data-hotkey="x"/>
</footer>
</xpath>
</field>
</record>
<record id="open_create_project" model="ir.actions.act_window">
<field name="name">Create a Project</field>
<field name="res_model">project.project</field>
<field name="view_mode">form</field>
<field name="view_id" ref="project_project_view_form_simplified_footer"/>
<field name="target">new</field>
<field name="context">{"default_allow_billable": 0}</field>
</record>
<record model="ir.ui.view" id="view_project_kanban">
<field name="name">project.project.kanban</field>
<field name="model">project.project</field>
<field name="arch" type="xml">
<kanban highlight_color="color"
class="o_project_kanban"
js_class="project_project_kanban"
on_create="project.open_create_project"
action="action_view_tasks" type="object"
quick_create_view="project.quick_create_project_form"
sample="1"
default_order="is_favorite desc, sequence, name, id"
>
<field name="allow_milestones"/>
<field name="rating_count" />
<field name="show_ratings"/>
<field name="privacy_visibility"/>
<field name="last_update_color"/>
<field name="is_milestone_deadline_exceeded"/>
<field name="can_mark_milestone_as_done"/>
<field name="is_template"/>
<progressbar field="last_update_status" colors='{"on_track": "success", "at_risk": "warning", "off_track": "danger", "on_hold": "info", "done": "purple"}'/>
<field name="sequence" widget="handle"/>
<templates>
<t t-name="menu" groups="base.group_user">
<div class="container">
<div class="row">
<div name="card_menu_view" class="col-6">
<h5 role="menuitem" class="o_kanban_card_manage_title">
<span>View</span>
</h5>
<div role="menuitem">
<a name="action_view_tasks" type="object">Tasks</a>
</div>
<div role="menuitem" groups="project.group_project_milestone" t-if="record.allow_milestones.raw_value and !record.is_template.raw_value">
<a name="action_get_list_view" type="object">Milestones</a>
</div>
</div>
<div class="col-6 o_kanban_manage_reporting" t-if="!record.is_template.raw_value">
<h5 role="menuitem" class="o_kanban_card_manage_title" groups="project.group_project_user">
<span>Reporting</span>
</h5>
<div class="d-inline-block" role="menuitem" groups="project.group_project_user" name="task_analysis">
<a name="action_view_tasks_analysis" type="object">Tasks Analysis</a>
</div>
<div class="d-inline-block" role="menuitem" name="project_burndown_menu" groups="project.group_project_user">
<a name="action_project_task_burndown_chart_report" type="object">Burndown Chart</a>
</div>
</div>
</div>
<div class="o_kanban_card_manage_settings row">
<div role="menuitem" aria-haspopup="true" class="col-6" groups="project.group_project_manager">
<field name="color" widget="kanban_color_picker"/>
</div>
<div role="menuitem" class="col-6" groups="project.group_project_manager">
<a t-if="['portal', 'invited_users'].includes(record.privacy_visibility.raw_value) and !record.is_template.raw_value" class="dropdown-item" role="menuitem" name="action_open_share_project_wizard" type="object">Share Project</a>
<a class="dropdown-item" role="menuitem" name="copy" type="object">Duplicate</a>
<a class="dropdown-item" role="menuitem" type="open">Settings</a>
</div>
<div class="col-12 ps-0" groups="!project.group_project_manager">
<div role="menuitem" class="w-100">
<a class="dropdown-item mx-0" role="menuitem" type="open">View</a>
</div>
</div>
</div>
</div>
</t>
<t t-name="card">
<main class="o_project_kanban_main">
<div class="d-flex align-items-baseline gap-1">
<field name="is_favorite" widget="project_is_favorite" nolabel="1"/>
<span class="text-truncate d-block fs-4 fw-bold" t-att-title="record.display_name.value"><field name="display_name"/></span>
</div>
<div class="o_project_kanban_body min-w-0 pb-4 me-2">
<span name="partner_name" class="text-muted d-flex align-items-baseline" t-if="record.partner_id.value">
<span class="fa fa-user me-2" aria-label="Partner" title="Partner"></span><field class="text-truncate" name="partner_id"/>
</span>
<div t-if="record.date.raw_value or record.date_start.raw_value" class="text-muted d-flex align-items-baseline">
<span class="fa fa-clock-o me-2" title="Dates"/>
<field name="date_start" widget="daterange" options="{'end_date_field': 'date'}"/>
</div>
<div t-if="record.alias_email.value" class="text-muted text-truncate" t-att-title="record.alias_email.value">
<span class="fa fa-envelope-o me-2" aria-label="Domain Alias" title="Domain Alias"></span><field name="alias_email"/>
</div>
<div t-if="record.show_ratings.raw_value and record.rating_count.raw_value &gt; 0" class="d-flex text-muted">
<b class="me-1">
<span class="fa mt4 fa-smile-o fw-bolder text-success" t-if="record.rating_avg.raw_value &gt;= 3.66" title="Average Rating: Happy" role="img" aria-label="Happy face"/>
<span class="fa mt4 fa-meh-o fw-bolder text-warning" t-elif="record.rating_avg.raw_value &gt;= 2.33" title="Average Rating: Neutral" role="img" aria-label="Neutral face"/>
<span class="fa mt4 fa-frown-o fw-bolder text-danger" t-else="" title="Average Rating: Unhappy" role="img" aria-label="Unhappy face"/>
</b>
<t t-if="record.rating_avg.raw_value % 1 == 0">
<field name="rating_avg" nolabel="1" widget="float" digits="[1, 0]"/>
</t>
<t t-else="">
<field name="rating_avg" nolabel="1" widget="float" digits="[1, 1]"/>
</t> / 5
</div>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
</div>
</main>
<footer class="mt-auto pt-0">
<div class="d-flex align-items-center">
<div class="o_project_kanban_boxes d-flex align-items-baseline">
<a class="o_project_kanban_box me-1" name="action_view_tasks" type="object">
<div>
<field name="open_task_count" class="o_value"/>
<field name="label_tasks" class="ms-1"/>
</div>
</a>
<a groups='project.group_project_milestone' t-if="record.allow_milestones and record.allow_milestones.raw_value and record.milestone_count.raw_value &gt; 0"
t-attf-class="d-inline-block small me-1 text-{{ record.is_milestone_deadline_exceeded.raw_value ? 'danger' : (record.can_mark_milestone_as_done.raw_value ? 'success' : 'muted') }}"
role="button"
name="action_get_list_view"
type="object"
t-attf-title="#{record.milestone_count_reached.value} Milestones reached out of #{record.milestone_count.value}"
>
<span class="fa fa-flag me-1"/>
<field name="milestone_count_reached"/>/<field name="milestone_count"/>
</a>
</div>
<field name="activity_ids" widget="kanban_activity" class="ms-2"/>
</div>
<div class="d-flex ms-auto align-items-center">
<field name="user_id" widget="many2one_avatar_user" domain="[('share', '=', False)]" class="me-1"/>
<field t-if="record.last_update_status.value &amp;&amp; widget.editable &amp;&amp; !record.is_template.raw_value" name="last_update_status" widget="project_state_selection"/>
<span t-if="record.last_update_status.value &amp;&amp; !widget.editable" t-att-class="'o_status_bubble mx-0 o_color_bubble_' + record.last_update_color.value" t-att-title="record.last_update_status.value"></span>
</div>
</footer>
</t>
</templates>
</kanban>
</field>
</record>
<record id="project_kanban_view_group_stage" model="ir.ui.view">
<field name="name">project.project.kanban.group.stage</field>
<field name="model">project.project</field>
<field name="mode">primary</field>
<field name="inherit_id" ref="view_project_kanban"/>
<field name="arch" type="xml">
<xpath expr="//kanban" position="attributes">
<attribute name="default_group_by">stage_id</attribute>
</xpath>
</field>
</record>
<record id="view_project_config_kanban" model="ir.ui.view">
<field name="name">project.kanban.inherit.config.project</field>
<field name="model">project.project</field>
<field name="mode">primary</field>
<field name="inherit_id" ref="view_project_kanban"/>
<field name="arch" type="xml">
<xpath expr="//kanban" position="attributes">
<attribute name="action"></attribute>
</xpath>
</field>
</record>
<record id="view_project_config_kanban_group_stage" model="ir.ui.view">
<field name="name">project.kanban.inherit.config.project.group.stage</field>
<field name="model">project.project</field>
<field name="mode">primary</field>
<field name="inherit_id" ref="view_project_config_kanban"/>
<field name="arch" type="xml">
<xpath expr="//kanban" position="attributes">
<attribute name="default_group_by">stage_id</attribute>
</xpath>
</field>
</record>
<record id="view_project_calendar" model="ir.ui.view">
<field name="name">project.project.calendar</field>
<field name="model">project.project</field>
<field name="arch" type="xml">
<calendar
date_start="date_start"
date_stop="date"
string="Projects"
mode="month"
scales="month,year"
event_open_popup="true"
quick_create="0"
color="stage_id_color"
js_class="project_project_calendar">
<field name="partner_id" invisible="not partner_id"/>
<field name="user_id" widget="many2one_avatar_user" invisible="not user_id"/>
<field name="is_favorite" widget="project_is_favorite" nolabel="1" string="Favorite"/>
<field name="stage_id_color" invisible="1"/>
<field name="stage_id" groups="project.group_project_stages" invisible="not stage_id"/>
<field name="last_update_color" invisible="1"/>
<field name="last_update_status" string="Status" widget="status_with_color" invisible="last_update_status == 'to_define'"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}" invisible="not tag_ids"/>
</calendar>
</field>
</record>
<!-- Please update both act_window when modifying one (open_view_project_all or open_view_project_all_group_stage) as one or the other is used in the menu menu_project -->
<record id="open_view_project_all" model="ir.actions.act_window">
<field name="name">Projects</field>
<field name="path">project</field>
<field name="res_model">project.project</field>
<field name="domain">[("is_template", "=", False)]</field>
<field name="context">{'display_milestone_deadline': True}</field>
<field name="view_mode">kanban,list,form</field>
<field name="view_id" ref="view_project_kanban"/>
<field name="search_view_id" ref="view_project_project_filter"/>
<field name="target">current</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No projects found. Let's create one!
</p>
<p>
Create projects to organize your tasks. Define a different workflow for each project.
</p>
</field>
</record>
<!-- Please update both act_window when modifying one (open_view_project_all or open_view_project_all_group_stage) as one or the other is used in the menu menu_project -->
<record id="open_view_project_all_group_stage" model="ir.actions.act_window">
<field name="name">Projects</field>
<field name="res_model">project.project</field>
<field name="context">{'display_milestone_deadline': True}</field>
<field name="domain">[("is_template", "=", False)]</field>
<field name="view_mode">kanban,list,form,calendar,activity</field>
<field name="view_id" ref="view_project_kanban"/>
<field name="search_view_id" ref="view_project_project_filter"/>
<field name="target">main</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No projects found. Let's create one!
</p>
<p>
Projects contain tasks on the same topic, and each has its own dashboard.
</p>
</field>
</record>
<record id="open_view_project_all_group_stage_kanban_view" model="ir.actions.act_window.view">
<field name="sequence" eval="10"/>
<field name="view_mode">kanban</field>
<field name="act_window_id" ref="open_view_project_all_group_stage"/>
<field name="view_id" ref="project_kanban_view_group_stage"/>
</record>
<record id="open_view_project_all_group_stage_tree_view" model="ir.actions.act_window.view">
<field name="sequence" eval="20"/>
<field name="view_mode">list</field>
<field name="act_window_id" ref="open_view_project_all_group_stage"/>
<field name="view_id" ref="project_list_view_group_stage"/>
</record>
<!-- Please update both act_window when modifying one (open_view_project_all_config or open_view_project_all_config_group_stage) as one or the other is used in the menu menu_project_config -->
<record id="open_view_project_all_config" model="ir.actions.act_window">
<field name="name">Projects</field>
<field name="res_model">project.project</field>
<field name="path">project-configuration</field>
<field name="domain">[('is_template', '=', False)]</field>
<field name="view_mode">list,kanban,form</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'list', 'view_id': ref('view_project_config')}),
(0, 0, {'view_mode': 'kanban', 'view_id': ref('view_project_config_kanban')})]"/>
<field name="search_view_id" ref="view_project_project_filter"/>
<field name="context">{'display_milestone_deadline': True}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No projects found. Let's create one!
</p>
<p>
Create projects to organize your tasks and define a different workflow for each project.
</p>
</field>
</record>
<!-- Please update both act_window when modifying one (open_view_project_all_config or open_view_project_all_config_group_stage) as one or the other is used in the menu menu_project_config -->
<record id="open_view_project_all_config_group_stage" model="ir.actions.act_window">
<field name="name">Projects</field>
<field name="res_model">project.project</field>
<field name="domain">[('is_template', '=', False)]</field>
<field name="view_mode">list,kanban,form,calendar,activity</field>
<field name="search_view_id" ref="view_project_project_filter"/>
<field name="context">{'display_milestone_deadline': True}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No projects found. Let's create one!
</p>
<p>
Projects contain tasks on the same topic, and each has its own dashboard.
</p>
</field>
</record>
<record id="open_view_project_all_config_group_stage_tree_action_view" model="ir.actions.act_window.view">
<field name="sequence" eval="10"/>
<field name="view_mode">list</field>
<field name="act_window_id" ref="project.open_view_project_all_config_group_stage"/>
<field name="view_id" ref="view_project_config_group_stage"/>
</record>
<record id="open_view_project_all_config_group_stage_kanban_action_view" model="ir.actions.act_window.view">
<field name="sequence" eval="20"/>
<field name="view_mode">kanban</field>
<field name="act_window_id" ref="project.open_view_project_all_config_group_stage"/>
<field name="view_id" ref="view_project_config_kanban_group_stage"/>
</record>
<record id="project_view_kanban_inherit_project" model="ir.ui.view">
<field name="name">project.kanban.inherit.project</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.view_project_kanban"/>
<field name="priority">200</field>
<field name="arch" type="xml">
<xpath expr="/kanban" position="inside">
<field name="id"/>
</xpath>
<xpath expr="//div[@name='task_analysis']" position="before">
<div role="menuitem" groups="project.group_project_user">
<a name="project_update_all_action" type="object" t-attf-context="{'active_id': #{record.id.raw_value} }">Dashboard</a>
</div>
</xpath>
</field>
</record>
<record id="project_templates_view_form" model="ir.ui.view">
<field name="name">project.project.template.form</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.edit_project"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<form position="attributes">
<attribute name="js_class"></attribute>
</form>
</field>
</record>
<record id="project_templates_view_list" model="ir.ui.view">
<field name="name">project.project.template.list</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.view_project"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<list position="attributes">
<attribute name="js_class"></attribute>
<attribute name="default_order">sequence, is_favorite desc, name, id</attribute>
</list>
<field name="sequence" position="attributes">
<attribute name="column_invisible">False</attribute>
<attribute name="widget">handle</attribute>
</field>
<field name="partner_id" position="replace"/>
<field name="next_milestone_id" position="replace"/>
<field name="milestone_progress" position="replace"/>
</field>
</record>
<record id="project_templates_view_kanban" model="ir.ui.view">
<field name="name">project.project.template.kanban</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.view_project_kanban"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<kanban position="attributes">
<attribute name="js_class"></attribute>
</kanban>
<span name="partner_name" position="replace"/>
</field>
</record>
<record id="action_server_convert_project_to_template" model="ir.actions.server">
<field name="name">Convert to Template</field>
<field name="model_id" ref="project.model_project_project"/>
<field name="binding_model_id" ref="project.model_project_project"/>
<field name="binding_view_types">form</field>
<field name="group_ids" eval="[Command.link(ref('project.group_project_manager'))]"/>
<field name="state">code</field>
<field name="code">
action = record.action_toggle_project_template_mode()
</field>
</record>
</odoo>

View file

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="project_role_view_list" model="ir.ui.view">
<field name="name">project.role.list</field>
<field name="model">project.role</field>
<field name="arch" type="xml">
<list editable="bottom" multi_edit="1">
<field name="sequence" widget="handle"/>
<field name="name" placeholder="e.g. Developer"/>
<field name="color" widget="color_picker" optional="show"/>
</list>
</field>
</record>
<record id="project_role_view_form" model="ir.ui.view">
<field name="name">project.role.form</field>
<field name="model">project.role</field>
<field name="arch" type="xml">
<form>
<sheet>
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
<group>
<field name="active" invisible="1"/>
<field name="name"/>
<field name="color" widget="color_picker"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="project_role_view_kanban" model="ir.ui.view">
<field name="name">project.role.kanban</field>
<field name="model">project.role</field>
<field name="arch" type="xml">
<kanban highlight_color="color">
<templates>
<t t-name="menu">
<a t-if="widget.editable" role="menuitem" type="open" class="dropdown-item">Edit</a>
<a t-if="widget.deletable" role="menuitem" type="delete" class="dropdown-item">Delete</a>
<field name="color" widget="kanban_color_picker"/>
</t>
<t t-name="card">
<field name="name" class="fw-bold fs-4 ms-1"/>
</t>
</templates>
</kanban>
</field>
</record>
<record id="project_role_view_search" model="ir.ui.view">
<field name="name">project.role.search</field>
<field name="model">project.role</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<filter name="archived" string="Archived" domain="[('active', '=', False)]"/>
</search>
</field>
</record>
<record id="project_roles_action" model="ir.actions.act_window">
<field name="name">Project Roles</field>
<field name="res_model">project.role</field>
<field name="view_mode">list,kanban,form</field>
<field name="search_view_id" ref="project_role_view_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No project role found. Let's create one!
</p>
<p>
Assign roles to tasks in your project templates.
When creating a new project from the template, choose who will take on each role.
</p>
</field>
</record>
<record id="project_roles_action_list" model="ir.actions.act_window.view">
<field name="act_window_id" ref="project_roles_action"/>
<field name="view_mode">list</field>
<field name="view_id" ref="project.project_role_view_list"/>
</record>
<record id="project_roles_action_kanban" model="ir.actions.act_window.view">
<field name="act_window_id" ref="project_roles_action"/>
<field name="view_mode">kanban</field>
<field name="view_id" ref="project.project_role_view_kanban"/>
</record>
</odoo>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="project_sharing_portal" name="Project Sharing View Embed">
<t t-call="web.layout">
<t t-set="head_project_sharing">
<script type="text/javascript">
odoo.__session_info__ = <t t-out="json.dumps(session_info)"/>;
// Prevent the menu_service to load anything. In an ideal world, Project Sharing assets would only contain
// what is genuinely necessary, and not the whole backend.
odoo.loadMenusPromise = Promise.resolve();
</script>
<base target="_parent"/>
<t t-call-assets="project.webclient"/>
<t t-call="web.conditional_assets_tests">
<t t-set="ignore_missing_deps" t-value="True"/>
</t>
</t>
<t t-set="head" t-value="head_project_sharing + (head or '')"/>
<t t-set="body_classname" t-value="'o_web_client o_project_sharing'"/>
</t>
</template>
</odoo>

View file

@ -0,0 +1,482 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="project_sharing_quick_create_task_form" model="ir.ui.view">
<field name="name">project.task.form.quick_create</field>
<field name="model">project.task</field>
<field name="priority">999</field>
<field name="group_ids" eval="[(4, ref('base.group_portal')), (4, ref('base.group_user'))]"/>
<field name="arch" type="xml">
<form>
<group>
<field name="name" string="Task Title" placeholder="e.g. Send Invitations"/>
</group>
</form>
</field>
</record>
<record id="project_sharing_project_task_view_kanban" model="ir.ui.view">
<field name="name">project.sharing.project.task.view.kanban</field>
<field name="model">project.task</field>
<field name="priority">999</field>
<field name="group_ids" eval="[(4, ref('base.group_portal')), (4, ref('base.group_user'))]"/>
<field name="arch" type="xml">
<kanban
highlight_color="color"
class="o_kanban_small_column o_kanban_project_tasks"
default_group_by="stage_id"
on_create="quick_create"
quick_create_view="project.project_sharing_quick_create_task_form"
archivable="0"
import="0"
groups_draggable="0"
group_edit="0"
default_order="priority desc, sequence, state, date_deadline asc, id desc"
>
<field name="state"/>
<field name="allow_milestones" />
<field name="has_late_and_unreached_milestone"/>
<progressbar field="state" colors='{"1_done": "success-done", "1_canceled": "danger", "03_approved": "success", "02_changes_requested": "warning", "04_waiting_normal": "info", "01_in_progress": "200"}'/>
<templates>
<t t-name="menu" t-if="!selection_mode">
<div role="separator" class="dropdown-divider"></div>
<field name="color" widget="kanban_color_picker"/>
</t>
<t t-name="card">
<div t-att-class="{'opacity-50': ['1_done', '1_canceled'].includes(record.state.raw_value)}">
<field name="name" class="fw-bolder fs-5" widget="name_with_subtask_count"/>
<field name="project_id" invisible="context.get('default_project_id', False)" required="1" class="text-muted"/>
<span t-if="record.allow_milestones.raw_value and record.milestone_id.raw_value" t-attf-class="{{record.has_late_and_unreached_milestone.raw_value and !record.state.raw_value.startsWith('1_') ? 'text-danger' : ''}}">
<field name="milestone_id" class="text-muted"/>
</span>
<field t-if="record.partner_id.value" name="partner_id" class="text-truncate text-muted d-block"/>
</div>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}" context="{'project_id': project_id}"/>
<field t-if="record.date_deadline.raw_value" name="date_deadline" invisible="state in ['1_done', '1_canceled']" widget="remaining_days"/>
<field t-if="record.displayed_image_id.value" groups="base.group_user" name="displayed_image_id" widget="attachment_image"/>
<footer t-if="!selection_mode">
<field name="priority" class="pt-1" widget="priority"/>
<div class="d-flex ms-auto text-truncate">
<span t-if="record.portal_user_names.raw_value.length > 0" class="pe-2 text-truncate" t-att-title="record.portal_user_names.raw_value">
<t t-set="user_count" t-value="record.portal_user_names.raw_value.split(',').length"/>
<t t-if="user_count > 1"><t t-out="user_count"/> assignees</t>
<field t-else="" name="portal_user_names"/>
</span>
<field name="state" widget="project_task_state_selection" options="{'is_toggle_mode': false}"/>
</div>
</footer>
</t>
</templates>
</kanban>
</field>
</record>
<record id="project_sharing_project_task_view_tree" model="ir.ui.view">
<field name="name">project.sharing.project.task.list</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project_task_view_tree_main_base"/>
<field name="mode">primary</field>
<field name="priority">999</field>
<field name="arch" type="xml">
<list position="attributes">
<attribute name="delete">0</attribute>
<attribute name="import">0</attribute>
<attribute name="group_create">0</attribute>
<attribute name="default_group_by">stage_id</attribute>
</list>
<xpath expr="//field[@widget='res_partner_many2one']" position="attributes">
<attribute name="widget">many2one</attribute>
</xpath>
<field name="user_ids" position="replace">
<field name="portal_user_names" string="Assignees"/>
</field>
<xpath expr="//field[@name='milestone_id']" position="attributes">
<attribute name="column_invisible">not context.get('allow_milestones', True)</attribute>
</xpath>
<field name="partner_id" position="attributes">
<attribute name="column_invisible">0</attribute>
</field>
<field name="is_rotting" position="replace"/>
<field name="rotting_days" position="replace"/>
<field name="stage_id" position="attributes">
<attribute name="widget">selection_badge</attribute>
</field>
</field>
</record>
<record id="project_sharing_project_task_view_form" model="ir.ui.view">
<field name="name">project.sharing.project.task.view.form</field>
<field name="model">project.task</field>
<field name="priority">999</field>
<field name="group_ids" eval="[(4, ref('base.group_portal')), (4, ref('base.group_user'))]"/>
<field name="arch" type="xml">
<form string="Project Sharing: Task" class="o_form_project_tasks">
<header>
<field name="stage_id" widget="statusbar_duration" options="{'clickable': '1', 'fold_field': 'fold'}" invisible="not project_id and not stage_id" />
</header>
<div groups="base.group_user" role="status" class="alert alert-info alert-dismissible rounded-0 fade show d-print-none css_editable_mode_hidden">
<div class="text-center">This is a preview of how the project will look when it's shared with customers and they have editing access.
<a name="action_redirect_to_project_task_form" type="object"><i class="oi oi-arrow-right me-1"/>Back to edit mode</a>
</div>
</div>
<sheet string="Task">
<div class="oe_button_box" name="button_box">
<field name="display_parent_task_button" invisible="1"/>
<field name="recurrence_id" invisible="1" />
<button name="action_project_sharing_view_parent_task" type="object" class="oe_stat_button" icon="fa-tasks" invisible="not display_parent_task_button">
<div class="o_stat_info">
<span class="o_stat_text">Parent Task</span>
</div>
</button>
<button name="action_project_sharing_recurring_tasks" type="object" invisible="not active or not recurrence_id or not allow_recurring_tasks"
class="oe_stat_button" icon="fa-repeat" context="{'default_user_ids': [(6, 0, [uid])]}">
<field name="recurring_count" widget="statinfo" string="Recurring Tasks"/>
</button>
<button name="action_project_sharing_open_subtasks" type="object" class="oe_stat_button" icon="fa-check"
invisible="not id or subtask_count == 0" context="{
'default_user_ids': [(6, 0, [uid])],
'default_project_id': project_id,
}"
>
<field name="subtask_count" widget="statinfo" string="Sub-tasks"/>
<field name="display_in_project" invisible="True"/>
</button>
<button name="action_project_sharing_open_blocking" type="object" invisible="not dependent_tasks_count or not allow_task_dependencies" class="oe_stat_button" icon="fa-tasks">
<div class="o_field_widget o_stat_info">
<span class="o_stat_text">Blocked Tasks</span>
<span class="o_stat_value ">
<field name="dependent_tasks_count" widget="statinfo" nolabel="1" />
</span>
</div>
</button>
</div>
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
<div class="d-flex justify-content-between align-items-center">
<h1 class="d-flex w-100">
<field name="name" options="{'line_breaks': False}" widget="text" class="o_task_name text-truncate w-md-75 w-100 pe-2" placeholder="Task Title..."/>
</h1>
<div class="d-flex justify-content-end align-items-center px-1" invisible="not active or is_template">
<field name="priority" class="h3 pe-2" widget="priority"/>
<field name="state" widget="project_task_state_selection" class="o_task_state_widget" />
</div>
<div class="d-flex justify-content-start align-items-center w-100 w-md-50 w-lg-25" invisible="active and not is_template">
<field name="priority" class="h3 pe-2" widget="priority"/>
<field name="state" widget="project_task_state_selection" class="o_task_state_widget" />
</div>
</div>
<group>
<group>
<field name="project_id" invisible="1"/>
<field name="allow_milestones" invisible="1"/>
<field name="milestone_id"
placeholder="e.g. Product Launch"
invisible="not allow_milestones"
readonly="1"
options="{'no_open': True}"/>
<field name="user_ids" invisible="1" />
<field name="portal_user_names"
string="Assignees"
class="o_task_user_field"/>
</group>
<group>
<field name="active" invisible="1"/>
<field name="parent_id" invisible="1" />
<field name="company_id" invisible="1" />
<field name="state" invisible="1" />
<field name="depend_on_count" invisible="1" />
<field name="allow_task_dependencies" invisible="1" />
<field name="current_user_same_company_partner" invisible="1"/>
<field name="tag_ids" context="{'project_id': project_id}" widget="many2many_tags" options="{'color_field': 'color', 'no_create': True, 'no_edit': True, 'no_edit_color': True}"/>
<field name="partner_id" readonly="not current_user_same_company_partner" options="{'no_open': True, 'no_create': True, 'no_edit': True}" invisible="not project_id"/>
<field name="date_deadline" decoration-danger="date_deadline &lt; current_date and state not in ['1_done', '1_canceled']"/>
<field name="recurring_task" invisible="not allow_recurring_tasks or not active or parent_id" groups="project.group_project_recurring_tasks"/>
<label for="repeat_interval" invisible="not recurring_task or not allow_recurring_tasks" groups="project.group_project_recurring_tasks"/>
<div invisible="not recurring_task or not allow_recurring_tasks" class="d-flex" groups="project.group_project_recurring_tasks" name="repeat_intervals">
<field name="repeat_interval" required="recurring_task"
class="me-2" style="max-width: 2rem !important;" />
<field name="repeat_unit" required="recurring_task"
class="me-2" style="max-width: 4rem !important;" />
<field name="repeat_type" required="recurring_task"
class="me-2" style="max-width: 15rem !important;" />
<field name="repeat_until" invisible="not repeat_type == 'until'" required="repeat_type == 'until'"
class="me-2" />
</div>
</group>
</group>
<notebook>
<page name="description_page" string="Description">
<field name="description" type="html" placeholder="Add details about this task..."
options="{'collaborative': true, 'allowAttachmentCreation': false, 'allowVideo': false}"/>
</page>
<page name="sub_tasks_page" string="Sub-tasks">
<field name="child_ids" context="{
'default_project_id': project_id, 'default_parent_id': id,
'default_milestone_id': allow_milestones and milestone_id, 'default_partner_id': partner_id,
'form_view_ref' : 'project.project_sharing_project_task_view_form',
}">
<list editable="bottom" decoration-muted="state in ['1_done','1_canceled']">
<field name="project_id" column_invisible="True"/>
<field name="display_in_project" column_invisible="True"/>
<field name="state" column_invisible="True"/>
<field name="sequence" widget="handle"/>
<field name="state" widget="project_task_state_selection" nolabel="1" width="20px"/>
<field name="subtask_count" column_invisible="1"/>
<field name="closed_subtask_count" column_invisible="1"/>
<field name="name" widget="name_with_subtask_count"/>
<field name="allow_milestones" column_invisible="True"/>
<field name="milestone_id"
optional="hide"
options="{'no_open': True}"
readonly="1"
column_invisible="not parent.allow_milestones"
invisible="not allow_milestones"/>
<field name="company_id" column_invisible="True"/>
<field name="partner_id" options="{'no_open': True, 'no_create': True, 'no_edit': True}" optional="hide"/>
<field name="user_ids" column_invisible="True" />
<field name="portal_user_names" string="Assignees" optional="show"/>
<field name="date_deadline" decoration-danger="date_deadline &lt; current_date and state not in ['1_done', '1_canceled']" optional="show"/>
<field name="priority" widget="priority" optional="show" nolabel="1" width="20px"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="hide"/>
<field name="stage_id" domain="[('user_id', '=', False), ('project_ids', 'in', [project_id])]"/>
<button name="action_open_task" type="object" title="View Task" string="View Task" class="btn btn-link float-end"
context="{'form_view_ref': 'project.project_sharing_project_task_view_form', 'search_view_ref': 'project.project_sharing_project_task_view_search'}"
invisible="project_id != context.get('active_id')"/>
</list>
</field>
</page>
<page name="task_dependencies" string="Blocked By" invisible="not allow_task_dependencies">
<field name="depend_on_ids" widget="depend_on_ids_one2many" nolabel="1" options="{'link': false}" readonly="1"
context="{
'depend_on_count': depend_on_count,
'list_view_ref': 'project.open_view_blocked_by_list_view',
'search_view_ref': 'project.project_sharing_project_task_view_search',
'search_default_project_id': project_id,
}">
<list editable="bottom" decoration-muted="state in ['1_done','1_canceled']">
<field name="project_id" column_invisible="True"/>
<field name="sequence" widget="handle"/>
<field name="state" widget="project_task_state_selection" nolabel="1" width="20px" readonly="1"/>
<field name="subtask_count" column_invisible="1"/>
<field name="closed_subtask_count" column_invisible="1"/>
<field name="name" widget="name_with_subtask_count"/>
<field name="allow_milestones" column_invisible="True"/>
<field name="milestone_id"
optional="hide"
context="{'default_project_id': project_id}"
invisible="not allow_milestones"
column_invisible="not parent.allow_milestones"
/>
<field name="company_id" column_invisible="True"/>
<field name="partner_id" options="{'no_open': True, 'no_create': True, 'no_edit': True}" optional="hide" invisible="not project_id"/>
<field name="portal_user_names" string="Assignees" optional="show"/>
<field name="date_deadline" decoration-danger="date_deadline &lt; current_date and state not in ['1_done', '1_canceled']" optional="show"/>
<field name="priority" widget="priority" optional="show" nolabel="1" width="20px" readonly="1"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="hide"/>
<field name="stage_id" optional="show"/>
<button name="action_open_task" type="object" title="View Task" string="View Task" class="btn btn-link float-end"
context="{'form_view_ref': 'project.project_sharing_project_task_view_form'}"
invisible="project_id != context.get('active_id')"/>
</list>
</field>
</page>
</notebook>
</sheet>
<!-- field used inside the chatter to know if the portal user is follower or not -->
<field name="message_is_follower" invisible="1"/>
<field name="display_follow_button" invisible="1"/>
<chatter/>
</form>
</field>
</record>
<record id="project_sharing_project_task_view_search" model="ir.ui.view">
<field name="name">project.task.search.form</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_search_form_base"/>
<field name="mode">primary</field>
<field name="priority">999</field>
<field name="arch" type="xml">
<filter name="creation_date_filter" position="after">
<filter string="Deadline" name="date_deadline" date="date_deadline">
<filter name="deadline_future" string="Future" domain="[('date_deadline', '&gt;=', 'today +1d')]"/>
<filter name="deadline_this_week" string="This Week" domain="[
('date_deadline', '&gt;=', '=week_start'),
('date_deadline', '&lt;', '=week_start +1w'),
]"/>
<filter name="deadline_today" string="Today" domain="[
('date_deadline', '&gt;=', 'today'),
('date_deadline', '&lt;', 'today +1d'),
]"/>
<filter name="deadline_past_due" string="Overdue" domain="[('date_deadline', '&lt;', 'today')]"/>
</filter>
</filter>
<filter name="create_date" position="after">
<filter string="Deadline" name="deadline" context="{'group_by': 'date_deadline'}"/>
</filter>
</field>
</record>
<record id="open_view_blocked_by_list_view" model="ir.ui.view">
<field name="name">open.view.blocked.by.list.view</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.open_view_all_tasks_list_view"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<list position="attributes">
<attribute name="default_group_by"/>
</list>
<field name="user_ids" position="attributes">
<attribute name="invisible">1</attribute>
</field>
<field name="user_ids" position="after">
<field name="portal_user_names" string="Assignees" optional="show"/>
</field>
</field>
</record>
<record id="project_sharing_project_task_action" model="ir.actions.act_window">
<field name="name">Project Sharing</field>
<field name="res_model">project.task</field>
<field name="path">project_sharing</field>
<field name="view_mode">kanban,list,form</field>
<field name="search_view_id" ref="project.project_sharing_project_task_view_search"/>
<field name="domain">[('project_id', '=', active_id), ('has_template_ancestor', '=', False)]</field>
<field name="context">{
'default_project_id': active_id,
'active_id_chatter': active_id,
'delete': false,
}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No tasks found. Let's create one!
</p>
<p>
Keep track of the progress of your tasks from creation to completion.<br/>
Collaborate efficiently by chatting in real-time or via email.
</p>
</field>
</record>
<record id="project_sharing_kanban_action_view" model="ir.actions.act_window.view">
<field name="sequence" eval="10"/>
<field name="view_mode">kanban</field>
<field name="act_window_id" ref="project.project_sharing_project_task_action"/>
<field name="view_id" ref="project.project_sharing_project_task_view_kanban"/>
</record>
<record id="project_sharing_tree_action_view" model="ir.actions.act_window.view">
<field name="sequence" eval="20"/>
<field name="view_mode">list</field>
<field name="act_window_id" ref="project.project_sharing_project_task_action"/>
<field name="view_id" ref="project.project_sharing_project_task_view_tree"/>
</record>
<record id="project_sharing_form_action_view" model="ir.actions.act_window.view">
<field name="view_mode">form</field>
<field name="act_window_id" ref="project.project_sharing_project_task_action"/>
<field name="view_id" ref="project.project_sharing_project_task_view_form"/>
</record>
<record id="project_sharing_project_task_action_blocking_tasks" model="ir.actions.act_window">
<field name="name">Blocking</field>
<field name="res_model">project.task</field>
<field name="view_mode">list,kanban,form</field>
<field name="search_view_id" ref="project.project_sharing_project_task_view_search"/>
<field name="domain">[('depend_on_ids', '=', active_id), ('id', '!=', active_id)]</field>
<field name="context">{'default_dependent_ids': active_id}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No tasks found. Let's create one!
</p><p>
To get things done, use activities and status on tasks.<br/>
Chat in real time or by email to collaborate efficiently.
</p>
</field>
</record>
<record id="project_sharing_blocking_tree_action_view" model="ir.actions.act_window.view">
<field name="view_mode">list</field>
<field name="act_window_id" ref="project.project_sharing_project_task_action_blocking_tasks"/>
<field name="view_id" ref="project.project_sharing_project_task_view_tree"/>
</record>
<record id="project_sharing_blocking_kanban_action_view" model="ir.actions.act_window.view">
<field name="view_mode">kanban</field>
<field name="act_window_id" ref="project.project_sharing_project_task_action_blocking_tasks"/>
<field name="view_id" ref="project.project_sharing_project_task_view_kanban"/>
</record>
<record id="project_sharing_blocking_form_action_view" model="ir.actions.act_window.view">
<field name="view_mode">form</field>
<field name="act_window_id" ref="project.project_sharing_project_task_action_blocking_tasks"/>
<field name="view_id" ref="project.project_sharing_project_task_view_form"/>
</record>
<record id="project_sharing_project_task_action_sub_task" model="ir.actions.act_window">
<field name="name">Sub-tasks</field>
<field name="res_model">project.task</field>
<field name="view_mode">list,kanban,form</field>
<field name="search_view_id" ref="project.project_sharing_project_task_view_search"/>
<field name="domain">[('id', 'child_of', active_id), ('id', '!=', active_id)]</field>
<field name="context">{'default_parent_id': active_id}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No tasks found. Let's create one!
</p><p>
To get things done, use activities and status on tasks.<br/>
Chat in real time or by email to collaborate efficiently.
</p>
</field>
</record>
<record id="project_sharing_subtasks_tree_action_view" model="ir.actions.act_window.view">
<field name="view_mode">list</field>
<field name="act_window_id" ref="project.project_sharing_project_task_action_sub_task"/>
<field name="view_id" ref="project.project_sharing_project_task_view_tree"/>
</record>
<record id="project_sharing_subtasks_kanban_action_view" model="ir.actions.act_window.view">
<field name="view_mode">kanban</field>
<field name="act_window_id" ref="project.project_sharing_project_task_action_sub_task"/>
<field name="view_id" ref="project.project_sharing_project_task_view_kanban"/>
</record>
<record id="project_sharing_subtasks_form_action_view" model="ir.actions.act_window.view">
<field name="view_mode">form</field>
<field name="act_window_id" ref="project.project_sharing_project_task_action_sub_task"/>
<field name="view_id" ref="project.project_sharing_project_task_view_form"/>
</record>
<record id="project_sharing_project_task_recurring_tasks_action" model="ir.actions.act_window">
<field name="name">Project Sharing Recurrence</field>
<field name="res_model">project.task</field>
<field name="view_mode">list,kanban,form</field>
<field name="search_view_id" ref="project.project_sharing_project_task_view_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No tasks found. Let's create one!
</p>
<p>
Keep track of the progress of your tasks from creation to completion.<br/>
Collaborate efficiently by chatting in real-time or via email.
</p>
</field>
</record>
<record id="project_sharing_recurring_tasks_tree_action_view" model="ir.actions.act_window.view">
<field name="view_mode">list</field>
<field name="act_window_id" ref="project.project_sharing_project_task_recurring_tasks_action"/>
<field name="view_id" ref="project.project_sharing_project_task_view_tree"/>
</record>
<record id="project_sharing_recurring_tasks_kanban_action_view" model="ir.actions.act_window.view">
<field name="view_mode">kanban</field>
<field name="act_window_id" ref="project.project_sharing_project_task_recurring_tasks_action"/>
<field name="view_id" ref="project.project_sharing_project_task_view_kanban"/>
</record>
<record id="project_sharing_recurring_tasks_form_action_view" model="ir.actions.act_window.view">
<field name="view_mode">form</field>
<field name="act_window_id" ref="project.project_sharing_project_task_recurring_tasks_action"/>
<field name="view_id" ref="project.project_sharing_project_task_view_form"/>
</record>
</odoo>

View file

@ -1,39 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="project_sharing_portal" name="Project Sharing View in Portal">
<t t-call="portal.frontend_layout">
<!-- To add the class on div#wrapwrap to remove the overflow -->
<t t-set="pageName" t-value="'o_project_sharing_container'"/>
<t t-set="no_footer" t-value="true"/>
<t t-call="project.project_sharing"/>
</t>
</template>
<template id="project_sharing" name="Project Sharing View">
<!-- We need to forward the request lang to ensure that the lang set on the portal match the lang delivered -->
<iframe class="flex-grow-1" frameborder="0" t-attf-src="/{{ request.context['lang'] }}/my/projects/{{ str(project_id) }}/project_sharing{{ '?task_id=' + task_id if task_id else '' }}"/>
</template>
<template id="project_sharing_embed" name="Project Sharing View Embed">
<t t-call="web.layout">
<t t-set="head_project_sharing">
<script type="text/javascript">
odoo.__session_info__ = <t t-out="json.dumps(session_info)"/>;
// Prevent the menu_service to load anything. In an ideal world, Project Sharing assets would only contain
// what is genuinely necessary, and not the whole backend.
odoo.loadMenusPromise = Promise.resolve();
</script>
<base target="_parent"/>
<t t-call-assets="web.assets_common" t-js="false"/>
<t t-call-assets="project.webclient" t-js="false"/>
<t t-call-assets="web.assets_common" t-css="false"/>
<t t-call-assets="project.webclient" t-css="false"/>
<t t-call="web.conditional_assets_tests"/>
</t>
<t t-set="head" t-value="head_project_sharing + (head or '')"/>
<t t-set="body_classname" t-value="'o_web_client o_project_sharing'"/>
</t>
</template>
</odoo>

View file

@ -1,371 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="project_sharing_quick_create_task_form" model="ir.ui.view">
<field name="name">project.task.form.quick_create</field>
<field name="model">project.task</field>
<field name="priority">999</field>
<field name="groups_id" eval="[(4, ref('base.group_portal')), (4, ref('base.group_user'))]"/>
<field name="arch" type="xml">
<form>
<group>
<field name="name" string="Task Title" placeholder="e.g. Send Invitations"/>
</group>
</form>
</field>
</record>
<record id="project_sharing_project_task_view_kanban" model="ir.ui.view">
<field name="name">project.sharing.project.task.view.kanban</field>
<field name="model">project.task</field>
<field name="priority">999</field>
<field name="groups_id" eval="[(4, ref('base.group_portal')), (4, ref('base.group_user'))]"/>
<field name="arch" type="xml">
<kanban
class="o_kanban_small_column o_kanban_project_tasks"
default_group_by="stage_id"
on_create="quick_create"
quick_create_view="project.project_sharing_quick_create_task_form"
archivable="0"
import="0"
>
<field name="color"/>
<field name="priority"/>
<field name="stage_id" options='{"group_by_tooltip": {"description": "Description"}}'/>
<field name="portal_user_names"/>
<field name="partner_id"/>
<field name="sequence"/>
<field name="is_closed" force_save="0"/>
<field name="partner_is_company"/>
<field name="displayed_image_id"/>
<field name="active"/>
<field name="allow_subtasks"/>
<field name="child_text"/>
<field name="legend_blocked" invisible="1" force_save="0"/>
<field name="legend_normal" invisible="1" force_save="0"/>
<field name="legend_done" invisible="1" force_save="0"/>
<field name="allow_milestones" />
<field name="has_late_and_unreached_milestone"/>
<progressbar field="kanban_state" colors='{"done": "success", "blocked": "danger", "normal": "200"}'/>
<templates>
<t t-name="kanban-box">
<div t-attf-class="{{!selection_mode ? 'oe_kanban_color_' + kanban_getcolor(record.color.raw_value) : ''}} oe_kanban_card oe_kanban_global_click">
<div class="oe_kanban_content">
<div class="o_kanban_record_top">
<div class="o_kanban_record_headings">
<strong class="o_kanban_record_title">
<s t-if="!record.active.raw_value"><field name="name" widget="name_with_subtask_count"/></s>
<t t-else=""><field name="name" widget="name_with_subtask_count"/></t>
</strong>
<span invisible="context.get('default_project_id', False)"><br/><field name="project_id" required="1"/></span>
<span t-if="record.allow_milestones.raw_value and record.milestone_id.raw_value" t-attf-class="{{record.has_late_and_unreached_milestone.raw_value ? 'text-danger' : ''}}">
<br/>
<field name="milestone_id" />
</span>
<br />
<t t-if="record.partner_id.value">
<span t-if="!record.partner_is_company.raw_value" t-attf-title="#{record.commercial_partner_id.value}">
<field name="commercial_partner_id" class="text-truncate d-block"/>
</span>
<span t-else="" t-attf-title="#{record.partner_id.value}">
<field name="partner_id" class="text-truncate d-block"/>
</span>
</t>
<t t-else="record.email_from.raw_value"><span><field name="email_from"/></span></t>
</div>
<div class="o_dropdown_kanban dropdown" t-if="!selection_mode">
<a role="button" class="dropdown-toggle o-no-caret btn" data-bs-toggle="dropdown" data-bs-display="static" href="#" aria-label="Dropdown menu" title="Dropdown menu">
<span class="fa fa-ellipsis-v"/>
</a>
<div class="dropdown-menu" role="menu">
<a t-if="widget.editable" role="menuitem" type="edit" class="dropdown-item">Edit</a>
<div invisible="1" role="separator" class="dropdown-divider"></div>
<ul invisible="1" class="oe_kanban_colorpicker" data-field="color"/>
</div>
</div>
</div>
<div class="o_kanban_record_body">
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}" context="{'project_id': project_id}"/>
<div t-if="record.date_deadline.raw_value" name="date_deadline" attrs="{'invisible': [('is_closed', '=', True)]}">
<field name="date_deadline" widget="remaining_days"/>
</div>
<div t-if="record.displayed_image_id.value" groups="base.group_user">
<field name="displayed_image_id" widget="attachment_image"/>
</div>
</div>
<div class="o_kanban_record_bottom" t-if="!selection_mode">
<div class="oe_kanban_bottom_left">
<field name="priority" widget="priority"/>
</div>
<div class="oe_kanban_bottom_right" t-if="!selection_mode">
<span t-if="record.portal_user_names.raw_value.length > 0" class="pe-2" t-att-title="record.portal_user_names.raw_value">
<t t-set="user_count" t-value="record.portal_user_names.raw_value.split(',').length"/>
<t t-out="user_count"/>
<t t-if="user_count > 1"> assignees</t>
<t t-else=""> assignee</t>
</span>
<field name="kanban_state" widget="state_selection"/>
</div>
</div>
</div>
<div class="clearfix"></div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<record id="project_sharing_project_task_view_tree" model="ir.ui.view">
<field name="name">project.sharing.project.task.tree</field>
<field name="model">project.task</field>
<field name="priority">999</field>
<field name="arch" type="xml">
<tree string="Tasks" sample="1" delete="0" import="0">
<field name="is_closed" invisible="1" />
<field name="sequence" invisible="1" readonly="1"/>
<field name="allow_milestones" invisible="1"/>
<field name="priority" widget="priority" optional="show" nolabel="1"/>
<field name="child_text" invisible="1"/>
<field name="allow_subtasks" invisible="1" />
<field name="name" widget="name_with_subtask_count"/>
<field name="company_id" invisible="1"/>
<field name="milestone_id" attrs="{'column_invisible': [('allow_milestones', '=', False)]}"/>
<field name="partner_id" optional="hide"/>
<field name="portal_user_names" string="Assignees" optional="show"/>
<field name="date_deadline" optional="hide" widget="remaining_days" attrs="{'invisible': [('is_closed', '=', True)]}"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="show"/>
<field name="kanban_state" widget="state_selection" options="{'hide_label': True}" nolabel="1" optional="show"/>
<field name="legend_blocked" invisible="1"/>
<field name="legend_normal" invisible="1"/>
<field name="legend_done" invisible="1"/>
<field name="stage_id" invisible="context.get('set_visible',False)" optional="show"/>
</tree>
</field>
</record>
<record id="project_sharing_project_task_view_form" model="ir.ui.view">
<field name="name">project.sharing.project.task.view.form</field>
<field name="model">project.task</field>
<field name="priority">999</field>
<field name="groups_id" eval="[(4, ref('base.group_portal')), (4, ref('base.group_user'))]"/>
<field name="arch" type="xml">
<form string="Project Sharing: Task" class="o_form_project_tasks">
<header>
<button name="action_assign_to_me" string="Assign to Me" type="object" class="oe_highlight"
attrs="{'invisible' : &quot;[('user_ids', 'in', [uid])]&quot;}" data-hotkey="q" groups="base.group_user"/>
<button name="action_unassign_me" string="Unassign Me" type="object" class="oe_highlight"
attrs="{'invisible' : &quot;[('user_ids', 'not in', [uid])]&quot;}" data-hotkey="q"/>
<field name="stage_id" widget="statusbar" options="{'clickable': '1', 'fold_field': 'fold'}" attrs="{'invisible': [('project_id', '=', False), ('stage_id', '=', False)]}" />
</header>
<sheet string="Task">
<div class="oe_button_box" name="button_box">
<field name="display_parent_task_button" invisible="1"/>
<button name="action_project_sharing_view_parent_task" type="object" class="oe_stat_button" icon="fa-tasks" string="Parent Task" attrs="{'invisible': [('display_parent_task_button', '=', False)]}"/>
<button name="action_project_sharing_open_subtasks" type="object" class="oe_stat_button" icon="fa-tasks"
attrs="{'invisible' : ['|', '|', ('allow_subtasks', '=', False), ('id', '=', False), ('subtask_count', '=', 0)]}" context="{'default_user_ids': [(6, 0, [uid])]}">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
<field name="subtask_count" widget="statinfo" nolabel="1"/>
</span>
<span class="o_stat_text">Sub-tasks</span>
</div>
</button>
</div>
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
<div class="oe_title pe-0">
<h1 class="d-flex flex-row justify-content-between">
<field name="priority" widget="priority" class="me-3"/>
<field name="name" class="o_task_name text-truncate" placeholder="Task Title..."/>
<field name="kanban_state" widget="state_selection" class="ms-auto"/>
<field name="legend_blocked" invisible="1"/>
<field name="legend_normal" invisible="1"/>
<field name="legend_done" invisible="1"/>
</h1>
</div>
<group>
<group>
<field name="project_id" invisible="1"/>
<field name="display_project_id" string="Project" invisible="1"/>
<field name="allow_milestones" invisible="1"/>
<field name="milestone_id"
placeholder="e.g. Product Launch"
context="{'default_project_id': project_id if not parent_id or not display_project_id else display_project_id}"
attrs="{'invisible': [('allow_milestones', '=', False)]}"
options="{'no_open': True, 'no_create': True, 'no_edit': True}"
/>
<field name="user_ids" invisible="1" />
<field name="portal_user_names"
string="Assignees"
class="o_task_user_field"/>
</group>
<group>
<field name="active" invisible="1"/>
<field name="parent_id" invisible="1" />
<field name="company_id" invisible="1" />
<field name="is_closed" invisible="1" />
<field name="allow_subtasks" invisible="1" />
<field name="partner_id" options="{'no_open': True, 'no_create': True, 'no_edit': True}"/>
<field name="date_deadline" attrs="{'invisible': [('is_closed', '=', True)]}"/>
<field name="tag_ids" context="{'project_id': project_id}" widget="many2many_tags" options="{'color_field': 'color', 'no_create_edit': True, 'no_edit_color': True}"/>
</group>
</group>
<notebook>
<page name="description_page" string="Description">
<field name="description" type="html" options="{'collaborative': true}"/>
</page>
<page name="sub_tasks_page" string="Sub-tasks" attrs="{'invisible': [('allow_subtasks', '=', False)]}">
<field name="child_ids" context="{'default_project_id': project_id if not parent_id or not display_project_id else display_project_id, 'default_parent_id': id, 'default_partner_id': partner_id, 'form_view_ref' : 'project.project_sharing_project_task_view_form'}">
<tree editable="bottom">
<field name="project_id" invisible="1"/>
<field name="is_closed" invisible="1"/>
<field name="sequence" widget="handle"/>
<field name="priority" widget="priority" optional="show" nolabel="1"/>
<field name="name"/>
<field name="display_project_id" string="Project" optional="hide" invisible="1"/>
<field name="allow_milestones" invisible="1"/>
<field name="milestone_id"
optional="hide"
context="{'default_project_id': display_project_id or project_id}"
attrs="{'invisible': [('allow_milestones', '=', False)], 'column_invisible': [('parent.allow_milestones', '=', False)]}"
options="{'no_open': True, 'no_create': True, 'no_edit': True}"
/>
<field name="company_id" invisible="1"/>
<field name="partner_id" options="{'no_open': True, 'no_create': True, 'no_edit': True}" optional="hide"/>
<field name="user_ids" invisible="1" />
<field name="portal_user_names" string="Assignees" optional="show"/>
<field name="date_deadline" attrs="{'invisible': [('is_closed', '=', True)]}" optional="show"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="hide"/>
<field name="kanban_state" widget="state_selection" options="{'hide_label': True}" nolabel="1" optional="show"/>
<field name="stage_id" domain="[('user_id', '=', False), ('project_ids', 'in', [project_id])]"/>
<button name="action_open_task" type="object" title="View Task" string="View Task" class="btn btn-link float-end"
context="{'form_view_ref': 'project.project_sharing_project_task_view_form'}"
attrs="{'invisible': &quot;[('project_id', '!=', False), ('project_id', '!=', active_id)]&quot;}"/>
</tree>
</field>
</page>
</notebook>
</sheet>
<div class="oe_chatter"/>
</form>
</field>
</record>
<record id="project_sharing_project_task_view_search" model="ir.ui.view">
<field name="name">project.task.search.form</field>
<field name="model">project.task</field>
<field name="priority">999</field>
<field name="arch" type="xml">
<search string="Tasks">
<field name="name" string="Task"/>
<field name="tag_ids"/>
<field name="portal_user_names" string="Assignees"/>
<field string="Project" name="display_project_id"/>
<field name="milestone_id" groups="project.group_project_milestone"/>
<field name="stage_id"/>
<field name="partner_id" operator="child_of"/>
<filter string="Unassigned" name="unassigned" domain="[('user_ids', '=', False)]"/>
<separator/>
<filter string="High Priority" name="high_priority" domain="[('priority', '=', 1)]"/>
<filter string="Low Priority" name="low_priority" domain="[('priority', '=', 0)]"/>
<separator/>
<filter string="Late Tasks" name="late" domain="[('date_deadline', '&lt;', context_today().strftime('%Y-%m-%d')), ('is_closed', '=', False)]"/>
<filter string="Tasks Due Today" name="tasks_due_today" domain="[('date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
<filter string="Late Milestones" name="late_milestone" domain="[('is_closed', '=', False), ('has_late_and_unreached_milestone', '=', True)]" groups="project.group_project_milestone"/>
<separator/>
<filter string="Open Tasks" name="open_tasks" domain="[('is_closed', '=', False)]"/>
<filter string="Closed Tasks" name="closed_tasks" domain="[('is_closed', '=', True)]"/>
<filter string="Closed Last 7 Days" name="closed_last_7_days" domain="[('is_closed', '=', True), ('date_last_stage_update', '&gt;', datetime.datetime.now() - relativedelta(days=7))]"/>
<filter string="Closed Last 30 Days" name="closed_last_30_days" domain="[('is_closed', '=', True), ('date_last_stage_update', '&gt;', datetime.datetime.now() - relativedelta(days=30))]"/>
<separator/>
<filter invisible="1" string="Today Activities" name="activities_today"
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
<filter invisible="1" string="Future Activities" name="activities_upcoming_all"
domain="[('activity_ids.date_deadline', '&gt;', context_today().strftime('%Y-%m-%d'))]"/>
<group expand="0" string="Group By">
<filter string="Stage" name="stage" context="{'group_by': 'stage_id'}"/>
<filter string="Milestone" name="milestone" context="{'group_by': 'milestone_id'}" groups="project.group_project_milestone"/>
<filter string="Customer" name="customer" context="{'group_by': 'partner_id'}"/>
<filter string="Kanban State" name="kanban_state" context="{'group_by': 'kanban_state'}"/>
<filter string="Deadline" name="date_deadline" context="{'group_by': 'date_deadline'}"/>
</group>
</search>
</field>
</record>
<record id="project_sharing_project_task_action" model="ir.actions.act_window">
<field name="name">Project Sharing</field>
<field name="res_model">project.task</field>
<field name="view_mode">kanban,tree,form</field>
<field name="search_view_id" ref="project.project_sharing_project_task_view_search"/>
<field name="domain">[('display_project_id', '=', active_id)]</field>
<field name="context">{
'default_project_id': active_id,
'delete': 0,
'active_id_chatter': active_id,
}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No tasks found. Let's create one!
</p>
<p>
Keep track of the progress of your tasks from creation to completion.<br/>
Collaborate efficiently by chatting in real-time or via email.
</p>
</field>
</record>
<record id="project_sharing_kanban_action_view" model="ir.actions.act_window.view">
<field name="view_mode">kanban</field>
<field name="act_window_id" ref="project.project_sharing_project_task_action"/>
<field name="view_id" ref="project.project_sharing_project_task_view_kanban"/>
</record>
<record id="project_sharing_tree_action_view" model="ir.actions.act_window.view">
<field name="view_mode">tree</field>
<field name="act_window_id" ref="project.project_sharing_project_task_action"/>
<field name="view_id" ref="project.project_sharing_project_task_view_tree"/>
</record>
<record id="project_sharing_form_action_view" model="ir.actions.act_window.view">
<field name="view_mode">form</field>
<field name="act_window_id" ref="project.project_sharing_project_task_action"/>
<field name="view_id" ref="project.project_sharing_project_task_view_form"/>
</record>
<record id="project_sharing_project_task_action_sub_task" model="ir.actions.act_window">
<field name="name">Sub-tasks</field>
<field name="res_model">project.task</field>
<field name="view_mode">tree,kanban,form</field>
<field name="search_view_id" ref="project.project_sharing_project_task_view_search"/>
<field name="domain">[('id', 'child_of', active_id), ('id', '!=', active_id)]</field>
<field name="context">{'default_parent_id': active_id}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No tasks found. Let's create one!
</p><p>
To get things done, use activities and status on tasks.<br/>
Chat in real time or by email to collaborate efficiently.
</p>
</field>
</record>
<record id="project_sharing_subtasks_tree_action_view" model="ir.actions.act_window.view">
<field name="view_mode">tree</field>
<field name="act_window_id" ref="project.project_sharing_project_task_action_sub_task"/>
<field name="view_id" ref="project.project_sharing_project_task_view_tree"/>
</record>
<record id="project_sharing_subtasks_kanban_action_view" model="ir.actions.act_window.view">
<field name="view_mode">kanban</field>
<field name="act_window_id" ref="project.project_sharing_project_task_action_sub_task"/>
<field name="view_id" ref="project.project_sharing_project_task_view_kanban"/>
</record>
<record id="project_sharing_subtasks_form_action_view" model="ir.actions.act_window.view">
<field name="view_mode">form</field>
<field name="act_window_id" ref="project.project_sharing_project_task_action_sub_task"/>
<field name="view_id" ref="project.project_sharing_project_task_view_form"/>
</record>
</odoo>

View file

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.ui.view" id="project_tags_search_view">
<field name="name">Tags</field>
<field name="model">project.tags</field>
<field name="arch" type="xml">
<search string="Issue Version">
<field name="name"/>
</search>
</field>
</record>
<record model="ir.ui.view" id="project_tags_form_view">
<field name="name">Tags</field>
<field name="model">project.tags</field>
<field name="arch" type="xml">
<form string="Tags">
<sheet>
<group>
<field name="name"/>
<field name="color" widget="color_picker"/>
</group>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="project_tags_tree_view">
<field name="name">Tags</field>
<field name="model">project.tags</field>
<field name="arch" type="xml">
<list string="Tags" editable="top" sample="1" multi_edit="1" default_order="name">
<field name="name"/>
<field name="color" widget="color_picker" optional="show"/>
</list>
</field>
</record>
<record id="project_tags_action" model="ir.actions.act_window">
<field name="name">Tags</field>
<field name="res_model">project.tags</field>
<field name="path">task-tags</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No tags found. Let's create one!
</p>
<p>
Use tags to categorize your tasks.
</p>
</field>
</record>
</odoo>

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="task_type_search" model="ir.ui.view">
<field name="name">project.task.type.search</field>
<field name="model">project.task.type</field>
<field name="arch" type="xml">
<search string="Tasks Stages">
<field name="name" string="Name"/>
<field name="project_ids" string="Project"/>
<field name="mail_template_id"/>
<field name="rating_template_id"/>
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
<group>
<filter string="Projects" name="project_ids" context="{'group_by': 'project_ids'}"/>
</group>
</search>
</field>
</record>
<record id="task_type_edit" model="ir.ui.view">
<field name="name">project.task.type.form</field>
<field name="model">project.task.type</field>
<field name="arch" type="xml">
<form string="Task Stage" delete="0">
<field name="active" invisible="1" />
<sheet>
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active" />
<group>
<group>
<field name="name" placeholder="e.g. To Do"/>
<field name="fold"/>
<field name="user_id" invisible="True"/>
<field name="auto_validation_state" invisible="not rating_template_id"/>
<field name="project_ids" widget="many2many_tags" options="{'color_field': 'color', 'edit_tags': True}" invisible="user_id" required="not user_id"/>
<field name="rotting_threshold_days"/>
<field name="color" widget="color_picker"/>
<field name="sequence" groups="base.group_no_one"/>
</group>
<group>
<field name="mail_template_id" context="{'default_model': 'project.task'}" invisible="user_id"/>
</group>
<group name="group_customer_rating" string="Customer Rating" class="row o_settings_container" invisible="user_id">
<setting class="col-lg-12">
<field name="rating_active"/>
<div invisible="not rating_active">
<field name="rating_status" widget="radio" class="o_row"/>
<group class="mt-2">
<field
name="rating_status_period"
invisible="rating_status != 'periodic'"
required="rating_status == 'periodic'"
/>
<field
name="rating_template_id"
placeholder="Task: Rating Request"
context="{'default_model': 'project.task'}"
required="rating_active"
/>
</group>
<span colspan="2" class="text-muted o_row ps-1">
<i class="fa fa-lightbulb-o pe-2"/>
<span name="rating_status_helper">
<span invisible="rating_status == 'periodic'">A rating request will be sent as soon as a task reaches this stage.</span>
<span invisible="rating_status == 'stage'">Rating requests will be sent as long as the task remains in this stage.</span>
</span>
</span>
</div>
</setting>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="task_type_tree" model="ir.ui.view">
<field name="name">project.task.type.list</field>
<field name="model">project.task.type</field>
<field name="arch" type="xml">
<list string="Task Stage" delete="0" sample="1" multi_edit="1" editable="bottom" open_form_view="True">
<field name="sequence" widget="handle" optional="show"/>
<field name="name" placeholder="e.g. To Do"/>
<field name="rotting_threshold_days" optional="hide"/>
<field name="mail_template_id" optional="hide"/>
<field name="project_ids" required="1" optional="show" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="color" optional="hide" widget="color_picker"/>
<field name="fold" optional="show"/>
</list>
</field>
</record>
<record id="task_type_tree_inherited" model="ir.ui.view">
<field name="name">project.task.type.list.inherited</field>
<field name="model">project.task.type</field>
<field name="mode">primary</field>
<field name="inherit_id" ref="task_type_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='name']" position="after">
<field name="rating_template_id" optional="hide"/>
</xpath>
</field>
</record>
<record id="view_project_task_type_kanban" model="ir.ui.view">
<field name="name">project.task.type.kanban</field>
<field name="model">project.task.type</field>
<field name="arch" type="xml">
<kanban highlight_color="color" class="o_kanban_mobile" sample="1" default_group_by="project_ids">
<templates>
<t t-name="menu" t-if="!selection_mode" groups="base.group_user">
<field name="color" widget="kanban_color_picker"/>
</t>
<t t-name="card">
<field name="name" class="fw-bolder text-truncate"/>
<field name="project_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
</t>
</templates>
</kanban>
</field>
</record>
<record id="open_task_type_form" model="ir.actions.act_window">
<field name="name">Task Stages</field>
<field name="res_model">project.task.type</field>
<field name="path">task-stages</field>
<field name="view_mode">list,kanban,form</field>
<field name="view_id" ref="task_type_tree_inherited"/>
<field name="domain">[('user_id', '=', False)]</field>
<field name="context">{'default_user_id': False}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No stages found. Let's create one!
</p><p>
Define the steps your tasks move through from creation to completion.
</p>
</field>
</record>
<record id="open_task_type_form_domain" model="ir.actions.act_window">
<field name="name">Task Stages</field>
<field name="res_model">project.task.type</field>
<field name="view_mode">list,kanban,form</field>
<field name="domain">[('project_ids','=', project_id)]</field>
<field name="view_id" ref="task_type_tree_inherited"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new stage in the task pipeline
</p><p>
Define the steps that will be used in the project from the
creation of the task, up to the closing of the task or issue.
You will use these stages in order to track the progress in
solving a task or an issue.
</p>
</field>
</record>
<record id="unlink_task_type_action" model="ir.actions.server">
<field name="name">Delete</field>
<field name="model_id" ref="project.model_project_task_type"/>
<field name="binding_model_id" ref="project.model_project_task_type"/>
<field name="binding_view_types">form,list,kanban</field>
<field name="state">code</field>
<field name="code">action = records.unlink_wizard(stage_view=True)</field>
</record>
</odoo>

File diff suppressed because it is too large Load diff

View file

@ -2,10 +2,10 @@
<odoo>
<template id="project.milestone_deadline">
<t t-if="milestone['deadline']">
(due <t t-esc="milestone['deadline']" t-options='{"widget": "date"}'/><t t-if="not milestone['is_reached'] or not milestone['reached_date']">
(due <t t-out="milestone['deadline']" t-options='{"widget": "date"}'/><t t-if="not milestone['is_reached'] or not milestone['reached_date']">
<t t-if="milestone['can_be_marked_as_done']"> - <font t-att-style="'color: rgb(0, ' + str(color_level) + ', 0)'">ready to be marked as reached</font></t>)</t><t t-else=""> - reached on<t t-if="milestone['reached_date'] &gt; milestone['deadline']">
<font t-att-style="'color: rgb(' + str(color_level) + ', 0, 0)'"><b><t t-esc="milestone['reached_date']" t-options='{"widget": "date"}'/></b></font>)</t><t t-else="">
<font t-att-style="'color: rgb(0, ' + str(color_level) + ', 0)'"><b><t t-esc="milestone['reached_date']" t-options='{"widget": "date"}'/></b></font>)</t></t>
<font style="color: rgb(255, 0, 0)"><t t-out="milestone['reached_date']" t-options='{"widget": "date"}'/></font>)</t><t t-else="">
<font style="color: rgb(0, 128, 0)"><t t-out="milestone['reached_date']" t-options='{"widget": "date"}'/></font>)</t></t>
</t>
<t t-elif="milestone['can_be_marked_as_done']">
(<font t-att-style="'color: rgb(0, ' + str(color_level) + ', 0)'">ready to be marked as reached</font>)
@ -23,29 +23,110 @@
<h1 style="font-weight: bolder;">Activities</h1>
</div>
<div name="profitability" t-if="show_profitability">
<t t-if="project.account_id" name="costs">
<h3 style="font-weight: bolder"><u>Profitability</u></h3>
<div name="profitability_detail" class="mt-4">
<table class="table table-striped" t-if="profitability['revenues']['data']">
<thead class="border-2 border-start-0 border-end-0">
<tr>
<th class="fw-bolder" style="width: 55%">Revenues</th>
<th class="fw-bolder text-end" style="width: 15%">Expected</th>
<th class="fw-bolder text-end" style="width: 15%">To Invoice</th>
<th class="fw-bolder text-end" style="width: 15%">Invoiced</th>
</tr>
</thead>
<tbody>
<tr t-foreach="profitability['revenues']['data']" t-as="revenue">
<tr class="revenue_data">
<td t-out="profitability['labels'][revenue['id']]"/>
<td class="text-end" t-out="format_monetary(revenue['invoiced'] + revenue['to_invoice'])"/>
<td class="text-end" t-out="format_monetary(revenue['to_invoice'])"/>
<td class="text-end" t-out="format_monetary(revenue['invoiced'])"/>
</tr>
</tr>
<tfoot>
<td class="fw-bolder">Total Revenues</td>
<td class="fw-bolder text-end" t-out="format_monetary(profitability['revenues']['total']['invoiced'] + profitability['revenues']['total']['to_invoice'])"/>
<td class="fw-bolder text-end" t-out="format_monetary(profitability['revenues']['total']['to_invoice'])"/>
<td class="fw-bolder text-end" t-out="format_monetary(profitability['revenues']['total']['invoiced'])"/>
</tfoot>
</tbody>
</table>
<table class="table table-striped mt-4 table-bordered" t-if="profitability['costs']['data']">
<thead>
<tr>
<th class="fw-bolder" style="width: 55%">Costs</th>
<th class="fw-bolder text-end" style="width: 15%">Expected</th>
<th class="fw-bolder text-end" style="width: 15%">To Bill</th>
<th class="fw-bolder text-end" style="width: 15%">Billed</th>
</tr>
</thead>
<tbody>
<tr t-foreach="profitability['costs']['data']" t-as="cost">
<td t-out="profitability['labels'][cost['id']]"/>
<td class="text-end" t-out="format_monetary(cost['billed'] + cost['to_bill'])"/>
<td class="text-end" t-out="format_monetary(cost['to_bill'])"/>
<td class="text-end" t-out="format_monetary(cost['billed'])"/>
</tr>
</tbody>
<tr>
<td class="fw-bolder">Total Costs</td>
<td class="fw-bolder text-end" t-out="format_monetary(profitability['costs']['total']['billed'] + profitability['costs']['total']['to_bill'])"/>
<td class="fw-bolder text-end" t-out="format_monetary(profitability['costs']['total']['to_bill'])"/>
<td class="fw-bolder text-end" t-out="format_monetary(profitability['costs']['total']['billed'])"/>
</tr>
<tr t-if="profitability['costs']['data'] and profitability['revenues']['data']">
<td class="fw-bolder align-middle">Total</td>
<td t-attf-class="#{'text-danger' if profitability['total']['margin'] &lt; 0 else 'text-success'}" style="text-align: right">
<t t-out="format_monetary(profitability['total']['margin'])"/><br/>
<t t-if="profitability['expected_percentage']">
<t t-out="profitability['expected_percentage']"/><t>%</t>
</t>
</td>
<td t-attf-class="#{'text-danger' if profitability['total']['revenues'] &lt; 0 else 'text-success'}" style="text-align: right">
<t t-out="format_monetary(profitability['total']['revenues'])"/><br/>
<t t-if="profitability['to_bill_to_invoice_percentage']">
<t t-out="profitability['to_bill_to_invoice_percentage']"/><t>%</t>
</t>
</td>
<td t-attf-class="#{'text-danger' if profitability['total']['costs'] &lt; 0 else 'text-success'}" style="text-align: right">
<t t-out="format_monetary(profitability['total']['costs'])"/><br/>
<t t-if="profitability['billed_invoiced_percentage']">
<t t-out="profitability['billed_invoiced_percentage']"/><t>%</t>
</t>
</td>
</tr>
</table>
</div>
</t>
</div>
<div name="milestone" t-if="milestones['show_section']">
<br/>
<h3 style="font-weight: bolder"><u>Milestones</u></h3>
<ul class="o_checklist" t-if="milestones['list']">
<t t-foreach="milestones['list']" t-as="milestone">
<li t-attf-class="{{milestone['is_reached'] and 'o_checked' or ''}}">
<t t-esc="milestone['name']"/>
<span t-if="milestone['is_deadline_future'] and not milestone['is_reached'] and not milestone['can_be_marked_as_done']"><font style="color: rgb(190, 190, 190);"><t t-set="color_level" t-value="64"/><t t-call="project.milestone_deadline"/></font></span>
<span t-elif="milestone['is_deadline_exceeded']"><font style="color: rgb(255, 0, 0);"><t t-call="project.milestone_deadline"/></font></span>
<li t-att-class="milestone['is_reached'] and 'o_checked'" t-attf-id="checkId-{{milestone_index}}">
<t t-out="milestone['name']"/>
<span t-if="milestone['is_deadline_exceeded']"><font style="color: rgb(255, 0, 0);"><t t-call="project.milestone_deadline"/></font></span>
<span t-elif="not milestone['can_be_marked_as_done']"><font style="color: rgb(190, 190, 190);"><t t-set="color_level" t-value="64"/><t t-call="project.milestone_deadline"/></font></span>
<span t-else=""><t t-set="color_level" t-value="128"/><t t-call="project.milestone_deadline"/></span>
</li>
</t>
</ul>
<t t-if="milestones['updated']">
<t t-if="milestones['last_update_date']">Since <t t-esc="milestones['last_update_date']" t-options='{"widget": "date"}'/> (last project update), </t>
<t t-if="milestones['last_update_date']">Since <t t-out="milestones['last_update_date']" t-options='{"widget": "date"}'/> (last project update), </t>
<t t-if="len(milestones['updated']) > 1">the deadline for the following milestones has been updated:</t>
<t t-else="">the deadline for the following milestone has been updated:</t>
<ul>
<t t-foreach="milestones['updated']" t-as="milestone">
<li>
<t t-esc="milestone['name']"/> (<t t-esc="milestone['old_value']" t-options='{"widget": "date"}'/> =&gt; <t t-esc="milestone['new_value']" t-options='{"widget": "date"}'/>)
<t t-out="milestone['name']"/> (<t t-out="milestone['old_value']" t-options='{"widget": "date"}'/> =&gt; <t t-out="milestone['new_value']" t-options='{"widget": "date"}'/>)
</li>
</t>
</ul>
@ -57,21 +138,13 @@
<ul>
<t t-foreach="milestones['created']" t-as="milestone">
<li>
<t t-esc="milestone['name']"/><span t-if="milestone['is_deadline_future'] and not milestone['is_reached']">
<font style="color: rgb(190, 190, 190);">
<t t-set="color_level" t-value="64"/>
<t t-call="project.milestone_deadline"/>
</font>
</span>
<span t-else="">
<t t-set="color_level" t-value="128"/>
<t t-call="project.milestone_deadline"/>
</span>
<t t-out="milestone['name']"/>
<span t-if="milestone['is_deadline_exceeded']"><font style="color: rgb(255, 0, 0);"><t t-call="project.milestone_deadline"/></font></span>
<span t-else=""><font style="color: rgb(190, 190, 190);"><t t-set="color_level" t-value="128"/><t t-call="project.milestone_deadline"/></font></span>
</li>
</t>
</ul>
</t>
</div>
</template>
</odoo>

View file

@ -11,7 +11,6 @@
<field name="description"/>
<field name="status"/>
<filter string="My Updates" name="my_updates" domain="[('user_id', '=', uid)]"/>
<filter string="Followed Updates" name="followed_updates" domain="[('message_is_follower', '=', True)]"/>
<separator/>
<filter string="On Track" name="on_track" domain="[('status', '=', 'on_track')]"/>
<filter string="At Risk" name="at_risk" domain="[('status', '=', 'at_risk')]"/>
@ -27,7 +26,7 @@
<field name="name">project.update.view.form</field>
<field name="model">project.update</field>
<field name="arch" type="xml">
<form string="Project Update" class="o_form_project_update" js_class="form_description_expander">
<form string="Project Update" js_class="form_description_expander">
<sheet>
<div class="oe_title">
<h1>
@ -38,7 +37,7 @@
<group>
<field name="project_id" invisible="1"/>
<field name="color" invisible="1"/>
<field name="status" widget="status_with_color" options="{'color_field': 'color'}"/>
<field name="status" widget="status_with_color"/>
<field name="progress" widget="progressbar" options="{'editable': true}"/>
</group>
<group>
@ -53,11 +52,7 @@
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" options="{'post_refresh':True}" groups="base.group_user"/>
<field name="activity_ids"/>
<field name="message_ids"/>
</div>
<chatter class="d-print-none" reload_on_follower="True"/>
</form>
</field>
</record>
@ -66,34 +61,38 @@
<field name="name">project.update.view.kanban</field>
<field name="model">project.update</field>
<field name="arch" type="xml">
<kanban class="o_pupdate_kanban" sample="1" js_class="project_update_kanban">
<kanban highlight_color="color" sample="0" js_class="project_update_kanban">
<field name="color"/>
<templates>
<t t-name="kanban-box">
<div t-attf-class="{{!selection_mode ? 'oe_kanban_color_' + record.color.raw_value : ''}} oe_kanban_global_click o_pupdate_kanban_card">
<!-- Project Update Kanban View is always ungrouped - see js_class -->
<div class="o_kanban_detail_ungrouped row">
<div class="col-sm-4 col-6 o_pupdate_name">
<b><field name="name_cropped"/></b>
<div>
<field name="user_id" widget="many2one_avatar_user"/>
<t t-esc="record.user_id.value"/>
</div>
</div>
<div class="col-sm-2 text-sm-start col-6 align-end">
<field name="color" invisible="1"/>
<b><field name="status" widget="status_with_color" options="{'color_field': 'color'}"/></b>
</div>
<div class="col-sm-2 col-6 pb-0">
<b><field name="progress_percentage" widget="percentage"/></b>
<div>Progress</div>
</div>
<div class="col-sm-2 col-6 pb-0">
<b><field name="date"/></b>
<div>Date</div>
</div>
<t t-name="card" class="o_project_update_card column-gap-0 column-gap-md-3 row-gap-2 flex-row flex-wrap align-items-center px-3">
<div class="flex-grow-1 flex-shrink-0 w-50 w-md-auto px-2 px-md-0">
<field name="name_cropped" class="fw-bolder"/>
<div class="d-flex gap-1">
<field name="user_id" widget="many2one_avatar_user"/>
<field name="user_id"/>
</div>
</div>
<div class="flex-grow-1 flex-shrink-0 w-50 w-md-auto px-2 px-md-0">
<span>Status</span>
<field name="color" invisible="1"/>
<field name="status" class="fw-bolder" readonly="1" widget="status_with_color"/>
</div>
<div class="flex-grow-1 flex-shrink-0 w-50 w-md-auto px-2 px-md-0">
<span>Progress</span>
<field name="progress_percentage" class="fw-bolder" widget="percentage"/>
</div>
<div class="flex-grow-1 flex-shrink-0 w-50 w-md-auto px-2 px-md-0">
<div id="tasks_stats" class="fw-bolder">
<span invisible="not task_count">
<field name="closed_task_count"/> / <field name="task_count"/> <field name="label_tasks"/> (<field name="closed_task_percentage"/>%)
</span>
<span invisible="task_count">0 <field name="label_tasks"/></span>
</div>
</div>
<div class="flex-grow-1 flex-shrink-0 w-50 w-md-auto px-2 px-md-0">
<div>Date</div>
<field name="date" class="fw-bolder"/>
</div>
</t>
</templates>
</kanban>
@ -101,25 +100,25 @@
</record>
<record id="project_update_view_tree" model="ir.ui.view">
<field name="name">project.update.view.tree</field>
<field name="name">project.update.view.list</field>
<field name="model">project.update</field>
<field name="arch" type="xml">
<tree sample="1" js_class="project_update_list">
<list sample="1" js_class="project_update_list">
<field name="name"/>
<field name="user_id" widget="many2one_avatar_user" class="fw-bold" optional="show"/>
<field name="user_id" widget="many2one_avatar_user" class="fw-bolder" optional="show"/>
<field name="date" optional="show"/>
<field name="progress_percentage" string="Progress" widget="percentage" optional="show"/>
<field name="color" invisible="1"/>
<field name="status" widget="status_with_color" options="{'color_field': 'color'}"/>
</tree>
<field name="progress" string="Progress" widget="progressbar" optional="show"/>
<field name="color" column_invisible="True"/>
<field name="status" widget="status_with_color"/>
</list>
</field>
</record>
<record id="project_update_all_action" model="ir.actions.act_window">
<field name="name">Project Updates</field>
<field name="type">ir.actions.act_window</field>
<field name="name">Dashboard</field>
<field name="res_model">project.update</field>
<field name="view_mode">kanban,tree,form</field>
<field name="path">project-dashboard</field>
<field name="view_mode">kanban,list,form</field>
<field name="domain">[('project_id', '=', active_id)]</field>
<field name="search_view_id" ref="project_update_view_search"/>
<field name="help" type="html">
@ -130,5 +129,4 @@
</p>
</field>
</record>
</odoo>

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="rating_rating_view_tree_project" model="ir.ui.view">
<field name="name">rating.rating.tree.project</field>
<field name="name">rating.rating.list.project</field>
<field name="model">rating.rating</field>
<field name="inherit_id" ref="rating.rating_rating_view_tree"/>
<field name="mode">primary</field>
@ -48,13 +48,10 @@
<field name="rated_partner_id" position="attributes">
<attribute name="string">Assigned to</attribute>
</field>
<field name="create_date" position="replace">
<field name="write_date" readonly="1" string="Submitted On"/>
</field>
<field name="feedback" position="attributes">
<attribute name="readonly">1</attribute>
</field>
<field name="write_date" position="after">
<field name="rated_on" position="after">
<field name="partner_id" position="move"/>
</field>
<xpath expr="//field[@name='is_internal']" position="attributes">
@ -70,12 +67,12 @@
<field name="mode">primary</field>
<field name="priority">64</field>
<field name="arch" type="xml">
<xpath expr="//pivot" position="attributes">
<attribute name="js_class">project_rating_pivot</attribute>
</xpath>
<xpath expr="//field[@name='create_date']" position="attributes">
<xpath expr="//field[@name='rated_on']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<xpath expr="//field[@name='rating']" position="attributes">
<attribute name="string">Rating (1-5)</attribute>
</xpath>
</field>
</record>
@ -86,21 +83,8 @@
<field name="mode">primary</field>
<field name="priority">64</field>
<field name="arch" type="xml">
<xpath expr="//graph" position="attributes">
<attribute name="js_class">project_rating_graph</attribute>
</xpath>
</field>
</record>
<record id="rating_rating_project_view_kanban" model="ir.ui.view">
<field name="name">rating.rating.kanban.project</field>
<field name="model">rating.rating</field>
<field name="inherit_id" ref="rating.rating_rating_view_kanban"/>
<field name="mode">primary</field>
<field name="priority">64</field>
<field name="arch" type="xml">
<xpath expr="//field[@name='create_date']" position="replace">
<field name="write_date"/>
<xpath expr="//field[@name='rating']" position="attributes">
<attribute name="string">Rating (1-5)</attribute>
</xpath>
</field>
</record>
@ -137,20 +121,11 @@
<xpath expr="//filter[@name='responsible']" position="attributes">
<attribute name="string">Assigned to</attribute>
</xpath>
<xpath expr="//filter[@name='filter_create_date']" position="replace">
<filter name="filter_write_date" string="Submitted On" date="write_date"/>
<xpath expr="//filter[@name='filter_rated_on']" position="attributes">
<attribute name="default_period">custom_rated_on_last_30_days</attribute>
</xpath>
<xpath expr="//filter[@name='month']" position="attributes">
<attribute name="context">{'group_by':'write_date:month'}</attribute>
</xpath>
<xpath expr="//filter[@name='today']" position="attributes">
<attribute name="domain">[('write_date', '&gt;', (context_today() - datetime.timedelta(days=1)).strftime('%Y-%m-%d'))]</attribute>
</xpath>
<xpath expr="//filter[@name='last_7days']" position="attributes">
<attribute name="domain">[('write_date','&gt;', (context_today() - datetime.timedelta(days=7)).strftime('%Y-%m-%d'))]</attribute>
</xpath>
<xpath expr="//filter[@name='last_month']" position="attributes">
<attribute name="domain">[('write_date','&gt;', (context_today() - relativedelta(months=1)).strftime('%Y-%m-%d'))]</attribute>
<attribute name="context">{'group_by':'rated_on:month'}</attribute>
</xpath>
</field>
</record>
@ -158,7 +133,7 @@
<record id="rating_rating_action_view_project_rating" model="ir.actions.act_window">
<field name="name">Ratings</field>
<field name="res_model">rating.rating</field>
<field name="view_mode">kanban,tree,graph,pivot,form</field>
<field name="view_mode">kanban,list,graph,pivot,form</field>
<field name="domain">[('consumed','=',True), ('parent_res_model','=','project.project'), ('parent_res_id', '=', active_id)]</field>
<field name="search_view_id" ref="rating_rating_view_search_project"/>
<field name="help" type="html">
@ -172,12 +147,12 @@
<field name="sequence" eval="5"/>
<field name="view_mode">kanban</field>
<field name="act_window_id" ref="rating_rating_action_view_project_rating"/>
<field name="view_id" ref="rating_rating_project_view_kanban"/>
<field name="view_id" ref="rating.rating_rating_view_kanban"/>
</record>
<record id="rating_rating_action_view_project_rating_tree" model="ir.actions.act_window.view">
<field name="sequence" eval="10"/>
<field name="view_mode">tree</field>
<field name="view_mode">list</field>
<field name="act_window_id" ref="rating_rating_action_view_project_rating"/>
<field name="view_id" ref="rating_rating_view_tree_project"/>
</record>
@ -206,7 +181,7 @@
<record id="rating_rating_action_task" model="ir.actions.act_window">
<field name="name">Ratings</field>
<field name="res_model">rating.rating</field>
<field name="view_mode">kanban,tree,pivot,graph,form</field>
<field name="view_mode">kanban,list,pivot,graph,form</field>
<field name="domain">[('res_model', '=', 'project.task'), ('res_id', '=', active_id), ('consumed', '=', True)]</field>
<field name="search_view_id" ref="rating_rating_view_search_project"/>
<field name="help" type="html">
@ -223,12 +198,12 @@
<field name="sequence" eval="5"/>
<field name="view_mode">kanban</field>
<field name="act_window_id" ref="rating_rating_action_task"/>
<field name="view_id" ref="rating_rating_project_view_kanban"/>
<field name="view_id" ref="rating.rating_rating_view_kanban"/>
</record>
<record id="rating_rating_action_task_tree" model="ir.actions.act_window.view">
<field name="sequence" eval="10"/>
<field name="view_mode">tree</field>
<field name="view_mode">list</field>
<field name="act_window_id" ref="rating_rating_action_task"/>
<field name="view_id" ref="rating_rating_view_tree_project"/>
</record>
@ -257,7 +232,8 @@
<record id="rating_rating_action_project_report" model="ir.actions.act_window">
<field name="name">Customer Ratings</field>
<field name="res_model">rating.rating</field>
<field name="view_mode">kanban,tree,pivot,graph,form</field>
<field name="path">task-ratings</field>
<field name="view_mode">kanban,list,pivot,graph,form</field>
<field name="domain">[('parent_res_model','=','project.project'), ('consumed', '=', True)]</field>
<field name="search_view_id" ref="rating_rating_view_search_project"/>
<field name="help" type="html">
@ -269,7 +245,7 @@
</p>
</field>
<field name="context">{
'search_default_last_month': 1,
'search_default_filter_rated_on': 1,
'graph_groupbys': ['rated_partner_id'],
}</field>
</record>
@ -278,12 +254,12 @@
<field name="sequence" eval="5"/>
<field name="view_mode">kanban</field>
<field name="act_window_id" ref="rating_rating_action_project_report"/>
<field name="view_id" ref="rating_rating_project_view_kanban"/>
<field name="view_id" ref="rating.rating_rating_view_kanban"/>
</record>
<record id="rating_rating_action_project_report_tree" model="ir.actions.act_window.view">
<field name="sequence" eval="10"/>
<field name="view_mode">tree</field>
<field name="view_mode">list</field>
<field name="act_window_id" ref="rating_rating_action_project_report"/>
<field name="view_id" ref="rating_rating_view_tree_project"/>
</record>
@ -308,5 +284,4 @@
<field name="act_window_id" ref="rating_rating_action_project_report"/>
<field name="view_id" ref="rating_rating_view_graph"/>
</record>
</odoo>

View file

@ -6,147 +6,32 @@
<field name="priority" eval="50"/>
<field name="inherit_id" ref="base.res_config_settings_view_form" />
<field name="arch" type="xml">
<xpath expr="//div[hasclass('settings')]" position="inside">
<div class="app_settings_block" data-string="Project" string="Project" data-key="project" groups="project.group_project_manager">
<h2>Tasks Management</h2>
<div class="row mt16 o_settings_container" id="tasks_management">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="group_subtask_project"/>
</div>
<div class="o_setting_right_pane">
<label for="group_subtask_project"/>
<div class="text-muted">
Split your tasks to organize your work into sub-milestones
<xpath expr="//form" position="inside">
<app data-string="Project" string="Project" name="project" groups="project.group_project_manager">
<block title="Tasks Management" id="tasks_management">
<setting id="project_stages" help="Track the progress of your projects">
<field name="group_project_stages"/>
<div class="content-group" invisible="not group_project_stages">
<div class="mt8">
<button name="%(project.project_project_stage_configure)d" icon="oi-arrow-right" type="action" string="Configure Stages" class="btn-link"/>
</div>
</div>
</div>
<div class="col-12 col-lg-6 o_setting_box" id="recurring_tasks_setting">
<div class="o_setting_left_pane">
<field name="group_project_recurring_tasks"/>
</div>
<div class="o_setting_right_pane">
<label for="group_project_recurring_tasks"/>
<div class="text-muted">
Auto-generate tasks for regular activities
</div>
</div>
</div>
<div class="col-12 col-lg-6 o_setting_box" id="task_dependencies_setting">
<div class="o_setting_left_pane">
<field name="group_project_task_dependencies"/>
</div>
<div class="o_setting_right_pane">
<label for="group_project_task_dependencies"/>
<div class="text-muted">
Determine the order in which to perform tasks
</div>
</div>
</div>
<div class="col-12 col-lg-6 o_setting_box" id="project_stages">
<div class="o_setting_left_pane">
<field name="group_project_stages"/>
</div>
<div class="o_setting_right_pane">
<label for="group_project_stages"/>
<div class="text-muted">
Track the progress of your projects
</div>
<div class="content-group" attrs="{'invisible': [('group_project_stages', '=', False)]}">
<div class="mt8">
<button name="%(project.project_project_stage_configure)d" icon="fa-arrow-right" type="action" string="Configure Stages" class="btn-link"/>
</div>
</div>
</div>
</div>
<div class="col-12 col-lg-6 o_setting_box" id="project_milestone">
<div class="o_setting_left_pane">
<field name="group_project_milestone"/>
</div>
<div class="o_setting_right_pane">
<label for="group_project_milestone"/>
<div class="text-muted">
Track major progress points that must be reached to achieve success
</div>
</div>
</div>
</div>
<h2>Time Management</h2>
<div class="row mt16 o_settings_container" name="project_time">
<div class="col-12 col-lg-6 o_setting_box" id="log_time_tasks_setting">
<div class="o_setting_left_pane">
<field name="module_hr_timesheet"/>
</div>
<div class="o_setting_right_pane">
<label for="module_hr_timesheet"/>
<div class="text-muted">
Track time spent on projects and tasks
</div>
</div>
</div>
<div class="col-12 col-lg-6 o_setting_box" name="project_time_management">
<div class="o_setting_left_pane">
<field name="module_project_forecast" widget="upgrade_boolean"/>
</div>
<div class="o_setting_right_pane">
<label for="module_project_forecast"/>
<div class="text-muted" name="project_forecast_msg">
Plan resource allocation across projects and estimate deadlines more accurately
</div>
</div>
</div>
</div>
<h2 name="section_analytics">Analytics</h2>
<div class="row mt16 o_settings_container" name="analytic">
<div class="col-12 col-lg-6 o_setting_box" id="track_customer_satisfaction_setting">
<div class="o_setting_left_pane">
<field name="group_project_rating"/>
</div>
<div class="o_setting_right_pane">
<label for="group_project_rating"/>
<div class="text-muted">
Track customer satisfaction on tasks
</div>
<div class="content-group" attrs="{'invisible': [('group_project_rating', '=', False)]}">
<div class="mt16">
<button name="%(project.open_task_type_form)d" context="{'project_id':id}" icon="fa-arrow-right" type="action" string="Set a Rating Email Template on Stages" class="btn-link"/>
</div>
</div>
</div>
</div>
<div class="col-12 col-lg-6 o_setting_box"
id="default_plan_setting"
groups="analytic.group_analytic_accounting"
title="Track the profitability of your projects. Any project, its tasks and timesheets are linked to an analytic account and any analytic account belongs to a plan.">
<div class="o_setting_left_pane"/>
<div class="o_setting_right_pane">
<label for="analytic_plan_id"/>
<span class="fa fa-lg fa-building-o" title="Values set here are company-specific." groups="base.group_multi_company"/>
<div class="text-muted">
Assign each new project to this plan
</div>
<div class="content-group">
<div class="mt16">
<field name="analytic_plan_id"/>
</div>
</div>
</div>
</div>
</div>
</div>
</setting>
</block>
<block title="Time Management" name="project_time">
<setting id="log_time_tasks_setting" help="Track time spent on projects and tasks">
<field name="module_hr_timesheet"/>
</setting>
</block>
</app>
</xpath>
</field>
</record>
<record id="project_config_settings_action" model="ir.actions.act_window">
<field name="name">Settings</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.config.settings</field>
<field name="view_mode">form</field>
<field name="target">inline</field>
<field name="context">{'module' : 'project', 'bin_size': False}</field>
</record>
<menuitem id="project_config_settings_menu_action" name="Settings" parent="menu_project_config"
sequence="0" action="project_config_settings_action" groups="base.group_system"/>
</odoo>

View file

@ -1,22 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Partners inherited form -->
<record id="view_task_partner_info_form" model="ir.ui.view">
<field name="name">res.partner.task.buttons</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="priority" eval="7"/>
<field name="priority" eval="8"/>
<field name="arch" type="xml">
<div name="button_box" position="inside">
<button class="oe_stat_button" type="action" name="%(project_task_action_from_partner)d"
groups="project.group_project_user"
context="{'search_default_partner_id': active_id, 'default_partner_id': active_id}" attrs="{'invisible': [('task_count', '=', 0)]}"
icon="fa-tasks">
<button class="oe_stat_button" type="object" name="action_view_tasks" groups="project.group_project_user" icon="fa-tasks">
<field string="Tasks" name="task_count" widget="statinfo"/>
</button>
</div>
</field>
</record>
</odoo>