summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Dubuc <xdu@odoo.com>2021-03-03 16:41:09 +0000
committerSébastien Theys <seb@odoo.com>2021-03-03 16:59:08 +0000
commitaa5eaf69380ca3c5b9ed1b5dcb183e90fdc9303a (patch)
tree7f3ee326751d5dd3ba011e91ef1b2db8cec88f47
parentf212223305a4d1da0f01f008c3874a72945fcae0 (diff)
[FIX] mail: do not show falsy value in tracking values messages
task-2438828 closes odoo/odoo#65183 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
-rw-r--r--addons/mail/static/src/components/message/message.js79
-rw-r--r--addons/mail/static/src/components/message/message.xml8
-rw-r--r--addons/mail/static/src/components/message/message_tests.js344
-rw-r--r--addons/mail/static/tests/helpers/mock_server.js4
4 files changed, 396 insertions, 39 deletions
diff --git a/addons/mail/static/src/components/message/message.js b/addons/mail/static/src/components/message/message.js
index 342c4ef162a..a357c024c02 100644
--- a/addons/mail/static/src/components/message/message.js
+++ b/addons/mail/static/src/components/message/message.js
@@ -15,6 +15,7 @@ const useStore = require('mail/static/src/component_hooks/use_store/use_store.js
const useUpdate = require('mail/static/src/component_hooks/use_update/use_update.js');
const { _lt } = require('web.core');
+const { format } = require('web.field_utils');
const { getLangDatetimeFormat } = require('web.time');
const { Component, useState } = owl;
@@ -274,24 +275,66 @@ class Message extends Component {
return this.message.tracking_value_ids.map(trackingValue => {
const value = Object.assign({}, trackingValue);
value.changed_field = _.str.sprintf(this.env._t("%s:"), value.changed_field);
- if (value.field_type === 'datetime') {
- if (value.old_value) {
- value.old_value =
- moment.utc(value.old_value).local().format('LLL');
- }
- if (value.new_value) {
- value.new_value =
- moment.utc(value.new_value).local().format('LLL');
- }
- } else if (value.field_type === 'date') {
- if (value.old_value) {
- value.old_value =
- moment(value.old_value).local().format('LL');
- }
- if (value.new_value) {
- value.new_value =
- moment(value.new_value).local().format('LL');
- }
+ /**
+ * Maps tracked field type to a JS formatter. Tracking values are
+ * not always stored in the same field type as their origin type.
+ * Field types that are not listed here are not supported by
+ * tracking in Python. Also see `create_tracking_values` in Python.
+ */
+ switch (value.field_type) {
+ case 'boolean':
+ value.old_value = format.boolean(value.old_value, undefined, { forceString: true });
+ value.new_value = format.boolean(value.new_value, undefined, { forceString: true });
+ break;
+ /**
+ * many2one formatter exists but is expecting id/name_get or data
+ * object but only the target record name is known in this context.
+ *
+ * Selection formatter exists but requires knowing all
+ * possibilities and they are not given in this context.
+ */
+ case 'char':
+ case 'many2one':
+ case 'selection':
+ value.old_value = format.char(value.old_value);
+ value.new_value = format.char(value.new_value);
+ break;
+ case 'date':
+ if (value.old_value) {
+ value.old_value = moment.utc(value.old_value);
+ }
+ if (value.new_value) {
+ value.new_value = moment.utc(value.new_value);
+ }
+ value.old_value = format.date(value.old_value);
+ value.new_value = format.date(value.new_value);
+ break;
+ case 'datetime':
+ if (value.old_value) {
+ value.old_value = moment.utc(value.old_value);
+ }
+ if (value.new_value) {
+ value.new_value = moment.utc(value.new_value);
+ }
+ value.old_value = format.datetime(value.old_value);
+ value.new_value = format.datetime(value.new_value);
+ break;
+ case 'float':
+ value.old_value = format.float(value.old_value);
+ value.new_value = format.float(value.new_value);
+ break;
+ case 'integer':
+ value.old_value = format.integer(value.old_value);
+ value.new_value = format.integer(value.new_value);
+ break;
+ case 'monetary':
+ value.old_value = format.monetary(value.old_value, undefined, { forceString: true });
+ value.new_value = format.monetary(value.new_value, undefined, { forceString: true });
+ break;
+ case 'text':
+ value.old_value = format.text(value.old_value);
+ value.new_value = format.text(value.new_value);
+ break;
}
return value;
});
diff --git a/addons/mail/static/src/components/message/message.xml b/addons/mail/static/src/components/message/message.xml
index c6b5090aff3..7dcae6d626f 100644
--- a/addons/mail/static/src/components/message/message.xml
+++ b/addons/mail/static/src/components/message/message.xml
@@ -166,9 +166,11 @@
<li>
<div class="o_Message_trackingValue">
<div class="o_Message_trackingValueFieldName o_Message_trackingValueItem" t-esc="value.changed_field"/>
- <div class="o_Message_trackingValueOldValue o_Message_trackingValueItem" t-esc="value.old_value"/>
- <t t-if="value.old_value !== value.new_value">
- <div class="o_Message_trackingValueSeparator o_Message_trackingValueItem fa fa-long-arrow-right" title="Changed" role="img"/>
+ <t t-if="value.old_value">
+ <div class="o_Message_trackingValueOldValue o_Message_trackingValueItem" t-esc="value.old_value"/>
+ </t>
+ <div class="o_Message_trackingValueSeparator o_Message_trackingValueItem fa fa-long-arrow-right" title="Changed" role="img"/>
+ <t t-if="value.new_value">
<div class="o_Message_trackingValueNewValue o_Message_trackingValueItem" t-esc="value.new_value"/>
</t>
</div>
diff --git a/addons/mail/static/src/components/message/message_tests.js b/addons/mail/static/src/components/message/message_tests.js
index b6fff1d526d..14bf9bcdd10 100644
--- a/addons/mail/static/src/components/message/message_tests.js
+++ b/addons/mail/static/src/components/message/message_tests.js
@@ -1042,8 +1042,8 @@ QUnit.test('basic rendering of tracking value (float type)', async function (ass
);
assert.strictEqual(
document.querySelector('.o_Message_trackingValueOldValue').textContent,
- "12.3",
- "should display the correct old value (12.3)",
+ "12.30",
+ "should display the correct old value (12.30)",
);
assert.containsOnce(
document.body,
@@ -1062,7 +1062,7 @@ QUnit.test('basic rendering of tracking value (float type)', async function (ass
);
});
-QUnit.test('rendering of tracked field with change of value from non-0 to 0', async function (assert) {
+QUnit.test('rendering of tracked field of type integer: from non-0 to 0', async function (assert) {
assert.expect(1);
await this.start();
@@ -1070,7 +1070,7 @@ QUnit.test('rendering of tracked field with change of value from non-0 to 0', as
id: 11,
tracking_value_ids: [{
changed_field: "Total",
- field_type: "float",
+ field_type: "integer",
id: 6,
new_value: 0,
old_value: 1,
@@ -1080,11 +1080,11 @@ QUnit.test('rendering of tracked field with change of value from non-0 to 0', as
assert.strictEqual(
document.querySelector('.o_Message_trackingValue').textContent,
"Total:10",
- "should display the correct content of tracked field with change of value from non-0 to 0 (Total: 1 -> 0)"
+ "should display the correct content of tracked field of type integer: from non-0 to 0 (Total: 1 -> 0)"
);
});
-QUnit.test('rendering of tracked field with change of value from 0 to non-0', async function (assert) {
+QUnit.test('rendering of tracked field of type integer: from 0 to non-0', async function (assert) {
assert.expect(1);
await this.start();
@@ -1092,7 +1092,7 @@ QUnit.test('rendering of tracked field with change of value from 0 to non-0', as
id: 11,
tracking_value_ids: [{
changed_field: "Total",
- field_type: "float",
+ field_type: "integer",
id: 6,
new_value: 1,
old_value: 0,
@@ -1102,11 +1102,99 @@ QUnit.test('rendering of tracked field with change of value from 0 to non-0', as
assert.strictEqual(
document.querySelector('.o_Message_trackingValue').textContent,
"Total:01",
- "should display the correct content of tracked field with change of value from 0 to non-0 (Total: 0 -> 1)"
+ "should display the correct content of tracked field of type integer: from 0 to non-0 (Total: 0 -> 1)"
+ );
+});
+
+QUnit.test('rendering of tracked field of type float: from non-0 to 0', async function (assert) {
+ assert.expect(1);
+
+ await this.start();
+ const message = this.env.models['mail.message'].create({
+ id: 11,
+ tracking_value_ids: [{
+ changed_field: "Total",
+ field_type: "float",
+ id: 6,
+ new_value: 0,
+ old_value: 1,
+ }],
+ });
+ await this.createMessageComponent(message);
+ assert.strictEqual(
+ document.querySelector('.o_Message_trackingValue').textContent,
+ "Total:1.000.00",
+ "should display the correct content of tracked field of type float: from non-0 to 0 (Total: 1.00 -> 0.00)"
+ );
+});
+
+QUnit.test('rendering of tracked field of type float: from 0 to non-0', async function (assert) {
+ assert.expect(1);
+
+ await this.start();
+ const message = this.env.models['mail.message'].create({
+ id: 11,
+ tracking_value_ids: [{
+ changed_field: "Total",
+ field_type: "float",
+ id: 6,
+ new_value: 1,
+ old_value: 0,
+ }],
+ });
+ await this.createMessageComponent(message);
+ assert.strictEqual(
+ document.querySelector('.o_Message_trackingValue').textContent,
+ "Total:0.001.00",
+ "should display the correct content of tracked field of type float: from 0 to non-0 (Total: 0.00 -> 1.00)"
+ );
+});
+
+QUnit.test('rendering of tracked field of type monetary: from non-0 to 0', async function (assert) {
+ assert.expect(1);
+
+ await this.start();
+ const message = this.env.models['mail.message'].create({
+ id: 11,
+ tracking_value_ids: [{
+ changed_field: "Total",
+ field_type: "monetary",
+ id: 6,
+ new_value: 0,
+ old_value: 1,
+ }],
+ });
+ await this.createMessageComponent(message);
+ assert.strictEqual(
+ document.querySelector('.o_Message_trackingValue').textContent,
+ "Total:1.000.00",
+ "should display the correct content of tracked field of type monetary: from non-0 to 0 (Total: 1.00 -> 0.00)"
+ );
+});
+
+QUnit.test('rendering of tracked field of type monetary: from 0 to non-0', async function (assert) {
+ assert.expect(1);
+
+ await this.start();
+ const message = this.env.models['mail.message'].create({
+ id: 11,
+ tracking_value_ids: [{
+ changed_field: "Total",
+ field_type: "monetary",
+ id: 6,
+ new_value: 1,
+ old_value: 0,
+ }],
+ });
+ await this.createMessageComponent(message);
+ assert.strictEqual(
+ document.querySelector('.o_Message_trackingValue').textContent,
+ "Total:0.001.00",
+ "should display the correct content of tracked field of type monetary: from 0 to non-0 (Total: 0.00 -> 1.00)"
);
});
-QUnit.test('rendering of tracked field with change of value from true to false', async function (assert) {
+QUnit.test('rendering of tracked field of type boolean: from true to false', async function (assert) {
assert.expect(1);
await this.start();
@@ -1123,12 +1211,12 @@ QUnit.test('rendering of tracked field with change of value from true to false',
await this.createMessageComponent(message);
assert.strictEqual(
document.querySelector('.o_Message_trackingValue').textContent,
- "Is Ready:truefalse",
- "should display the correct content of tracked field with change of value from true to false (Is Ready: true -> false)"
+ "Is Ready:TrueFalse",
+ "should display the correct content of tracked field of type boolean: from true to false (Is Ready: True -> False)"
);
});
-QUnit.test('rendering of tracked field with change of value from false to true', async function (assert) {
+QUnit.test('rendering of tracked field of type boolean: from false to true', async function (assert) {
assert.expect(1);
await this.start();
@@ -1145,12 +1233,12 @@ QUnit.test('rendering of tracked field with change of value from false to true',
await this.createMessageComponent(message);
assert.strictEqual(
document.querySelector('.o_Message_trackingValue').textContent,
- "Is Ready:falsetrue",
- "should display the correct content of tracked field with change of value from false to true (Is Ready: false -> true)"
+ "Is Ready:FalseTrue",
+ "should display the correct content of tracked field of type boolean: from false to true (Is Ready: False -> True)"
);
});
-QUnit.test('rendering of tracked field with change of value from string to empty', async function (assert) {
+QUnit.test('rendering of tracked field of type char: from a string to empty string', async function (assert) {
assert.expect(1);
await this.start();
@@ -1168,11 +1256,11 @@ QUnit.test('rendering of tracked field with change of value from string to empty
assert.strictEqual(
document.querySelector('.o_Message_trackingValue').textContent,
"Name:Marc",
- "should display the correct content of tracked field with change of value from string to empty (Total: Marc ->)"
+ "should display the correct content of tracked field of type char: from a string to empty string (Name: Marc ->)"
);
});
-QUnit.test('rendering of tracked field with change of value from empty to string', async function (assert) {
+QUnit.test('rendering of tracked field of type char: from empty string to a string', async function (assert) {
assert.expect(1);
await this.start();
@@ -1190,7 +1278,227 @@ QUnit.test('rendering of tracked field with change of value from empty to string
assert.strictEqual(
document.querySelector('.o_Message_trackingValue').textContent,
"Name:Marc",
- "should display the correct content of tracked field with change of value from empty to string (Total: -> Marc)"
+ "should display the correct content of tracked field of type char: from empty string to a string (Name: -> Marc)"
+ );
+});
+
+QUnit.test('rendering of tracked field of type date: from no date to a set date', async function (assert) {
+ assert.expect(1);
+
+ await this.start();
+ const message = this.env.models['mail.message'].create({
+ id: 11,
+ tracking_value_ids: [{
+ changed_field: "Deadline",
+ field_type: "date",
+ id: 6,
+ new_value: "2018-12-14",
+ old_value: false,
+ }],
+ });
+ await this.createMessageComponent(message);
+ assert.strictEqual(
+ document.querySelector('.o_Message_trackingValue').textContent,
+ "Deadline:12/14/2018",
+ "should display the correct content of tracked field of type date: from no date to a set date (Deadline: -> 12/14/2018)"
+ );
+});
+
+QUnit.test('rendering of tracked field of type date: from a set date to no date', async function (assert) {
+ assert.expect(1);
+
+ await this.start();
+ const message = this.env.models['mail.message'].create({
+ id: 11,
+ tracking_value_ids: [{
+ changed_field: "Deadline",
+ field_type: "date",
+ id: 6,
+ new_value: false,
+ old_value: "2018-12-14",
+ }],
+ });
+ await this.createMessageComponent(message);
+ assert.strictEqual(
+ document.querySelector('.o_Message_trackingValue').textContent,
+ "Deadline:12/14/2018",
+ "should display the correct content of tracked field of type date: from a set date to no date (Deadline: 12/14/2018 ->)"
+ );
+});
+
+QUnit.test('rendering of tracked field of type datetime: from no date and time to a set date and time', async function (assert) {
+ assert.expect(1);
+
+ await this.start();
+ const message = this.env.models['mail.message'].create({
+ id: 11,
+ tracking_value_ids: [{
+ changed_field: "Deadline",
+ field_type: "datetime",
+ id: 6,
+ new_value: "2018-12-14 13:42:28",
+ old_value: false,
+ }],
+ });
+ await this.createMessageComponent(message);
+ assert.strictEqual(
+ document.querySelector('.o_Message_trackingValue').textContent,
+ "Deadline:12/14/2018 13:42:28",
+ "should display the correct content of tracked field of type datetime: from no date and time to a set date and time (Deadline: -> 12/14/2018 13:42:28)"
+ );
+});
+
+QUnit.test('rendering of tracked field of type datetime: from a set date and time to no date and time', async function (assert) {
+ assert.expect(1);
+
+ await this.start();
+ const message = this.env.models['mail.message'].create({
+ id: 11,
+ tracking_value_ids: [{
+ changed_field: "Deadline",
+ field_type: "datetime",
+ id: 6,
+ new_value: false,
+ old_value: "2018-12-14 13:42:28",
+ }],
+ });
+ await this.createMessageComponent(message);
+ assert.strictEqual(
+ document.querySelector('.o_Message_trackingValue').textContent,
+ "Deadline:12/14/2018 13:42:28",
+ "should display the correct content of tracked field of type datetime: from a set date and time to no date and time (Deadline: 12/14/2018 13:42:28 ->)"
+ );
+});
+
+QUnit.test('rendering of tracked field of type text: from some text to empty', async function (assert) {
+ assert.expect(1);
+
+ await this.start();
+ const message = this.env.models['mail.message'].create({
+ id: 11,
+ tracking_value_ids: [{
+ changed_field: "Name",
+ field_type: "text",
+ id: 6,
+ new_value: "",
+ old_value: "Marc",
+ }],
+ });
+ await this.createMessageComponent(message);
+ assert.strictEqual(
+ document.querySelector('.o_Message_trackingValue').textContent,
+ "Name:Marc",
+ "should display the correct content of tracked field of type text: from some text to empty (Name: Marc ->)"
+ );
+});
+
+QUnit.test('rendering of tracked field of type text: from empty to some text', async function (assert) {
+ assert.expect(1);
+
+ await this.start();
+ const message = this.env.models['mail.message'].create({
+ id: 11,
+ tracking_value_ids: [{
+ changed_field: "Name",
+ field_type: "text",
+ id: 6,
+ new_value: "Marc",
+ old_value: "",
+ }],
+ });
+ await this.createMessageComponent(message);
+ assert.strictEqual(
+ document.querySelector('.o_Message_trackingValue').textContent,
+ "Name:Marc",
+ "should display the correct content of tracked field of type text: from empty to some text (Name: -> Marc)"
+ );
+});
+
+QUnit.test('rendering of tracked field of type selection: from a selection to no selection', async function (assert) {
+ assert.expect(1);
+
+ await this.start();
+ const message = this.env.models['mail.message'].create({
+ id: 11,
+ tracking_value_ids: [{
+ changed_field: "State",
+ field_type: "selection",
+ id: 6,
+ new_value: "",
+ old_value: "ok",
+ }],
+ });
+ await this.createMessageComponent(message);
+ assert.strictEqual(
+ document.querySelector('.o_Message_trackingValue').textContent,
+ "State:ok",
+ "should display the correct content of tracked field of type selection: from a selection to no selection (State: ok ->)"
+ );
+});
+
+QUnit.test('rendering of tracked field of type selection: from no selection to a selection', async function (assert) {
+ assert.expect(1);
+
+ await this.start();
+ const message = this.env.models['mail.message'].create({
+ id: 11,
+ tracking_value_ids: [{
+ changed_field: "State",
+ field_type: "selection",
+ id: 6,
+ new_value: "ok",
+ old_value: "",
+ }],
+ });
+ await this.createMessageComponent(message);
+ assert.strictEqual(
+ document.querySelector('.o_Message_trackingValue').textContent,
+ "State:ok",
+ "should display the correct content of tracked field of type selection: from no selection to a selection (State: -> ok)"
+ );
+});
+
+QUnit.test('rendering of tracked field of type many2one: from having a related record to no related record', async function (assert) {
+ assert.expect(1);
+
+ await this.start();
+ const message = this.env.models['mail.message'].create({
+ id: 11,
+ tracking_value_ids: [{
+ changed_field: "Author",
+ field_type: "many2one",
+ id: 6,
+ new_value: "",
+ old_value: "Marc",
+ }],
+ });
+ await this.createMessageComponent(message);
+ assert.strictEqual(
+ document.querySelector('.o_Message_trackingValue').textContent,
+ "Author:Marc",
+ "should display the correct content of tracked field of type many2one: from having a related record to no related record (Author: Marc ->)"
+ );
+});
+
+QUnit.test('rendering of tracked field of type many2one: from no related record to having a related record', async function (assert) {
+ assert.expect(1);
+
+ await this.start();
+ const message = this.env.models['mail.message'].create({
+ id: 11,
+ tracking_value_ids: [{
+ changed_field: "Author",
+ field_type: "many2one",
+ id: 6,
+ new_value: "Marc",
+ old_value: "",
+ }],
+ });
+ await this.createMessageComponent(message);
+ assert.strictEqual(
+ document.querySelector('.o_Message_trackingValue').textContent,
+ "Author:Marc",
+ "should display the correct content of tracked field of type many2one: from no related record to having a related record (Author: -> Marc)"
);
});
diff --git a/addons/mail/static/tests/helpers/mock_server.js b/addons/mail/static/tests/helpers/mock_server.js
index dc027c679f5..3574a57ca34 100644
--- a/addons/mail/static/tests/helpers/mock_server.js
+++ b/addons/mail/static/tests/helpers/mock_server.js
@@ -1218,12 +1218,16 @@ MockServer.include({
notifications = this._mockMailNotification_NotificationFormat(
notifications.map(notification => notification.id)
);
+ const trackingValueIds = this._getRecords('mail.tracking.value', [
+ ['id', 'in', message.tracking_value_ids],
+ ]);
const response = Object.assign({}, message, {
attachment_ids: formattedAttachments,
author_id: formattedAuthor,
history_partner_ids: historyPartnerIds,
needaction_partner_ids: needactionPartnerIds,
notifications,
+ tracking_value_ids: trackingValueIds,
});
if (message.subtype_id) {
const subtype = this._getRecords('mail.message.subtype', [