From 3751379f1e9a4c215fb6eb898b4ccc67659b9ace Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Tue, 10 May 2022 21:51:50 +0700 Subject: initial commit 2 --- addons/note/models/__init__.py | 6 ++ addons/note/models/mail_activity.py | 16 +++++ addons/note/models/note.py | 137 ++++++++++++++++++++++++++++++++++++ addons/note/models/res_users.py | 73 +++++++++++++++++++ 4 files changed, 232 insertions(+) create mode 100644 addons/note/models/__init__.py create mode 100644 addons/note/models/mail_activity.py create mode 100644 addons/note/models/note.py create mode 100644 addons/note/models/res_users.py (limited to 'addons/note/models') diff --git a/addons/note/models/__init__.py b/addons/note/models/__init__.py new file mode 100644 index 00000000..db6de6e3 --- /dev/null +++ b/addons/note/models/__init__.py @@ -0,0 +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 00000000..67f7ea82 --- /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/note.py b/addons/note/models/note.py new file mode 100644 index 00000000..4df70de7 --- /dev/null +++ b/addons/note/models/note.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models, _ +from odoo.tools import html2plaintext + + +class Stage(models.Model): + + _name = "note.stage" + _description = "Note Stage" + _order = 'sequence' + + name = fields.Char('Stage Name', translate=True, required=True) + sequence = fields.Integer(help="Used to order the note stages", default=1) + user_id = fields.Many2one('res.users', string='Owner', required=True, ondelete='cascade', default=lambda self: self.env.uid, help="Owner of the note stage") + fold = fields.Boolean('Folded by Default') + + +class Tag(models.Model): + + _name = "note.tag" + _description = "Note Tag" + + name = fields.Char('Tag Name', required=True, translate=True) + color = fields.Integer('Color Index') + + _sql_constraints = [ + ('name_uniq', 'unique (name)', "Tag name already exists !"), + ] + + +class Note(models.Model): + + _name = 'note.note' + _inherit = ['mail.thread', 'mail.activity.mixin'] + _description = "Note" + _order = 'sequence' + + def _get_default_stage_id(self): + return self.env['note.stage'].search([('user_id', '=', self.env.uid)], limit=1) + + name = fields.Text(compute='_compute_name', string='Note Summary', store=True) + user_id = fields.Many2one('res.users', string='Owner', default=lambda self: self.env.uid) + memo = fields.Html('Note Content') + sequence = fields.Integer('Sequence') + stage_id = fields.Many2one('note.stage', compute='_compute_stage_id', + inverse='_inverse_stage_id', string='Stage', default=_get_default_stage_id) + stage_ids = fields.Many2many('note.stage', 'note_stage_rel', 'note_id', 'stage_id', + string='Stages of Users', default=_get_default_stage_id) + open = fields.Boolean(string='Active', default=True) + date_done = fields.Date('Date done') + color = fields.Integer(string='Color Index') + tag_ids = fields.Many2many('note.tag', 'note_tags_rel', 'note_id', 'tag_id', string='Tags') + message_partner_ids = fields.Many2many( + comodel_name='res.partner', string='Followers (Partners)', + compute='_get_followers', search='_search_follower_partners', + compute_sudo=True) + message_channel_ids = fields.Many2many( + comodel_name='mail.channel', string='Followers (Channels)', + compute='_get_followers', search='_search_follower_channels', + compute_sudo=True) + + @api.depends('memo') + def _compute_name(self): + """ Read the first line of the memo to determine the note name """ + for note in self: + text = html2plaintext(note.memo) if note.memo else '' + note.name = text.strip().replace('*', '').split("\n")[0] + + def _compute_stage_id(self): + first_user_stage = self.env['note.stage'].search([('user_id', '=', self.env.uid)], limit=1) + for note in self: + for stage in note.stage_ids.filtered(lambda stage: stage.user_id == self.env.user): + note.stage_id = stage + # note without user's stage + if not note.stage_id: + note.stage_id = first_user_stage + + def _inverse_stage_id(self): + for note in self.filtered('stage_id'): + note.stage_ids = note.stage_id + note.stage_ids.filtered(lambda stage: stage.user_id != self.env.user) + + @api.model + def name_create(self, name): + return self.create({'memo': name}).name_get()[0] + + @api.model + def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True): + if groupby and groupby[0] == "stage_id" and (len(groupby) == 1 or lazy): + stages = self.env['note.stage'].search([('user_id', '=', self.env.uid)]) + if stages: # if the user has some stages + result = [{ # notes by stage for stages user + '__context': {'group_by': groupby[1:]}, + '__domain': domain + [('stage_ids.id', '=', stage.id)], + 'stage_id': (stage.id, stage.name), + 'stage_id_count': self.search_count(domain + [('stage_ids', '=', stage.id)]), + '__fold': stage.fold, + } for stage in stages] + + # note without user's stage + nb_notes_ws = self.search_count(domain + [('stage_ids', 'not in', stages.ids)]) + if nb_notes_ws: + # add note to the first column if it's the first stage + dom_not_in = ('stage_ids', 'not in', stages.ids) + if result and result[0]['stage_id'][0] == stages[0].id: + dom_in = result[0]['__domain'].pop() + result[0]['__domain'] = domain + ['|', dom_in, dom_not_in] + result[0]['stage_id_count'] += nb_notes_ws + else: + # add the first stage column + result = [{ + '__context': {'group_by': groupby[1:]}, + '__domain': domain + [dom_not_in], + 'stage_id': (stages[0].id, stages[0].name), + 'stage_id_count': nb_notes_ws, + '__fold': stages[0].name, + }] + result + else: # if stage_ids is empty, get note without user's stage + nb_notes_ws = self.search_count(domain) + if nb_notes_ws: + result = [{ # notes for unknown stage + '__context': {'group_by': groupby[1:]}, + '__domain': domain, + 'stage_id': False, + 'stage_id_count': nb_notes_ws + }] + else: + result = [] + return result + return super(Note, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy) + + def action_close(self): + return self.write({'open': False, 'date_done': fields.date.today()}) + + def action_open(self): + return self.write({'open': True}) diff --git a/addons/note/models/res_users.py b/addons/note/models/res_users.py new file mode 100644 index 00000000..a2780f86 --- /dev/null +++ b/addons/note/models/res_users.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import logging + +from odoo import api, models, modules, _ + +_logger = logging.getLogger(__name__) + + +class Users(models.Model): + _name = 'res.users' + _inherit = ['res.users'] + + @api.model_create_multi + def create(self, vals_list): + users = super().create(vals_list) + user_group_id = self.env['ir.model.data'].xmlid_to_res_id('base.group_user') + # for new employee, create his own 5 base note stages + users.filtered_domain([('groups_id', 'in', [user_group_id])])._create_note_stages() + return users + + @api.model + def _init_data_user_note_stages(self): + emp_group_id = self.env.ref('base.group_user').id + query = """ +SELECT res_users.id +FROM res_users +WHERE res_users.active IS TRUE AND EXISTS ( + SELECT 1 FROM res_groups_users_rel WHERE res_groups_users_rel.gid = %s AND res_groups_users_rel.uid = res_users.id +) AND NOT EXISTS ( + SELECT 1 FROM note_stage stage WHERE stage.user_id = res_users.id +) +GROUP BY id""" + self.env.cr.execute(query, (emp_group_id,)) + uids = [res[0] for res in self.env.cr.fetchall()] + self.browse(uids)._create_note_stages() + + def _create_note_stages(self): + for num in range(4): + stage = self.env.ref('note.note_stage_%02d' % (num,), raise_if_not_found=False) + if not stage: + break + for user in self: + stage.sudo().copy(default={'user_id': user.id}) + else: + _logger.debug("Created note columns for %s", self) + + @api.model + def systray_get_activities(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).systray_get_activities() + notes_count = self.env['note.note'].search_count([('user_id', '=', self.env.uid)]) + if notes_count: + note_index = next((index for (index, a) in enumerate(activities) if a["model"] == "note.note"), None) + note_label = _('Notes') + if note_index is not None: + activities[note_index]['name'] = note_label + else: + activities.append({ + 'type': 'activity', + '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 -- cgit v1.2.3