[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>
This commit is contained in:
Yannick Tivisse
2020-05-25 12:44:14 +00:00
parent f27baf0c94
commit 45f3659305
18 changed files with 183 additions and 119 deletions
+1 -1
View File
@@ -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>
@@ -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:
@@ -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>
+17 -15
View File
@@ -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"/>
@@ -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);
@@ -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,
};
});
+1
View File
@@ -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>
@@ -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>
+9 -12
View File
@@ -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"/>
+9
View File
@@ -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
+1 -2
View File
@@ -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"/>
+1
View File
@@ -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>
-6
View File
@@ -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
-47
View File
@@ -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;
});
-1
View File
@@ -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>
+10 -21
View File
@@ -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"/>
+1 -1
View File
@@ -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>
@@ -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')