diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py index deb0df7cdee..c03ebc3f9ec 100644 --- a/addons/crm/models/crm_lead.py +++ b/addons/crm/models/crm_lead.py @@ -147,7 +147,7 @@ class Lead(models.Model): # Revenues expected_revenue = fields.Monetary('Expected Revenue', currency_field='company_currency', tracking=True) prorated_revenue = fields.Monetary('Prorated Revenue', currency_field='company_currency', store=True, compute="_compute_prorated_revenue") - recurring_revenue = fields.Monetary('Recurring Revenues', currency_field='company_currency', groups="crm.group_use_recurring_revenues") + recurring_revenue = fields.Monetary('Recurring Revenues', currency_field='company_currency', groups="crm.group_use_recurring_revenues", tracking=True) recurring_plan = fields.Many2one('crm.recurring.plan', string="Recurring Plan", groups="crm.group_use_recurring_revenues") recurring_revenue_monthly = fields.Monetary('Expected MRR', currency_field='company_currency', store=True, compute="_compute_recurring_revenue_monthly", diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py index 3477881ebe1..6dd9c5edb6f 100644 --- a/addons/mail/models/mail_message.py +++ b/addons/mail/models/mail_message.py @@ -985,6 +985,7 @@ class Message(models.Model): 'old_value': tracking.get_old_display_value()[0], 'new_value': tracking.get_new_display_value()[0], 'field_type': tracking.field_type, + 'currency_id': tracking.currency_id.id, }) vals.update({ diff --git a/addons/mail/models/mail_tracking_value.py b/addons/mail/models/mail_tracking_value.py index 472555a53ba..2edfab5abfc 100644 --- a/addons/mail/models/mail_tracking_value.py +++ b/addons/mail/models/mail_tracking_value.py @@ -31,6 +31,9 @@ class MailTracking(models.Model): new_value_text = fields.Text('New Value Text', readonly=1) new_value_datetime = fields.Datetime('New Value Datetime', readonly=1) + currency_id = fields.Many2one('res.currency', 'Currency', readonly=True, ondelete='set null', + help="Used to display the currency when tracking monetary values") + mail_message_id = fields.Many2one('mail.message', 'Message ID', required=True, index=True, ondelete='cascade') tracking_sequence = fields.Integer('Tracking field sequence', readonly=1, default=100) diff --git a/addons/mail/models/models.py b/addons/mail/models/models.py index f68cf42ed5f..947100de01a 100644 --- a/addons/mail/models/models.py +++ b/addons/mail/models/models.py @@ -46,6 +46,8 @@ class BaseModel(models.AbstractModel): tracking_sequence = 100 tracking = self.env['mail.tracking.value'].create_tracking_values(initial_value, new_value, col_name, col_info, tracking_sequence, self._name) if tracking: + if tracking['field_type'] == 'monetary': + tracking['currency_id'] = getattr(self, col_info.get('currency_field', ''), self.company_id.currency_id).id tracking_value_ids.append([0, 0, tracking]) changes.add(col_name) diff --git a/addons/mail/static/src/components/message/message.js b/addons/mail/static/src/components/message/message.js index 6d6e64e2b18..52333f2cd5b 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; @@ -310,6 +311,14 @@ class Message extends Component { if (value.new_value !== undefined){ value.new_value = value.new_value ? this.env._t("True") : this.env._t("False"); } + } else if (value.field_type === 'monetary' && value.currency_id) { + const currency_id = this.env.session.currencies[value.currency_id] + if (value.old_value !== undefined) { + value.old_value = format.monetary(value.old_value, null, { currency: currency_id, forceString: true }); + } + if (value.new_value !== undefined) { + value.new_value = format.monetary(value.new_value, null, { currency: currency_id, forceString: true }); + } } return value; }); diff --git a/addons/mail/static/src/components/message/message_tests.js b/addons/mail/static/src/components/message/message_tests.js index 8aaf1d2a268..99f7bf24aae 100644 --- a/addons/mail/static/src/components/message/message_tests.js +++ b/addons/mail/static/src/components/message/message_tests.js @@ -1212,6 +1212,71 @@ QUnit.test('rendering of tracked field with change of value from empty to string ); }); +QUnit.test('basic rendering of tracking value (monetary type)', async function (assert) { + assert.expect(8); + + await this.start({ + env: { + session: { + currencies: { 1: { symbol: '$', position: 'before' } }, + }, + }, + }); + const message = this.env.models['mail.message'].create({ + id: 11, + tracking_value_ids: [{ + changed_field: "Revenue", + currency_id: 1, + field_type: "monetary", + id: 6, + new_value: 500, + old_value: 1000, + }], + }); + + await this.createMessageComponent(message); + assert.containsOnce( + document.body, + '.o_Message_trackingValue', + "should display a tracking value" + ); + assert.containsOnce( + document.body, + '.o_Message_trackingValueFieldName', + "should display the name of the tracked field" + ); + assert.strictEqual( + document.querySelector('.o_Message_trackingValueFieldName').textContent, + "Revenue:", + "should display the correct tracked field name (Revenue)", + ); + assert.containsOnce( + document.body, + '.o_Message_trackingValueOldValue', + "should display the old value" + ); + assert.strictEqual( + document.querySelector('.o_Message_trackingValueOldValue').innerHTML, + "$ 1000.00", + "should display the correct old value with the currency symbol ($ 1000.00)", + ); + assert.containsOnce( + document.body, + '.o_Message_trackingValueSeparator', + "should display the separator" + ); + assert.containsOnce( + document.body, + '.o_Message_trackingValueNewValue', + "should display the new value" + ); + assert.strictEqual( + document.querySelector('.o_Message_trackingValueNewValue').innerHTML, + "$ 500.00", + "should display the correct new value with the currency symbol ($ 500.00)", + ); +}); + }); }); }); diff --git a/addons/mail/tests/common.py b/addons/mail/tests/common.py index 1d74e915bc8..7f9485d59a5 100644 --- a/addons/mail/tests/common.py +++ b/addons/mail/tests/common.py @@ -662,6 +662,9 @@ class MailCase(MockEmail): self.assertEqual(tracking.new_value_integer, new_value and new_value.id or False) self.assertEqual(tracking.old_value_char, old_value and old_value.display_name or '') self.assertEqual(tracking.new_value_char, new_value and new_value.display_name or '') + elif value_type in ('monetary'): + self.assertEqual(tracking.old_value_monetary, old_value) + self.assertEqual(tracking.new_value_monetary, new_value) else: self.assertEqual(1, 0) diff --git a/addons/test_mail/models/test_mail_corner_case_models.py b/addons/test_mail/models/test_mail_corner_case_models.py index 6ede8e2f735..b6547223468 100644 --- a/addons/test_mail/models/test_mail_corner_case_models.py +++ b/addons/test_mail/models/test_mail_corner_case_models.py @@ -42,6 +42,15 @@ class MailTestTrackCompute(models.Model): partner_email = fields.Char(related='partner_id.email', store=True, tracking=True) partner_phone = fields.Char(related='partner_id.phone', tracking=True) +class MailTestTrackMonetary(models.Model): + _name = 'mail.test.track.monetary' + _description = 'Test tracking monetary field' + _inherit = ['mail.thread'] + + company_id = fields.Many2one('res.company') + company_currency = fields.Many2one("res.currency", string='Currency', related='company_id.currency_id', readonly=True, tracking=True) + revenue = fields.Monetary('Revenue', currency_field='company_currency', tracking=True) + class MailTestMultiCompany(models.Model): """ This model can be used in multi company tests""" diff --git a/addons/test_mail/security/ir.model.access.csv b/addons/test_mail/security/ir.model.access.csv index 46aa2a2f50e..2f2fe28732d 100644 --- a/addons/test_mail/security/ir.model.access.csv +++ b/addons/test_mail/security/ir.model.access.csv @@ -18,3 +18,4 @@ access_mail_test_cc_user,mail.test.cc.user,model_mail_test_cc,base.group_user,1, access_mail_test_multi_company_user,mail.test.multi.company.user,model_mail_test_multi_company,base.group_user,1,1,1,1 access_mail_test_multi_company_portal,mail.test.multi.company.portal,model_mail_test_multi_company,base.group_portal,1,0,0,0 access_mail_test_track_compute,mail.test.track.compute,model_mail_test_track_compute,base.group_user,1,1,1,1 +access_mail_test_track_monetary,mail.test.track.monetary,model_mail_test_track_monetary,base.group_user,1,1,1,1 diff --git a/addons/test_mail/tests/common.py b/addons/test_mail/tests/common.py index 0d1f3beb6f4..5ac033e76d8 100644 --- a/addons/test_mail/tests/common.py +++ b/addons/test_mail/tests/common.py @@ -43,6 +43,7 @@ class TestMailMultiCompanyCommon(MailCommon): super(TestMailMultiCompanyCommon, cls).setUpClass() cls.company_2 = cls.env['res.company'].create({ 'name': 'Second Test Company', + 'currency_id': 2, }) diff --git a/addons/test_mail/tests/test_message_track.py b/addons/test_mail/tests/test_message_track.py index df024c1e094..924691dc9df 100644 --- a/addons/test_mail/tests/test_message_track.py +++ b/addons/test_mail/tests/test_message_track.py @@ -3,7 +3,7 @@ from unittest.mock import patch -from odoo.addons.test_mail.tests.common import TestMailCommon +from odoo.addons.test_mail.tests.common import TestMailCommon, TestMailMultiCompanyCommon from odoo.tests.common import tagged from odoo.tests import Form @@ -283,6 +283,44 @@ class TestTracking(TestMailCommon): ('partner_email', 'char', 'foo@example.com', 'bar@example.com'), ]) +@tagged('mail_track') +class TestTrackingMonetary(TestMailMultiCompanyCommon): + + def setUp(self): + super(TestTrackingMonetary, self).setUp() + + record = self.env['mail.test.track.monetary'].with_user(self.user_employee).with_context(self._test_context).create({ + 'company_id': self.user_employee.company_id.id, + }) + self.flush_tracking() + self.record = record.with_context(mail_notrack=False) + + def test_message_track_monetary(self): + """ Update a record with a tracked monetary field """ + + # Check if the tracking value have the correct currency and values + self.record.write({ + 'revenue': 100, + }) + self.flush_tracking() + self.assertEqual(len(self.record.message_ids), 1) + + self.assertTracking(self.record.message_ids[0], [ + ('revenue', 'monetary', 0, 100), + ]) + + # Check if the tracking value have the correct currency and values after changing the value and the company + self.record.write({ + 'revenue': 200, + 'company_id': self.company_2.id, + }) + self.flush_tracking() + self.assertEqual(len(self.record.message_ids), 2) + + self.assertTracking(self.record.message_ids[0], [ + ('revenue', 'monetary', 100, 200), + ('company_currency', 'many2one', self.user_employee.company_id.currency_id, self.company_2.currency_id) + ]) @tagged('mail_track') class TestTrackingInternals(TestMailCommon):