summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorYannick Tivisse <yti@odoo.com>2020-05-14 13:44:05 +0000
committerYannick Tivisse <yti@odoo.com>2020-05-25 12:44:14 +0000
commit45f3659305359fff5b0c7bf3de7d49a25ba5c36d (patch)
tree05eb674b1adf2cf31f96d8344dfef83bbced4a03 /addons
parentf27baf0c94d978d21900cbc85368d97c113fea2a (diff)
[IMP] project,...: Improve list views with new widgets
Purpose ======= Task 2195254 introduced many wonderful widgets to pimp the list view. This task aims at implemeting them into the Services apps: project, timesheets, helpdesk, expense, planning and field service. Remaining days: https://drive.google.com/file/d/1o4Wn47445GxLqBkw-ifH9MgtFU_437xz/view Avatar: https://drive.google.com/file/d/1yDwJWP0MZCUKZbqCa8hte1ziVTYRMMtb/view Activities: https://drive.google.com/file/d/1e_rXH50eZq4zY2HOzw7_pfgiD39SoRIl/view Field-specific decorations: https://drive.google.com/file/d/1WFnqJ-jkyDFftRHxWW4bgomoWrOpOuaY/view More control on buttons Specifications ============== You will find below a list of which widgets to apply to which fields by model. Some changes have to be done in the form/kanban views as well. 1) project.task remaining days -> date_deadline -> list + kanban + form views hide this field if is_closed is true avatar -> user_id -> list view activities -> 'next activity' field to be added -> list view field-specific decorations https://nimb.ws/ACewrY remove the current orange/red decorations we have if planned_hours > 0, display the remaining_hours field in (list view): red if progress is > 100% orange if progress is between 80% and 100% green if progress is <80% display unit_amount in red if > 24:00 -> form view 2) rating.rating display the rating in: green = satisfied orange = not_satisfied red = highly_dissatisfied list + form views 3) project.project avatar -> user_id -> list + form views 4) account.analytic.line avatar -> employee_id -> list view display unit_amount in red if > 24:00 -> list + kanban + form view https://nimb.ws/eIWa7E merge the timer and duration fields together 5) helpdesk.ticket avatar user_id -> list view employee_id under timesheet_ids -> form view remaining hours -> sla_deadline activities -> 'next activity' field to be added -> list view field specific decoration -> display unit_amount in red if > 24:00 -> form view 6) hr.expense avatar -> employee_id -> list view activities -> 'next activity' field to be added -> list view badge -> state -> list view draft = blue reported = green approved = green done = green refused = red combine the unit_amount and the currency_id fields (see the total_amount field for reference) -> list view (idem for the expense_line_ids one2many on hr.expense.sheet) display the total_amount in bold move 'company_id' before 'amount' 7) hr.expense.sheet avatar employee_id -> list view user_id -> list view activities -> 'next activity' field to be added -> list view badge -> state -> list view draft = blue submit = green approve = green post = green done = green cancel = red display the total_amount in bold move 'company_id' before 'amount' 8) planning.slot avatar -> employee_id -> list view decoration display the allocated_percentage field in red if > 100% -> list + form view display the forecast_hours field in red if > planned hours and if planned_hours > 0 -> form view display the effective_hours field in red if > forecast_hours and if forecast_hours > 0 -> form view 9) misc calendar.event -> avatar -> user_id -> form view mail.activity.type -> combine the delay_count and delay_unit fields together -> list view note.note -> 'next activity' field to be added -> list view crm.lead.mining.request combine the lead_number and lead_type fields together -> list view display the name in bold badge -> state draft = blue done = green error = red planning.slot.template -> display the duration field in red if it is = 0 closes odoo/odoo#51271 Taskid: 2248351 Related: odoo/enterprise#10605 Related: odoo/upgrade#1252 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
Diffstat (limited to 'addons')
-rw-r--r--addons/calendar/views/calendar_views.xml2
-rw-r--r--addons/crm_iap_lead/models/crm_iap_lead_mining_request.py12
-rw-r--r--addons/crm_iap_lead/views/crm_iap_lead_views.xml7
-rw-r--r--addons/hr_expense/views/hr_expense_views.xml32
-rw-r--r--addons/hr_timesheet/static/src/js/timesheet_uom.js7
-rw-r--r--addons/hr_timesheet/static/src/js/timesheet_uom_timer.js107
-rw-r--r--addons/hr_timesheet/views/assets.xml1
-rw-r--r--addons/hr_timesheet/views/hr_timesheet_views.xml8
-rw-r--r--addons/hr_timesheet/views/project_views.xml21
-rw-r--r--addons/mail/models/mail_activity.py9
-rw-r--r--addons/mail/views/mail_activity_views.xml3
-rw-r--r--addons/note/views/note_views.xml1
-rw-r--r--addons/project/models/project.py6
-rw-r--r--addons/project/static/src/js/project.js47
-rw-r--r--addons/project/views/project_assets.xml1
-rw-r--r--addons/project/views/project_views.xml31
-rw-r--r--addons/rating/views/rating_rating_views.xml2
-rw-r--r--addons/timer/static/src/js/timer_toggle_button.js5
18 files changed, 183 insertions, 119 deletions
diff --git a/addons/calendar/views/calendar_views.xml b/addons/calendar/views/calendar_views.xml
index a5b1d080b63..5d8e8f598f7 100644
--- a/addons/calendar/views/calendar_views.xml
+++ b/addons/calendar/views/calendar_views.xml
@@ -233,7 +233,7 @@
<page name="page_misc" string="Misc" groups="base.group_no_one">
<group>
<label for="user_id" string="Owner"/>
- <field name="user_id" nolabel="1"/>
+ <field name="user_id" nolabel="1" widget="many2one_avatar_user"/>
</group>
</page>
</notebook>
diff --git a/addons/crm_iap_lead/models/crm_iap_lead_mining_request.py b/addons/crm_iap_lead/models/crm_iap_lead_mining_request.py
index 598fe68d8d6..6f30f596d2a 100644
--- a/addons/crm_iap_lead/models/crm_iap_lead_mining_request.py
+++ b/addons/crm_iap_lead/models/crm_iap_lead_mining_request.py
@@ -37,7 +37,8 @@ class CRMLeadMiningRequest(models.Model):
error = fields.Text(string='Error', readonly=True)
# Lead / Opportunity Data
- lead_type = fields.Selection([('lead', 'Lead'), ('opportunity', 'Opportunity')], string='Type', required=True, default=_default_lead_type)
+ lead_type = fields.Selection([('lead', 'Leads'), ('opportunity', 'Opportunities')], string='Type', required=True, default=_default_lead_type)
+ display_lead_label = fields.Char(compute='_compute_display_lead_label')
team_id = fields.Many2one('crm.team', string='Sales Team', domain="[('use_opportunities', '=', True)]")
user_id = fields.Many2one('res.users', string='Salesperson')
tag_ids = fields.Many2many('crm.tag', string='Tags')
@@ -64,6 +65,15 @@ class CRMLeadMiningRequest(models.Model):
lead_contacts_credits = fields.Char(compute='_compute_tooltip', readonly=True)
lead_total_credits = fields.Char(compute='_compute_tooltip', readonly=True)
+ @api.depends('lead_type', 'lead_number')
+ def _compute_display_lead_label(self):
+ selection_description_values = {
+ e[0]: e[1] for e in self._fields['lead_type']._description_selection(self.env)}
+ for request in self:
+ lead_type = selection_description_values[request.lead_type]
+ request.display_lead_label = '%s %s' % (request.lead_number, lead_type)
+
+
@api.onchange('lead_number', 'contact_number')
def _compute_tooltip(self):
for record in self:
diff --git a/addons/crm_iap_lead/views/crm_iap_lead_views.xml b/addons/crm_iap_lead/views/crm_iap_lead_views.xml
index f76fb55013c..5621f10109e 100644
--- a/addons/crm_iap_lead/views/crm_iap_lead_views.xml
+++ b/addons/crm_iap_lead/views/crm_iap_lead_views.xml
@@ -116,15 +116,14 @@
<field name="model">crm.iap.lead.mining.request</field>
<field name="arch" type="xml">
<tree>
- <field name="name"/>
- <field name="lead_number"/>
- <field name="lead_type"/>
+ <field name="name" decoration-bf="1"/>
+ <field name="display_lead_label" string="Number of Leads"/>
<field name="search_type"/>
<field name="country_ids" widget="many2many_tags"/>
<field name="team_id"/>
<field name="user_id"/>
<field name="tag_ids" widget="many2many_tags"/>
- <field name="state" readonly="1"/>
+ <field name="state" readonly="1" decoration-info="state == 'draft'" decoration-success="state == 'done'" decoration-danger="state == 'error'" widget="badge"/>
</tree>
</field>
</record>
diff --git a/addons/hr_expense/views/hr_expense_views.xml b/addons/hr_expense/views/hr_expense_views.xml
index 72192d284a0..8468611276e 100644
--- a/addons/hr_expense/views/hr_expense_views.xml
+++ b/addons/hr_expense/views/hr_expense_views.xml
@@ -38,27 +38,28 @@
<field name="name">hr.expense.tree</field>
<field name="model">hr.expense</field>
<field name="arch" type="xml">
- <tree string="Expenses" decoration-bf="message_unread == True" class="o_expense_tree" js_class="hr_expense_tree_dashboard_upload" decoration-info="state == 'draft'" decoration-muted="state == 'cancel'" multi_edit="1">
+ <tree string="Expenses" multi_edit="1">
<field name="attachment_number" invisible="True"/>
<field name="date" optional="show"/>
<field name="accounting_date" optional="hide" groups="account.group_account_invoice,account.group_account_readonly"/>
<field name="name"/>
- <field name="employee_id"/>
+ <field name="employee_id" widget="many2one_avatar_employee"/>
<field name="sheet_id" optional="show" invisible="not context.get('show_report', False)" readonly="1"/>
<field name="payment_mode" optional="show"/>
<field name="reference" optional="hide"/>
<field name="analytic_account_id" optional="show" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" optional="hide" widget="many2many_tags" groups="analytic.group_analytic_tags"/>
<field name="account_id" optional="hide"/>
- <field name="unit_amount" optional="hide"/>
+ <field name="company_id" optional="show" groups="base.group_multi_company" readonly="1"/>
+ <field name="unit_amount" optional="hide" widget="monetary" options="{'currency_field': 'currency_id'}"/>
<field name="currency_id" optional="hide"/>
<field name="quantity" optional="hide"/>
+ <field name="activity_ids" widget="list_activity" optional="show"/>
<field name="tax_ids" optional="show" widget="many2many_tags" groups="account.group_account_user"/>
- <field name="total_amount" optional="show" sum="Total Amount" widget="monetary" options="{'currency_field': 'currency_id'}"/>
+ <field name="total_amount" optional="show" sum="Total Amount" widget="monetary" options="{'currency_field': 'currency_id'}" decoration-bf="True"/>
<button name="action_get_attachment_view" string="Attachments" type="object" icon="fa-paperclip" attrs="{'invisible': [('attachment_number', '=', 0)]}"/>
<field name="message_unread" invisible="1"/>
- <field name="state" optional="show" readonly="1"/>
- <field name="company_id" optional="show" groups="base.group_multi_company" readonly="1"/>
+ <field name="state" optional="show" readonly="1" decoration-info="state == 'draft'" decoration-success="state in ['reported', 'approved', 'done']" decoration-danger="state in 'refused'" widget="badge"/>
<field name="activity_exception_decoration" widget="activity_exception"/>
</tree>
</field>
@@ -460,17 +461,18 @@
<field name="name">hr.expense.sheet.tree</field>
<field name="model">hr.expense.sheet</field>
<field name="arch" type="xml">
- <tree string="Expense Reports" decoration-warning="state=='draft'" decoration-bf="message_unread == True" decoration-danger="state=='cancel'">
+ <tree string="Expense Reports">
<field name="accounting_date" optional="show" groups="account.group_account_manager"/>
<field name="create_date" optional="hide"/>
<field name="name" string="Expense Report"/>
- <field name="employee_id"/>
- <field name="user_id" optional="show"/>
- <field name="total_amount" optional="show" sum="Total Amount"/>
+ <field name="employee_id" widget="many2one_avatar_employee"/>
+ <field name="user_id" optional="show" widget="many2one_avatar_user"/>
+ <field name="company_id" optional="show" groups="base.group_multi_company"/>
+ <field name="total_amount" optional="show" sum="Total Amount" decoration-bf="True"/>
<field name="currency_id" optional="hide"/>
<field name="journal_id" optional="hide"/>
- <field name="state" optional="show"/>
- <field name="company_id" optional="show" groups="base.group_multi_company"/>
+ <field name="state" optional="show" decoration-info="state == 'draft'" decoration-success="state in ['submit', 'approve', 'post', 'done']" decoration-danger="state == 'cancel'" widget="badge"/>
+ <field name="activity_ids" widget="list_activity" optional="show"/>
<field name="message_unread" invisible="1"/>
<field name="activity_exception_decoration" widget="activity_exception"/>
</tree>
@@ -522,9 +524,9 @@
</div>
<group>
<group name="employee_details">
- <field name="employee_id" context="{'default_company_id': company_id}"/>
+ <field name="employee_id" context="{'default_company_id': company_id}" widget="many2one_avatar_employee"/>
<field name="payment_mode"/>
- <field name="user_id" domain="[('share', '=', False)]"/>
+ <field name="user_id" domain="[('share', '=', False)]" widget="many2one_avatar_user"/>
<field name="address_id" invisible="1" context="{'default_company_id': company_id}"/>
<field name="department_id" invisible="1" context="{'default_company_id': company_id}"/>
<field name="company_id" groups="base.group_multi_company"/>
@@ -544,7 +546,7 @@
<field name="message_unread" invisible="1"/>
<field name="attachment_number" string=" "/>
<button name="action_get_attachment_view" string="View Attachments" type="object" icon="fa-paperclip"/>
- <field name="unit_amount" optional="hide"/>
+ <field name="unit_amount" optional="hide" widget="monetary" options="{'currency_field': 'currency_id'}"/>
<field name="currency_id" optional="hide"/>
<field name="quantity" optional="hide"/>
<field name="company_id" invisible="1"/>
diff --git a/addons/hr_timesheet/static/src/js/timesheet_uom.js b/addons/hr_timesheet/static/src/js/timesheet_uom.js
index 6e6a3f2e953..acfd9fc6e5b 100644
--- a/addons/hr_timesheet/static/src/js/timesheet_uom.js
+++ b/addons/hr_timesheet/static/src/js/timesheet_uom.js
@@ -121,7 +121,11 @@ const FieldTimesheetTime = basicFields.FieldFloatTime.extend({
if (this.time) {
this.timer = setInterval(() => {
this.time.addSecond();
- this.$el.text(this.time.toString());
+ if (this.$el.children().length) {
+ this.$el.contents()[1].replaceWith(this.time.toString());
+ } else {
+ this.$el.text(this.time.toString());
+ }
this.$el.addClass('font-weight-bold text-danger');
}, 1000);
} else {
@@ -155,7 +159,6 @@ if (widgetName === 'float_toggle') {
fieldRegistry.get(widgetName).extend({})
) || FieldTimesheetFactor;
}
-
fieldRegistry.add('timesheet_uom', FieldTimesheetUom);
diff --git a/addons/hr_timesheet/static/src/js/timesheet_uom_timer.js b/addons/hr_timesheet/static/src/js/timesheet_uom_timer.js
new file mode 100644
index 00000000000..1355ea009a5
--- /dev/null
+++ b/addons/hr_timesheet/static/src/js/timesheet_uom_timer.js
@@ -0,0 +1,107 @@
+odoo.define('hr_timesheet.timesheet_uom_timer', function (require) {
+"use strict";
+
+const fieldRegistry = require('web.field_registry');
+const TimesheetUom = require('hr_timesheet.timesheet_uom');
+const { _lt } = require('web.core');
+const session = require('web.session');
+
+/**
+ * Extend float time widget to add the using of a timer for duration
+ * (unit_amount) field.
+ */
+const FieldTimesheetTimeTimer = TimesheetUom.FieldTimesheetTime.extend({
+ init: function () {
+ this._super.apply(this, arguments);
+ this.isTimerRunning = this.record.data.is_timer_running;
+ },
+
+ _render: function () {
+ this._super.apply(this, arguments);
+ const my_timesheets = this.record.getContext().my_timesheet_display_timer;
+ const display_timer = this.record.data.display_timer;
+ if (my_timesheets && display_timer) {
+ const title = this.isTimerRunning ? _lt('Stop') : _lt('Play');
+ const name = this.isTimerRunning ? 'action_timer_stop' : 'action_timer_start';
+ const label = this.isTimerRunning ? _lt('Stop') : _lt('Start');
+
+ const button = $('<button>', {
+ 'class': 'o_icon_button o-timer-button mr8',
+ 'title': title,
+ 'name': name,
+ 'aria-label': label,
+ 'aria-pressed': this.isTimerRunning,
+ 'type': 'button',
+ 'role': 'button',
+ });
+ button.html('<i/>');
+ button.find('i')
+ .addClass('fa')
+ .toggleClass('fa-stop-circle o-timer-stop-button', this.isTimerRunning)
+ .toggleClass('fa-play-circle o-timer-play-button', !this.isTimerRunning)
+ .attr('title', title);
+ button.on('click', this._onToggleButton.bind(this));
+ this.$el.prepend(button);
+ }
+ },
+
+ _onToggleButton: async function (event) {
+ const context = this.record.getContext();
+ const prevent_deletion = this.attrs.options && this.attrs.options.prevent_deletion || false;
+ event.stopPropagation();
+ const result = await this._rpc({
+ model: this.model,
+ method: this._getActionButton(),
+ context: $.extend({}, context, {prevent_deletion: prevent_deletion}),
+ args: [this.res_id]
+ });
+
+ this.trigger_up('field_changed', {
+ dataPointID: this.dataPointID,
+ changes: {
+ 'is_timer_running': !this.isTimerRunning,
+ },
+ });
+ this.trigger_up('timer_changed', {
+ id: this.res_id,
+ is_timer_running: !this.isTimerRunning
+ });
+ },
+
+ _getActionButton: function () {
+ return this.isTimerRunning ? 'action_timer_stop' : 'action_timer_start';
+ }
+
+});
+
+/**
+ * Binding depending on Company Preference
+ *
+ * determine wich widget will be the timesheet one.
+ * Simply match the 'timesheet_uom' widget key with the correct
+ * implementation (float_time, float_toggle, ...). The default
+ * value will be 'float_factor'.
+**/
+const widgetName = 'timesheet_uom' in session ?
+ session.timesheet_uom.timesheet_widget : 'float_factor';
+
+let FieldTimesheetUom = null;
+
+if (widgetName === 'float_toggle') {
+ FieldTimesheetUom = TimesheetUom.FieldTimesheetToggle;
+} else if (widgetName === 'float_time') {
+ FieldTimesheetUom = FieldTimesheetTimeTimer;
+} else {
+ FieldTimesheetUom = (
+ fieldRegistry.get(widgetName) &&
+ fieldRegistry.get(widgetName).extend({})
+ ) || TimesheetUom.FieldTimesheetFactor;
+}
+fieldRegistry.add('timesheet_uom_timer', FieldTimesheetUom);
+
+
+return {
+ FieldTimesheetTimeTimer,
+};
+
+});
diff --git a/addons/hr_timesheet/views/assets.xml b/addons/hr_timesheet/views/assets.xml
index 34f25b9ab14..b20bdf051b8 100644
--- a/addons/hr_timesheet/views/assets.xml
+++ b/addons/hr_timesheet/views/assets.xml
@@ -5,6 +5,7 @@
<link type="text/scss" href="/hr_timesheet/static/src/scss/timer_button.scss" rel="stylesheet"/>
<script type="text/javascript" src="/hr_timesheet/static/src/js/task_with_hours.js"></script>
<script type="text/javascript" src="/hr_timesheet/static/src/js/timesheet_uom.js"/>
+ <script type="text/javascript" src="/hr_timesheet/static/src/js/timesheet_uom_timer.js"/>
<script type="text/javascript" src="/hr_timesheet/static/src/js/timesheet_list_view.js"/>
<script type="text/javascript" src="/hr_timesheet/static/src/js/timesheet_kanban_view.js"/>
</xpath>
diff --git a/addons/hr_timesheet/views/hr_timesheet_views.xml b/addons/hr_timesheet/views/hr_timesheet_views.xml
index d84f61e320c..994b4ec80f7 100644
--- a/addons/hr_timesheet/views/hr_timesheet_views.xml
+++ b/addons/hr_timesheet/views/hr_timesheet_views.xml
@@ -26,8 +26,8 @@
<field name="project_id" required="1" context="{'form_view_ref': 'project.project_project_view_form_simplified',}"/>
<field name="task_id" optional="show" widget="task_with_hours" context="{'default_project_id': project_id}" domain="[('project_id', '=', project_id)]"/>
<field name="name" optional="show" required="0"/>
- <field name="is_timer_running" string="Timer" optional="show" widget="timer_toggle_button" readonly="1" attrs="{ 'invisible': [('display_timer', '=', False)] }" invisible="not context.get('my_timesheet_display_timer')" />
- <field name="unit_amount" optional="show" widget="timesheet_uom" sum="Total" attrs="{'readonly': [('id', '!=', False), ('timer_start', '!=', False)]}"/>
+ <field name="is_timer_running" invisible="1"/>
+ <field name="unit_amount" optional="show" widget="timesheet_uom_timer" sum="Total" attrs="{'readonly': [('id', '!=', False), ('timer_start', '!=', False)]}" decoration-danger="unit_amount &gt; 24"/>
<field name="company_id" invisible="1"/>
<field name="timer_start" invisible="1"/>
<field name="display_timer" invisible="1"/>
@@ -93,7 +93,7 @@
</group>
<group>
<field name="amount"/>
- <field name="unit_amount" widget="timesheet_uom" attrs="{'readonly': [('id', '!=', False), ('timer_start', '!=', False)]}"/>
+ <field name="unit_amount" widget="timesheet_uom" attrs="{'readonly': [('id', '!=', False), ('timer_start', '!=', False)]}" decoration-danger="unit_amount &gt; 24"/>
<field name="currency_id" invisible="1"/>
<field name="timer_start" invisible="1"/>
</group>
@@ -196,7 +196,7 @@
<t t-esc="record.date.value"/>
</span>
<span class="float-right">
- <strong>Duration: </strong><field name="unit_amount" widget="timesheet_uom"/>
+ <strong>Duration: </strong><field name="unit_amount" widget="timesheet_uom" decoration-danger="unit_amount &gt; 24"/>
</span>
</div>
</t>
diff --git a/addons/hr_timesheet/views/project_views.xml b/addons/hr_timesheet/views/project_views.xml
index 2cfea29a39a..1e7cc08f383 100644
--- a/addons/hr_timesheet/views/project_views.xml
+++ b/addons/hr_timesheet/views/project_views.xml
@@ -121,18 +121,18 @@
<tree editable="bottom" string="Timesheet Activities" default_order="date">
<field name="date"/>
<field name="user_id" invisible="1"/>
- <field name="employee_id" required="1"/>
+ <field name="employee_id" required="1" widget="many2one_avatar_employee"/>
<field name="name" required="0"/>
- <field name="unit_amount" widget="timesheet_uom"/>
+ <field name="unit_amount" widget="timesheet_uom" decoration-danger="unit_amount &gt; 24"/>
<field name="project_id" invisible="1"/>
<field name="company_id" invisible="1"/>
</tree>
<kanban class="o_kanban_mobile">
<field name="date"/>
<field name="user_id"/>
- <field name="employee_id"/>
+ <field name="employee_id" widget="many2one_avatar_employee"/>
<field name="name"/>
- <field name="unit_amount"/>
+ <field name="unit_amount" decoration-danger="unit_amount &gt; 24"/>
<field name="project_id"/>
<templates>
<t t-name="kanban-box">
@@ -164,9 +164,9 @@
<group>
<field name="date"/>
<field name="user_id" invisible="1"/>
- <field name="employee_id" required="1"/>
+ <field name="employee_id" required="1" widget="many2one_avatar_employee"/>
<field name="name"/>
- <field name="unit_amount" string="Duration" widget="float_time"/>
+ <field name="unit_amount" string="Duration" widget="float_time" decoration-danger="unit_amount &gt; 24"/>
<field name="project_id" invisible="1"/>
<field name="company_id" invisible="1"/>
</group>
@@ -193,13 +193,10 @@
<field name="inherit_id" ref="project.view_task_tree2" />
<field eval="2" name="priority"/>
<field name="arch" type="xml">
- <xpath expr="//tree" position="attributes">
- <attribute name="decoration-warning">planned_hours and (effective_hours&gt;planned_hours)</attribute>
- </xpath>
<field name="user_id" position="after">
<field name="planned_hours" widget="float_time" sum="Initially Planned Hours" optional="show"/>
<field name="effective_hours" widget="float_time" sum="Effective Hours" optional="show"/>
- <field name="remaining_hours" widget="float_time" sum="Remaining Hours" optional="show"/>
+ <field name="remaining_hours" widget="float_time" sum="Remaining Hours" optional="show" decoration-danger="progress &gt;= 100" decoration-warning="progress &gt;= 80 and progress &lt; 100"/>
<field name="progress" widget="progressbar" optional="show" groups="hr_timesheet.group_hr_timesheet_user"/>
</field>
</field>
@@ -240,7 +237,7 @@
</templates>
<div class="oe_kanban_bottom_left" position="inside">
<t t-if="record.planned_hours.raw_value &gt; 0 and record.allow_timesheets.raw_value">
- <t t-set="badge" t-value="" t-if="record.progress.raw_value &lt; 80"/>
+ <t t-set="badge" t-value=""/>
<t t-set="badge" t-value="'badge-warning'" t-if="record.progress.raw_value &gt;= 80 and record.progress.raw_value &lt;= 100"/>
<t t-set="badge" t-value="'badge-danger'" t-if="record.remaining_hours.raw_value &lt; 0"/>
<div t-attf-class="oe_kanban_align badge {{ badge }}" title="Remaining hours">
@@ -302,7 +299,7 @@
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_kanban"/>
<field name="arch" type="xml">
- <xpath expr="//span[@name='date']" position="before">
+ <xpath expr="//div[@name='date_deadline']" position="before">
<field name="display_timesheet_timer" invisible="1"/>
<field name="timer_start" invisible="1"/>
<field name="timer_pause" invisible="1"/>
diff --git a/addons/mail/models/mail_activity.py b/addons/mail/models/mail_activity.py
index 0822c3adb06..ca6caf35635 100644
--- a/addons/mail/models/mail_activity.py
+++ b/addons/mail/models/mail_activity.py
@@ -45,6 +45,7 @@ class MailActivityType(models.Model):
('days', 'days'),
('weeks', 'weeks'),
('months', 'months')], string="Delay units", help="Unit of delay", required=True, default='days')
+ delay_label = fields.Char(compute='_compute_delay_label')
delay_from = fields.Selection([
('current_date', 'after validation date'),
('previous_activity', 'after previous activity deadline')], string="Delay Type", help="Type of delay", required=True, default='previous_activity')
@@ -91,6 +92,14 @@ class MailActivityType(models.Model):
for activity_type in self:
activity_type.initial_res_model_id = activity_type.res_model_id
+ @api.depends('delay_unit', 'delay_count')
+ def _compute_delay_label(self):
+ selection_description_values = {
+ e[0]: e[1] for e in self._fields['delay_unit']._description_selection(self.env)}
+ for activity_type in self:
+ unit = selection_description_values[activity_type.delay_unit]
+ activity_type.delay_label = '%s %s' % (activity_type.delay_count, unit)
+
class MailActivity(models.Model):
""" An actual activity to perform. Activities are linked to
diff --git a/addons/mail/views/mail_activity_views.xml b/addons/mail/views/mail_activity_views.xml
index eaea8c27cb0..337455bae57 100644
--- a/addons/mail/views/mail_activity_views.xml
+++ b/addons/mail/views/mail_activity_views.xml
@@ -70,8 +70,7 @@
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="summary"/>
- <field name="delay_count" string="Planned in"/>
- <field name="delay_unit" string="Unit"/>
+ <field name="delay_label" string="Planned in" class="text-right"/>
<field name="delay_from" string="Type"/>
<field name="res_model_id" groups="base.group_no_one"/>
<field name="icon" groups="base.group_no_one"/>
diff --git a/addons/note/views/note_views.xml b/addons/note/views/note_views.xml
index b6c586157d5..2f10994aa04 100644
--- a/addons/note/views/note_views.xml
+++ b/addons/note/views/note_views.xml
@@ -141,6 +141,7 @@
<field name="open"/>
<field name="stage_id"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
+ <field name="activity_ids" widget="list_activity" optional="show"/>
<field name="activity_exception_decoration" widget="activity_exception"/>
</tree>
</field>
diff --git a/addons/project/models/project.py b/addons/project/models/project.py
index cc90d132df1..3cbed1b1107 100644
--- a/addons/project/models/project.py
+++ b/addons/project/models/project.py
@@ -559,7 +559,6 @@ class Task(models.Model):
date_end = fields.Datetime(string='Ending Date', index=True, copy=False)
date_assign = fields.Datetime(string='Assigning Date', index=True, copy=False, readonly=True)
date_deadline = fields.Date(string='Deadline', index=True, copy=False, tracking=True)
- date_deadline_formatted = fields.Char(compute='_compute_date_deadline_formatted')
date_last_stage_update = fields.Datetime(string='Last Stage Update',
index=True,
copy=False,
@@ -624,11 +623,6 @@ class Task(models.Model):
user_names = ', '.join(portal_users[:10].mapped('name'))
raise ValidationError(_("The project visibility setting doesn't allow portal users to see the project's tasks. (%s)") % user_names)
- @api.depends('date_deadline')
- def _compute_date_deadline_formatted(self):
- for task in self:
- task.date_deadline_formatted = format_date(self.env, task.date_deadline) if task.date_deadline else None
-
def _compute_attachment_ids(self):
for task in self:
attachment_ids = self.env['ir.attachment'].search([('res_id', '=', task.id), ('res_model', '=', 'project.task')]).ids
diff --git a/addons/project/static/src/js/project.js b/addons/project/static/src/js/project.js
deleted file mode 100644
index 5e0062a9965..00000000000
--- a/addons/project/static/src/js/project.js
+++ /dev/null
@@ -1,47 +0,0 @@
-odoo.define('project.update_kanban', function (require) {
-'use strict';
-
-var KanbanRecord = require('web.KanbanRecord');
-var basic_fields = require('web.basic_fields');
-var registry = require('web.field_registry');
-
-KanbanRecord.include({
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * @override
- * @private
- */
- _openRecord: function () {
- if (this.modelName === 'project.project' && this.$(".o_project_kanban_boxes a").length) {
- this.$('.o_project_kanban_boxes a').first().click();
- } else {
- this._super.apply(this, arguments);
- }
- },
-
-});
-
-var FieldTaskClosedDeadline = basic_fields.FieldDate.extend({
- _renderReadonly: function () {
- if (this.value && this.value.toISOString()) {
- var deadline = moment(this.value.toISOString()).startOf('day');
- var task_closed = 'is_closed' in this.record.data && this.record.data.is_closed;
-
- if (!task_closed && deadline < moment().startOf('day')) {
- this.$el.addClass('text-danger font-weight-bold');
- } else if (!task_closed && deadline < moment().endOf('day')) {
- this.$el.addClass('text-warning font-weight-bold');
- }
- }
-
- this.$el.text(this._formatValue(this.value));
- }
-});
-
-registry.add('task_closed_deadline', FieldTaskClosedDeadline);
-
-return FieldTaskClosedDeadline;
-});
diff --git a/addons/project/views/project_assets.xml b/addons/project/views/project_assets.xml
index 347f620f386..e6e6dddeea2 100644
--- a/addons/project/views/project_assets.xml
+++ b/addons/project/views/project_assets.xml
@@ -3,7 +3,6 @@
<template id="assets_backend" name="project assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<link rel="stylesheet" href="/project/static/src/css/project.css"/>
- <script type="text/javascript" src="/project/static/src/js/project.js"></script>
<script type="text/javascript" src="/project/static/src/js/project_kanban.js"></script>
<script type="text/javascript" src="/project/static/src/js/project_task_kanban_examples.js"></script>
<script type="text/javascript" src="/project/static/src/js/tours/project.js"></script>
diff --git a/addons/project/views/project_views.xml b/addons/project/views/project_views.xml
index 80f725c5f0f..f35e189f9ae 100644
--- a/addons/project/views/project_views.xml
+++ b/addons/project/views/project_views.xml
@@ -291,7 +291,7 @@
<group>
<group>
<field name="active" invisible="1"/>
- <field name="user_id" string="Project Manager" attrs="{'readonly':[('active','=',False)]}" domain="[('share', '=', False)]"/>
+ <field name="user_id" string="Project Manager" widget="many2one_avatar_user" attrs="{'readonly':[('active','=',False)]}" domain="[('share', '=', False)]"/>
<field name="partner_id" string="Customer"/>
</group>
<group>
@@ -404,7 +404,7 @@
<field name="message_needaction" invisible="1"/>
<field name="active" invisible="1"/>
<field name="name" string="Project Name"/>
- <field name="user_id" optional="show" string="Project Manager"/>
+ <field name="user_id" optional="show" string="Project Manager" widget="many2one_avatar_user"/>
<field name="partner_id" optional="show" string="Contact"/>
<field name="analytic_account_id" optional="hide"/>
<field name="privacy_visibility" optional="hide"/>
@@ -666,7 +666,7 @@
domain="[('parent_id', '=', False)]"
attrs="{'invisible' : [('allow_subtasks', '=', False)]}"
/>
- <field name="date_deadline" widget="task_closed_deadline" />
+ <field name="date_deadline" widget="remaining_days" attrs="{'invisible': [('is_closed', '=', True)]}"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color', 'no_create_edit': True}"/>
</group>
</group>
@@ -761,8 +761,6 @@
<field name="user_id"/>
<field name="partner_id"/>
<field name="sequence"/>
- <field name="date_deadline"/>
- <field name="date_deadline_formatted"/>
<field name="is_closed"/>
<field name="partner_is_company"/>
<field name="displayed_image_id"/>
@@ -823,19 +821,9 @@
<span style="font-weight:bold;" class="fa fa-fw mt4 fa-meh-o text-warning" t-if="record.rating_last_value.value == 3" title="Latest Rating: Not Satisfied" role="img" aria-label="Neutral face"/>
<span style="font-weight:bold;" class="fa fa-fw mt4 fa-frown-o text-danger" t-if="record.rating_last_value.value == 1" title="Latest Rating: Higly Dissatisfied" role="img" aria-label="Sad face"/>
</b>
- <!-- formating of the date -->
- <t t-set="date_format" t-value="'MM/DD/YY'" />
- <t t-set="date" t-value=""/>
- <!-- color of the span -->
- <t t-if="!record.is_closed.raw_value and record.date_deadline.raw_value and moment(record.date_deadline.raw_value.toISOString()).startOf('day') lt moment().startOf('day')">
- <t t-set="deadline_class" t-value="'oe_kanban_text_red'" />
- </t>
- <t t-elif="!record.is_closed.raw_value and record.date_deadline.raw_value and moment(record.date_deadline.raw_value.toISOString()).startOf('day') lt moment().endOf('day')">
- <t t-set="deadline_class" t-value="'text-warning font-weight-bold'" />
- </t>
- <!-- Date value -->
- <t t-if="record.date_deadline.raw_value" t-set="date" t-value="record.date_deadline_formatted.raw_value" />
- <span name="date" title="Deadline" t-attf-class="#{deadline_class || ''}"><t t-esc="date" /></span>
+ <div name="date_deadline" attrs="{'invisible': [('is_closed', '=', True)]}">
+ <field name="date_deadline" widget="remaining_days"/>
+ </div>
</div>
<div class="oe_kanban_bottom_right" t-if="!selection_mode">
<field name="kanban_state" widget="state_selection" groups="base.group_user" invisible="context.get('fsm_mode', False)"/>
@@ -856,7 +844,7 @@
<field name="model">project.task</field>
<field eval="2" name="priority"/>
<field name="arch" type="xml">
- <tree decoration-danger="not is_closed and date_deadline and (date_deadline&lt;current_date)" string="Tasks" multi_edit="1">
+ <tree string="Tasks" multi_edit="1">
<field name="message_needaction" invisible="1" readonly="1"/>
<field name="is_closed" invisible="1" />
<field name="sequence" invisible="not context.get('seq_visible', False)" readonly="1"/>
@@ -865,8 +853,9 @@
<field name="project_id" invisible="context.get('user_invisible', False)" optional="show" readonly="1"/>
<field name="partner_id" optional="hide"/>
<field name="parent_id" groups="project.group_subtask_project" optional="show"/>
- <field name="user_id" invisible="context.get('user_invisible', False)" optional="show"/>
- <field name="date_deadline" optional="hide"/>
+ <field name="user_id" invisible="context.get('user_invisible', False)" optional="show" widget="many2one_avatar_user"/>
+ <field name="activity_ids" widget="list_activity" optional="show"/>
+ <field name="date_deadline" optional="hide" widget="remaining_days" attrs="{'invisible': [('is_closed', '=', True)]}"/>
<field name="stage_id" invisible="context.get('set_visible',False)" optional="show" readonly="1"/>
<field name="tag_ids" optional="hide" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="company_id" groups="base.group_multi_company" optional="show"/>
diff --git a/addons/rating/views/rating_rating_views.xml b/addons/rating/views/rating_rating_views.xml
index a41c404019d..9ed2ca1029e 100644
--- a/addons/rating/views/rating_rating_views.xml
+++ b/addons/rating/views/rating_rating_views.xml
@@ -10,7 +10,7 @@
<field name="parent_res_name"/>
<field name="rated_partner_id"/>
<field name="partner_id"/>
- <field name="rating_text"/>
+ <field name="rating_text" decoration-danger="rating_text == 'highly_dissatisfied'" decoration-warning="rating_text == 'not_satisfied'" decoration-success="rating_text == 'satisfied'"/>
<field name="feedback"/>
<field name="create_date" string="Submitted on"/>
</tree>
diff --git a/addons/timer/static/src/js/timer_toggle_button.js b/addons/timer/static/src/js/timer_toggle_button.js
index 1bb5c2185d7..78274e05e0c 100644
--- a/addons/timer/static/src/js/timer_toggle_button.js
+++ b/addons/timer/static/src/js/timer_toggle_button.js
@@ -3,6 +3,7 @@ odoo.define('timer.timer_toggle_button', function (require) {
const fieldRegistry = require('web.field_registry');
const { FieldToggleBoolean } = require("web.basic_fields");
+const { _lt } = require('web.core');
/**
* The TimerToggleButton is used to display correctly the button
@@ -16,9 +17,9 @@ const TimerToggleButton = FieldToggleBoolean.extend({
*/
_render: function () {
// When the is_timer_running field is false, then the button is used to start the timer
- const title = this.value ? 'stop' : 'play';
+ const title = this.value ? _lt('Stop') : _lt('Play');
const name = this.value ? 'action_timer_stop' : 'action_timer_start';
- const label = this.value ? 'stop' : 'start';
+ const label = this.value ? _lt('Stop') : _lt('Start');
this.$('i')
.addClass('fa')