diff options
| author | Dharmang Soni <dpr@openerp.com> | 2018-04-04 15:34:43 +0530 |
|---|---|---|
| committer | Thibault Delavallée <tde@odoo.com> | 2018-04-09 14:35:06 +0200 |
| commit | 807c4c3f5e729d8b2f463d93e73b0dbbc008492b (patch) | |
| tree | dc6774487c50efbd6c09b5ae642744582b3fe260 /addons/note | |
| parent | c56491b11bc399247a595a2e08bbc940d2be3746 (diff) | |
[REF] mail, note: move reminder feature from mail to note
This commit refactors f488dc58c37fdc7fd49df759165de641e213444b that added reminder feature in activities.
After merging it was suggested specifications were not correct and it has
been decided to move the whole reminder feature in note.
Reminders are now notes instead of activities. Now when having note appµ
systray proposes to create a note / reminder. A note.note record is created
with info given by the user. If a date is given in the systray quick create
then an activity is scheduled on this note.
This means user can create records in activity systray that are not
activities but notes used as reminders. Those will use the first availableµ
note column for the user.
As reminders are now notes the support of activities not linked to any
document is removed, as well as archiving activities and tests linked to
that behavior.
This commit has been not carefully tested nor in-depth reviewied. It is
considered as too important for the upcoming freeze. This closes task ID
1833628. Closes #24129 . Many thanks to @dpr-odoo for its quick development
and good work on this task.
Diffstat (limited to 'addons/note')
| -rw-r--r-- | addons/note/__init__.py | 3 | ||||
| -rw-r--r-- | addons/note/__manifest__.py | 5 | ||||
| -rw-r--r-- | addons/note/controllers/__init__.py | 4 | ||||
| -rw-r--r-- | addons/note/controllers/note.py | 27 | ||||
| -rw-r--r-- | addons/note/data/mail_activity_data.xml | 12 | ||||
| -rw-r--r-- | addons/note/models/__init__.py | 1 | ||||
| -rw-r--r-- | addons/note/models/mail_activity.py | 16 | ||||
| -rw-r--r-- | addons/note/models/res_users.py | 27 | ||||
| -rw-r--r-- | addons/note/static/src/js/systray.js | 138 | ||||
| -rw-r--r-- | addons/note/static/src/less/note.less | 38 | ||||
| -rw-r--r-- | addons/note/static/src/xml/systray.xml | 32 | ||||
| -rw-r--r-- | addons/note/static/tests/systray_tests.js | 66 | ||||
| -rw-r--r-- | addons/note/views/mail_activity_views.xml | 14 | ||||
| -rw-r--r-- | addons/note/views/note_templates.xml | 7 |
14 files changed, 388 insertions, 2 deletions
diff --git a/addons/note/__init__.py b/addons/note/__init__.py index dc5e6b693d1..3ea0f0cac45 100644 --- a/addons/note/__init__.py +++ b/addons/note/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from . import models +from . import controllers +from . import models
\ No newline at end of file diff --git a/addons/note/__manifest__.py b/addons/note/__manifest__.py index 7a0edd704a9..02cb893ca1d 100644 --- a/addons/note/__manifest__.py +++ b/addons/note/__manifest__.py @@ -15,13 +15,18 @@ 'data': [ 'security/note_security.xml', 'security/ir.model.access.csv', + 'data/mail_activity_data.xml', 'data/note_data.xml', 'views/note_views.xml', 'views/note_templates.xml', + 'views/mail_activity_views.xml', ], 'demo': [ 'data/note_demo.xml', ], + 'qweb': [ + 'static/src/xml/systray.xml', + ], 'test': [ ], 'installable': True, diff --git a/addons/note/controllers/__init__.py b/addons/note/controllers/__init__.py new file mode 100644 index 00000000000..4d2e8b5ad27 --- /dev/null +++ b/addons/note/controllers/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import note
\ No newline at end of file diff --git a/addons/note/controllers/note.py b/addons/note/controllers/note.py new file mode 100644 index 00000000000..bdcf2e85059 --- /dev/null +++ b/addons/note/controllers/note.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import http +from odoo.http import request + + +class NoteController(http.Controller): + + @http.route('/note/new', type='json', auth='user') + def note_new_from_systray(self, note, activity_type_id=None, date_deadline=None): + """ Route to create note and their activity directly from the systray """ + note = request.env['note.note'].create({'memo': note}) + if date_deadline: + activity_values = { + 'note': note.memo, + 'date_deadline': date_deadline, + 'res_model_id': request.env.ref("note.model_note_note").id, + 'res_id': note.id, + 'note_id': note.id, + } + if not activity_type_id: + activity_type_id = request.env['mail.activity.type'].sudo().search([('category', '=', 'reminder')], limit=1).id + if activity_type_id: + activity_values['activity_type_id'] = activity_type_id + request.env['mail.activity'].create(activity_values) + return note.id diff --git a/addons/note/data/mail_activity_data.xml b/addons/note/data/mail_activity_data.xml new file mode 100644 index 00000000000..5b2a5e6c905 --- /dev/null +++ b/addons/note/data/mail_activity_data.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <data noupdate="1"> + <record id="mail_activity_data_reminder" model="mail.activity.type"> + <field name="name">Reminder</field> + <field name="category">reminder</field> + <field name="icon">fa-tasks</field> + <field name="days">0</field> + <field name="sequence">20</field> + </record> + </data> +</odoo> diff --git a/addons/note/models/__init__.py b/addons/note/models/__init__.py index 5c651fca9a0..db6de6e3c7f 100644 --- a/addons/note/models/__init__.py +++ b/addons/note/models/__init__.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import mail_activity from . import note from . import res_users diff --git a/addons/note/models/mail_activity.py b/addons/note/models/mail_activity.py new file mode 100644 index 00000000000..67f7ea82966 --- /dev/null +++ b/addons/note/models/mail_activity.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models, fields + + +class MailActivityType(models.Model): + _inherit = "mail.activity.type" + + category = fields.Selection(selection_add=[('reminder', 'Reminder')]) + + +class MailActivity(models.Model): + _inherit = "mail.activity" + + note_id = fields.Many2one('note.note', string="Related Note", ondelete='cascade') diff --git a/addons/note/models/res_users.py b/addons/note/models/res_users.py index cfeba15922f..be218c27b28 100644 --- a/addons/note/models/res_users.py +++ b/addons/note/models/res_users.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import api, models +from odoo import api, models, modules, _ class Users(models.Model): @@ -19,3 +19,28 @@ class Users(models.Model): if stage: stage.sudo().copy(default={'user_id': user.id}) return user + + @api.model + def activity_user_count(self): + """ If user have not scheduled any note, it will not appear in activity menu. + Making note activity always visible with number of notes on label. If there is no notes, + activity menu not visible for note. + """ + activities = super(Users, self).activity_user_count() + notes_count = self.env['note.note'].search_count([('user_id', '=', self.env.uid), ('open', '=', True)]) + if notes_count: + note_index = next((index for (index, a) in enumerate(activities) if a["model"] == "note.note"), None) + note_label = ("%s (%d)") % (_('Notes'), notes_count) + if note_index is not None: + activities[note_index]['name'] = note_label + else: + activities.append({ + 'name': note_label, + 'model': 'note.note', + 'icon': modules.module.get_module_icon(self.env['note.note']._original_module), + 'total_count': 0, + 'today_count': 0, + 'overdue_count': 0, + 'planned_count': 0 + }) + return activities diff --git a/addons/note/static/src/js/systray.js b/addons/note/static/src/js/systray.js new file mode 100644 index 00000000000..54a2fe6bdd1 --- /dev/null +++ b/addons/note/static/src/js/systray.js @@ -0,0 +1,138 @@ +odoo.define('note.systray', function (require) { +"use strict"; + +var core = require('web.core'); +var ActivityMenu = require('mail.systray').ActivityMenu; +var datepicker = require('web.datepicker'); + +var _t = core._t; + +ActivityMenu.include({ + events: _.extend({}, ActivityMenu.prototype.events, { + 'click .o_note_show': '_onAddNoteClick', + 'click .o_note_save': '_onNoteSaveClick', + 'click .o_note_set_datetime': '_onNoteDateTimeSetClick', + 'keydown input.o_note_input': '_onNoteInputKeyDown', + 'click .o_note': '_onNewNoteClick', + }), + //-------------------------------------------------- + // Private + //-------------------------------------------------- + /** + * Moving notes at first place + * @override + */ + _getActivityData: function () { + var self = this; + return this._super.apply(this, arguments).then(function() { + var reminderIndex = _.findIndex(self.activities, function (val) { return val.model == 'note.note'; }); + if (reminderIndex > 0) { + self.activities.splice(0, 0, self.activities.splice(reminderIndex, 1)[0]); + } + }); + }, + /** + * Save the note to database using datepicker date and field as note + * @private + */ + _saveNote: function () { + var note = this.$('.o_note_input').val().trim(); + if (! note) { + return; + } + var params = {'note': note}; + var noteDateTime = this.noteDateTimeWidget.getValue(); + if (noteDateTime) { + params = _.extend(params, {'date_deadline': noteDateTime}); + } + this.$('.o_note_show').removeClass('hidden'); + this.$('.o_note').addClass('hidden'); + this._rpc({ + route: '/note/new', + params: params, + }).then(this._updateActivityPreview.bind(this)); + }, + //----------------------------------------- + // Handlers + //----------------------------------------- + /** + * @override + */ + _onActivityFilterClick: function (ev) { + var $el = $(ev.currentTarget); + if(!$el.hasClass("o_note")) { + var data = _.extend({}, $el.data(), $(ev.target).data()); + if (data.res_model === "note.note" && data.filter === "my") { + this.do_action({ + type: 'ir.actions.act_window', + name: data.model_name, + res_model: data.res_model, + views: [[false, 'kanban'], [false, 'form'], [false, 'list']] + }); + } else { + this._super.apply(this, arguments); + } + } + }, + /** + * When add new note button clicked, toggling quick note create view inside + * Systray activity view + * + * @private + * @param {MouseEvent} ev + */ + _onAddNoteClick: function (ev) { + ev.stopPropagation(); + if (!this.noteDateTimeWidget){ + this.noteDateTimeWidget = new datepicker.DateWidget(this, {useCurrent: true}); + } + this.noteDateTimeWidget.appendTo(this.$('.o_note_datetime')); + this.noteDateTimeWidget.$input.attr('placeholder', _t("Today")); + this.$('.o_note_show, .o_note').toggleClass('hidden'); + this.$('.o_note_input').val('').focus(); + + }, + /** + * When focusing on input for new quick note systerm tray must be open. + * Preventing to close + * + * @private + * @param {MouseEvent} ev + */ + _onNewNoteClick: function (ev) { + ev.stopPropagation(); + }, + /** + * Opens datetime picker for note. + * Quick FIX due to no option for set custom icon instead of caret in datepicker. + * + * @private + * @param {MouseEvent} ev + */ + _onNoteDateTimeSetClick: function (ev) { + ev.preventDefault(); + ev.stopPropagation(); + this.noteDateTimeWidget.$input.click(); + }, + /** + * Saving note (quick create) and updating activity preview + * + * @private + * @param {MouseEvent} ev + */ + _onNoteSaveClick: function (ev) { + this._saveNote(); + }, + /** + * Handling Enter key for quick create note. + * + * @private + * @param {KeyboardEvent} ev + */ + _onNoteInputKeyDown: function (ev) { + if (ev.which === $.ui.keyCode.ENTER) { + this._saveNote(); + } + }, +}); +}); diff --git a/addons/note/static/src/less/note.less b/addons/note/static/src/less/note.less index c92c5cd0500..926e7ddb913 100644 --- a/addons/note/static/src/less/note.less +++ b/addons/note/static/src/less/note.less @@ -23,3 +23,41 @@ } } } + +// Quick create notes from systray +.o_note.o_mail_channel_preview { + background-color: white; + .o_channel_info { + .o_channel_title { + .o_channel_name { + .o-flex(1, 1, 100%); + } + } + .o_note_input_box { + .o-flex-display(); + p { + .o-flex(1, 1, auto); + } + } + .o_note_save { + font-size: 11px; + font-weight: bold; + } + } + .o_note_input { + border: none; + } + .o_note_datetime { + .o_datepicker { + .o_datepicker_input { + float: right; + text-align: right; + border: none; + font-size: 11px; + } + .o_datepicker_button { + display: none; + } + } + } +}
\ No newline at end of file diff --git a/addons/note/static/src/xml/systray.xml b/addons/note/static/src/xml/systray.xml new file mode 100644 index 00000000000..4b2052c3b85 --- /dev/null +++ b/addons/note/static/src/xml/systray.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates> + <t t-extend="mail.chat.ActivityMenuPreview"> + <t t-jquery="t[t-foreach*='activities'][t-as*='activity']" t-operation="after"> + <div class="o_note_show"> + <a class="btn btn-block text-center">Add new note</a> + </div> + <div class="o_note o_mail_channel_preview hidden"> + <div class="o_mail_channel_image o_mail_channel_app"> + <img src="/note/static/description/icon.png"/> + </div> + <div class="o_channel_info"> + <div class="o_channel_title"> + <span class="o_channel_name"><strong>Add a note</strong></span> + <div class="o_note_datetime"/> + <span class="ml4"> + <a class="o_note_set_datetime text-muted"> + <span class="fa fa-clock-o"/> + </a> + </span> + </div> + <div class="o_note_input_box"> + <p><input class="o_note_input" type="text" placeholder="Remember..." /></p> + <span class="ml8 mr4"> + <a class="o_note_save">SAVE</a> + </span> + </div> + </div> + </div> + </t> + </t> +</templates>
\ No newline at end of file diff --git a/addons/note/static/tests/systray_tests.js b/addons/note/static/tests/systray_tests.js new file mode 100644 index 00000000000..2b07bf85757 --- /dev/null +++ b/addons/note/static/tests/systray_tests.js @@ -0,0 +1,66 @@ +odoo.define('note.systray_tests', function (require) { +"use strict"; + +var ChatManager = require('mail.ChatManager'); +var systray = require('mail.systray'); +var testUtils = require('web.test_utils'); +var createBusService = require('mail.testUtils').createBusService; + +QUnit.module('note', {}, function () { + +QUnit.module("ActivityMenu", { + beforeEach: function () { + this.services = [ChatManager, createBusService()]; + this.data = { + 'mail.activity.menu': { + records: [], + }, + }; + } +}); + +QUnit.test('note activity menu widget: create note from activity menu', function (assert) { + assert.expect(8); + var self = this; + var activityMenu = new systray.ActivityMenu(); + testUtils.addMockEnvironment(activityMenu, { + services: this.services, + mockRPC: function (route, args) { + if (args.method === 'activity_user_count') { + return $.when(self.data['mail.activity.menu']['records']); + } + if (route === '/note/new') { + return $.when(); + } + return this._super(route, args); + }, + }); + activityMenu.appendTo($('#qunit-fixture')); + assert.ok(activityMenu.$el.hasClass('o_mail_navbar_item'), 'should be the instance of widget'); + + // toggle quick create for note + var step = 1; + activityMenu.$('.dropdown-toggle').click(); + assert.strictEqual(activityMenu.$('.o_note_show').hasClass("hidden"), false, 'ActivityMenu should have Add new note CTA'); + activityMenu.$('.o_note_show').click(); + assert.strictEqual(activityMenu.$('.o_note_show').hasClass("hidden"), true, 'ActivityMenu should hide CTA when entering a new note'); + assert.strictEqual(activityMenu.$('.o_note').hasClass("hidden"), false, 'ActivityMenu should display input for new note'); + + // creating quick note without date + activityMenu.$("input.o_note_input").val("New Note"); + activityMenu.$(".o_note_save").click(); + assert.strictEqual(activityMenu.$('.o_note_show').hasClass("hidden"), false, 'ActivityMenu add note button should be displayed'); + assert.strictEqual(activityMenu.$('.o_note').hasClass("hidden"), true, 'ActivityMenu add note input should be hidden'); + + // creating quick note with date + step = 2; + activityMenu.$('.o_note_show').click(); + activityMenu.$('input.o_note_input').val("New Note"); + activityMenu.$('.o_note_set_datetime').click(); + activityMenu.$(".o_note_save").click(); + assert.strictEqual(activityMenu.$('.o_note_show').hasClass("hidden"), false, 'ActivityMenu add note button should be displayed'); + assert.strictEqual(activityMenu.$('.o_note').hasClass("hidden"), true, 'ActivityMenu add note input should be hidden'); + activityMenu.destroy(); +}); +}); +}); diff --git a/addons/note/views/mail_activity_views.xml b/addons/note/views/mail_activity_views.xml new file mode 100644 index 00000000000..bc980423e3f --- /dev/null +++ b/addons/note/views/mail_activity_views.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<odoo> + + <record id="mail_activity_type_view_form" model="ir.ui.view"> + <field name="name">mail.activity.type.form.inherit.note</field> + <field name="model">mail.activity.type</field> + <field name="inherit_id" ref="mail.mail_activity_type_view_form"/> + <field name="arch" type="xml"> + <xpath expr="//field[@name='category']" position="attributes"> + <attribute name="invisible">0</attribute> + </xpath> + </field> + </record> +</odoo>
\ No newline at end of file diff --git a/addons/note/views/note_templates.xml b/addons/note/views/note_templates.xml index 3b6c3e1acc4..c80d00d84e0 100644 --- a/addons/note/views/note_templates.xml +++ b/addons/note/views/note_templates.xml @@ -3,6 +3,13 @@ <template id="assets_backend" inherit_id="web.assets_backend"> <xpath expr="link[last()]" position="after"> <link rel="stylesheet" type="text/less" href="/note/static/src/less/note.less"/> + <script type="text/javascript" src="/note/static/src/js/systray.js"></script> + </xpath> + </template> + + <template id="qunit_suite" name="note_tests" inherit_id="web.qunit_suite"> + <xpath expr="//t[@t-set='head']" position="inside"> + <script type="text/javascript" src="/note/static/tests/systray_tests.js"></script> </xpath> </template> </odoo> |
