diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 1e345cd9913..c17ae4247f2 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -373,10 +373,7 @@ class account_invoice(osv.osv): if context is None: context = {} try: - res = super(account_invoice, self).create(cr, uid, vals, context) - if res: - self.create_send_note(cr, uid, [res], context=context) - return res + return super(account_invoice, self).create(cr, uid, vals, context) except Exception, e: if '"journal_id" viol' in e.args[0]: raise orm.except_orm(_('Configuration Error!'), @@ -1079,7 +1076,7 @@ class account_invoice(osv.osv): if obj_inv.type in ('out_invoice', 'out_refund'): ctx = self.get_log_context(cr, uid, context=ctx) message = _("Invoice '%s' is validated.") % name - self.message_post(cr, uid, [inv_id], body=message, context=context) + self.message_post(cr, uid, [inv_id], body=message, subtype="account.mt_invoice_validated", context=context) return True def action_cancel(self, cr, uid, ids, context=None): @@ -1335,11 +1332,6 @@ class account_invoice(osv.osv): } return type_dict.get(type, 'Invoice') - def create_send_note(self, cr, uid, ids, context=None): - for obj in self.browse(cr, uid, ids, context=context): - self.message_post(cr, uid, [obj.id], body=_("%s created.") % (self._get_document_type(obj.type)), - subtype="account.mt_invoice_new", context=context) - def confirm_paid_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): self.message_post(cr, uid, [obj.id], body=_("%s paid.") % (self._get_document_type(obj.type)), diff --git a/addons/account/data/account_data.xml b/addons/account/data/account_data.xml index f8a0fcdfcaa..96b021650ce 100644 --- a/addons/account/data/account_data.xml +++ b/addons/account/data/account_data.xml @@ -151,13 +151,14 @@ account.invoice - - - created + + + Validated account.invoice + - paid + Paid account.invoice diff --git a/addons/account_voucher/account_voucher_data.xml b/addons/account_voucher/account_voucher_data.xml index fc008e479e9..7e11f625d9e 100644 --- a/addons/account_voucher/account_voucher_data.xml +++ b/addons/account_voucher/account_voucher_data.xml @@ -13,7 +13,7 @@

You can track customer payments easily and automate follow-ups. You get an overview of the discussion with your customers on each invoice for easier traceability. For advanced accounting features, you should install the "Accounting and Finance" module.

]]>
- + Status Change account.voucher diff --git a/addons/analytic/__openerp__.py b/addons/analytic/__openerp__.py index 6227cd369d3..417d2b29b20 100644 --- a/addons/analytic/__openerp__.py +++ b/addons/analytic/__openerp__.py @@ -38,8 +38,7 @@ that have no counterpart in the general financial accounts. 'security/analytic_security.xml', 'security/ir.model.access.csv', 'analytic_sequence.xml', - 'analytic_view.xml', - 'analytic_data.xml' + 'analytic_view.xml' ], 'demo': [], 'installable': True, diff --git a/addons/analytic/analytic.py b/addons/analytic/analytic.py index bae2d892526..7b5a372c569 100644 --- a/addons/analytic/analytic.py +++ b/addons/analytic/analytic.py @@ -320,7 +320,7 @@ class account_analytic_account(osv.osv): if obj.partner_id: message = _("Contract for %s has been created.") % (obj.partner_id.name,) self.message_post(cr, uid, [obj.id], body=message, - subtype="analytic.mt_account_status", context=context) + context=context) account_analytic_account() diff --git a/addons/analytic/analytic_data.xml b/addons/analytic/analytic_data.xml deleted file mode 100644 index 67940fb2063..00000000000 --- a/addons/analytic/analytic_data.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - Status Change - account.analytic.account - - - - diff --git a/addons/analytic_contract_crm/__init__.py b/addons/analytic_contract_crm/__init__.py new file mode 100644 index 00000000000..d9f03c8c56c --- /dev/null +++ b/addons/analytic_contract_crm/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import analytic_contract_crm + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/analytic_contract_crm/__openerp__.py b/addons/analytic_contract_crm/__openerp__.py new file mode 100644 index 00000000000..096c22c7d9b --- /dev/null +++ b/addons/analytic_contract_crm/__openerp__.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + + +{ + 'name': 'CRM: Contract link', + 'version': '1.1', + 'category': 'Hidden', + 'description': """ +This module is for set subtype of sales team in contract. +====================================================================================================== +""", + 'author': 'OpenERP S.A.', + 'website': 'http://www.openerp.com/', + 'depends': ['account_analytic_analysis','crm'], + 'data': [], + 'demo': [], + 'css' : [], + 'installable': True, + 'auto_install': True, +} + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/analytic_contract_crm/analytic_contract_crm.py b/addons/analytic_contract_crm/analytic_contract_crm.py new file mode 100644 index 00000000000..8dfdca08d41 --- /dev/null +++ b/addons/analytic_contract_crm/analytic_contract_crm.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from osv import osv, fields +from tools.translate import _ + +class account_analytic_account(osv.osv): + _inherit = "account.analytic.account" + + def create(self, cr, uid, vals, context=None): + obj_id = super(account_analytic_account, self).create(cr, uid, vals, context=context) + manager_id = self.browse(cr, uid, obj_id, context=context).manager_id + if manager_id: + if manager_id.default_section_id: + # subscribe salesteam followers & subtypes to the contract + self._subscribe_followers_subtype(cr, uid, [obj_id], manager_id.default_section_id, 'crm.case.section', context=context) + if obj_id: + self.create_send_note(cr, uid, [obj_id], context=context) + return obj_id + + def write(self, cr, uid, ids, vals, context=None): + if isinstance(ids, (int, long)): + ids = [ids] + if vals.get('manager_id'): + section_id = self.pool.get('res.users').browse(cr, uid, vals.get('manager_id'), context=context).default_section_id + if section_id: + vals.setdefault('message_follower_ids', []) + vals['message_follower_ids'] += [(6, 0,[follower.id]) for follower in section_id.message_follower_ids] + res = super(account_analytic_account, self).write(cr, uid, ids, vals, context=context) + # subscribe new salesteam followers & subtypes to the contract + if vals.get('manager_id'): + if section_id: + self._subscribe_followers_subtype(cr, uid, ids, section_id, 'crm.case.section', context=context) + return res + +account_analytic_account() + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 5bf16384642..1127edccba0 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -315,8 +315,8 @@ class crm_lead(base_stage, format_address, osv.osv): obj_id = super(crm_lead, self).create(cr, uid, vals, context) section_id = self.browse(cr, uid, obj_id, context=context).section_id if section_id: - followers = [follow.id for follow in section_id.message_follower_ids] - self.message_subscribe(cr, uid, [obj_id], followers, context=context) + # subscribe salesteam followers & subtypes to the lead + self._subscribe_followers_subtype(cr, uid, [obj_id], section_id, 'crm.case.section', context=context) return obj_id def onchange_stage_id(self, cr, uid, ids, stage_id, context=None): @@ -920,10 +920,13 @@ class crm_lead(base_stage, format_address, osv.osv): vals['probability'] = stage.probability if vals.get('section_id'): section_id = self.pool.get('crm.case.section').browse(cr, uid, vals.get('section_id'), context=context) - if section_id: - vals.setdefault('message_follower_ids', []) - vals['message_follower_ids'] += [(4, follower.id) for follower in section_id.message_follower_ids] - return super(crm_lead,self).write(cr, uid, ids, vals, context) + vals.setdefault('message_follower_ids', []) + vals['message_follower_ids'] += [(6, 0,[follower.id]) for follower in section_id.message_follower_ids] + res = super(crm_lead,self).write(cr, uid, ids, vals, context) + # subscribe new salesteam followers & subtypes to the lead + if vals.get('section_id'): + self._subscribe_followers_subtype(cr, uid, ids, vals.get('section_id'), 'crm.case.section', context=context) + return res # ---------------------------------------- # Mail Gateway @@ -992,7 +995,7 @@ class crm_lead(base_stage, format_address, osv.osv): def convert_opportunity_send_note(self, cr, uid, lead, context=None): message = _("Lead has been converted to an opportunity.") - lead.message_post(body=message) + lead.message_post(body=message, subtype="crm.mt_lead_convert_to_opportunity") return True def onchange_state(self, cr, uid, ids, state_id, context=None): diff --git a/addons/crm/crm_lead_data.xml b/addons/crm/crm_lead_data.xml index 8bc17c34081..de9d002b3f0 100644 --- a/addons/crm/crm_lead_data.xml +++ b/addons/crm/crm_lead_data.xml @@ -154,23 +154,60 @@ - - - Won - crm.lead - Opportunity <b>won</> - - - Lost + + + Lead Created crm.lead - Opportunity <b>lost</> - + + Opportunity Lost + crm.lead + + Opportunity <b>lost</b> + + + Opportunity Won + crm.lead + Opportunity <b>won</b> + + Stage Changed crm.lead - Stage <b>changed</> + Stage <b>changed</b> + + + Lead to Opportunity + crm.lead + + + + + + Lead Created + crm.case.section + + + + Lead to Opportunity + crm.case.section + + + + Lead Stage Changed + crm.case.section + + + + Opportunity Lost + crm.case.section + + + + Opportunity Won + crm.case.section +>>>>>>> MERGE-SOURCE diff --git a/addons/crm/crm_view.xml b/addons/crm/crm_view.xml index de73c16864d..95a64adaed6 100644 --- a/addons/crm/crm_view.xml +++ b/addons/crm/crm_view.xml @@ -128,7 +128,7 @@
- +
diff --git a/addons/event/event_data.xml b/addons/event/event_data.xml index 47efca17d6d..490a2b869e7 100644 --- a/addons/event/event_data.xml +++ b/addons/event/event_data.xml @@ -12,9 +12,9 @@ open
- + - New Registrations + New Registration event.event diff --git a/addons/hr_expense/hr_expense.py b/addons/hr_expense/hr_expense.py index ea20ce3cf4f..a09620868e5 100644 --- a/addons/hr_expense/hr_expense.py +++ b/addons/hr_expense/hr_expense.py @@ -128,6 +128,8 @@ class hr_expense_expense(osv.osv): for expense in self.browse(cr, uid, ids): if expense.employee_id and expense.employee_id.parent_id.user_id: self.message_subscribe_users(cr, uid, [expense.id], user_ids=[expense.employee_id.parent_id.user_id.id]) + self.message_post(cr, uid, ids, body=_("The request is waiting for Approval"), + subtype="hr_expense.mt_expense_approve") self.write(cr, uid, ids, { 'state':'confirm', 'date_confirm': time.strftime('%Y-%m-%d') @@ -135,6 +137,8 @@ class hr_expense_expense(osv.osv): return True def expense_accept(self, cr, uid, ids, *args): + self.message_post(cr, uid, ids, body=_("The request has been approved"), + subtype="hr_expense.mt_expense_approved") self.write(cr, uid, ids, { 'state':'accepted', 'date_valid':time.strftime('%Y-%m-%d'), @@ -143,6 +147,8 @@ class hr_expense_expense(osv.osv): return True def expense_canceled(self, cr, uid, ids, *args): + self.message_post(cr, uid, ids, body=_("Request refused"), + subtype="hr_expense.mt_expense_refused") self.write(cr, uid, ids, {'state':'cancelled'}) return True diff --git a/addons/hr_expense/hr_expense_data.xml b/addons/hr_expense/hr_expense_data.xml index f54156ac5ec..c5f1b99bb11 100644 --- a/addons/hr_expense/hr_expense_data.xml +++ b/addons/hr_expense/hr_expense_data.xml @@ -17,5 +17,25 @@ Expenses
+ + + + To Approve + hr.expense.expense + + + + + + Approved + hr.expense.expense + + + + Refused + hr.expense.expense + + + diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 630bce14e91..f501c2622e4 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -431,7 +431,7 @@ class hr_holidays(osv.osv): def holidays_confirm_notificate(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids): self.message_post(cr, uid, [obj.id], - _("Request submitted, waiting for validation by the manager."), context=context) + _("Request submitted, waiting for validation by the manager."), subtype="hr_holidays.mt_approve", context=context) def holidays_first_validate_notificate(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): @@ -445,12 +445,12 @@ class hr_holidays(osv.osv): _("Request validated."), context=context) else: self.message_post(cr, uid, [obj.id], - _("The request has been approved."), context=context) + _("The request has been approved."), subtype="hr_holidays.mt_approved", context=context) def holidays_refuse_notificate(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids): self.message_post(cr, uid, [obj.id], - _("Request refused"), context=context) + _("Request refused"), subtype="hr_holidays.mt_refused", context=context) class resource_calendar_leaves(osv.osv): diff --git a/addons/hr_holidays/hr_holidays_data.xml b/addons/hr_holidays/hr_holidays_data.xml index 5af0c60fc44..54240eeea0b 100644 --- a/addons/hr_holidays/hr_holidays_data.xml +++ b/addons/hr_holidays/hr_holidays_data.xml @@ -44,5 +44,23 @@ True brown + + + + To Approve + hr.holidays + + + + Approved + hr.holidays + + + + Refused + hr.holidays + + + diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index 9a3b82e388a..6ccb0a22c94 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -469,12 +469,6 @@ class hr_applicant(base_stage, osv.Model): # OpenChatter methods and notifications # ------------------------------------------------------- - def stage_set_send_note(self, cr, uid, ids, stage_id, context=None): - """ Override of the (void) default notification method. """ - if not stage_id: return True - stage_name = self.pool.get('hr.recruitment.stage').name_get(cr, uid, [stage_id], context=context)[0][1] - return self.message_post(cr, uid, ids, body=_("Stage changed to %s.") % (stage_name), context=context) - def case_get_note_msg_prefix(self, cr, uid, id, context=None): return 'Applicant' @@ -487,18 +481,18 @@ class hr_applicant(base_stage, osv.Model): context = {} for applicant in self.browse(cr, uid, ids, context=context): if applicant.job_id: - self.pool.get('hr.job').message_post(cr, uid, [applicant.job_id.id], body=_('New employee joined the company %s.')%(applicant.name,), subtype="hr_recruitment.mt_hired", context=context) + self.pool.get('hr.job').message_post(cr, uid, [applicant.job_id.id], body=_('New employee joined the company %s.')%(applicant.name,), context=context) if applicant.emp_id: message = _("Applicant has been hired and created as an employee.") - self.message_post(cr, uid, [applicant.id], body=message, context=context) + self.message_post(cr, uid, [applicant.id], body=message, subtype="hr_recruitment.mt_applicant_hired", context=context) else: message = _("Applicant has been hired.") - self.message_post(cr, uid, [applicant.id], body=message, context=context) + self.message_post(cr, uid, [applicant.id], body=message, subtype="hr_recruitment.mt_applicant_hired", context=context) return True def case_cancel_send_note(self, cr, uid, ids, context=None): msg = 'Applicant refused.' - return self.message_post(cr, uid, ids, body=msg, context=context) + return self.message_post(cr, uid, ids, body=msg, subtype="hr_recruitment.mt_applicant_refused",context=context) def case_reset_send_note(self, cr, uid, ids, context=None): message =_("Applicant has been set as new.") diff --git a/addons/hr_recruitment/hr_recruitment_data.xml b/addons/hr_recruitment/hr_recruitment_data.xml index adaeed81205..a9bc7440276 100644 --- a/addons/hr_recruitment/hr_recruitment_data.xml +++ b/addons/hr_recruitment/hr_recruitment_data.xml @@ -461,14 +461,42 @@ - - - Employee Hired - hr.job - + New Applicant hr.job + + Stage Changed + hr.job + + + + Applicant Hired + hr.job + + + + Refused + hr.job + + + + + Stage Changed + hr.applicant + + + + Applicant Hired + hr.applicant + + + + Applicant Refused + hr.applicant + + + diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py index 3d6733d0d73..89df9b45c88 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py @@ -95,19 +95,19 @@ class account_analytic_account(osv.osv): def set_cancel(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'state': 'cancelled'}, context=context) message = _("Contract has been canceled.") - self.message_post(cr, uid, ids, body=message, subtype="hr_timesheet_invoice.mt_account_canceled", context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True def set_open(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'state': 'open'}, context=context) message = _("Contract has been opened.") - self.message_post(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, subtype="hr_timesheet_invoice.mt_account_renewed", context=context) return True def set_pending(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'state': 'pending'}, context=context) message = _("Contract has been set as pending.") - self.message_post(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, subtype="hr_timesheet_invoice.mt_account_renew", context=context) return True account_analytic_account() diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice_data.xml b/addons/hr_timesheet_invoice/hr_timesheet_invoice_data.xml index e82b6fd19e8..895329debfc 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice_data.xml +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice_data.xml @@ -22,15 +22,35 @@ 20.0 - - - finished + + + Contract to Renew account.analytic.account - - canceled + + Contract Finished account.analytic.account + + Contract Renewed + account.analytic.account + + + + Contract to Renew + crm.case.section + + + + Contract Finished + crm.case.section + + + + Contract Renewed + crm.case.section + + diff --git a/addons/mail/data/mail_data.xml b/addons/mail/data/mail_data.xml index b095c54247f..f59ddd1c3ea 100644 --- a/addons/mail/data/mail_data.xml +++ b/addons/mail/data/mail_data.xml @@ -27,6 +27,7 @@ 1000 + Discussions diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index cdd22d14134..b797001b26b 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -952,6 +952,28 @@ class mail_thread(osv.AbstractModel): self.check_access_rights(cr, uid, 'read') return self.write(cr, SUPERUSER_ID, ids, {'message_follower_ids': [(3, pid) for pid in partner_ids]}, context=context) + def _subscribe_followers_subtype(self, cr, uid, ids, res_id, model, context=None): + """ TDE note: not the best way to do this, we could override _get_followers + of task, and perform a better mapping of subtypes than a mapping + based on names. + However we will keep this implementation, maybe to be refactored + in 7.1 of future versions. """ + subtype_obj = self.pool.get('mail.message.subtype') + follower_obj = self.pool.get('mail.followers') + # create mapping + subtype_ids = subtype_obj.search(cr, uid, ['|', ('res_model', '=', False), ('res_model', '=', self._name)], context=context) + subtypes = subtype_obj.browse(cr, uid, subtype_ids, context=context) + # fetch subscriptions + follower_ids = follower_obj.search(cr, uid, [('res_model', '=', model), ('res_id', '=', res_id)], context=context) + # copy followers + for follower in follower_obj.browse(cr, uid, follower_ids, context=context): + if not follower.subtype_ids: + continue + subtype_names = [follower_subtype.name for follower_subtype in follower.subtype_ids] + subtype_ids = [subtype.id for subtype in subtypes if subtype.name in subtype_names] + self.message_subscribe(cr, uid, ids, [follower.partner_id.id], + subtype_ids=subtype_ids, context=context) + #------------------------------------------------------ # Thread state #------------------------------------------------------ diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js index 0645c2b9225..0bf49d97570 100644 --- a/addons/mail/static/src/js/mail_followers.js +++ b/addons/mail/static/src/js/mail_followers.js @@ -184,7 +184,6 @@ openerp_mail_followers = function(session, mail) { /** Fetch subtypes, only if current user is follower */ fetch_subtypes: function () { - var subtype_list_ul = this.$('.oe_subtype_list').empty(); if (! this.message_is_follower) return; var context = new session.web.CompoundContext(this.build_context(), {}); this.ds_model.call('message_get_subscription_data', [[this.view.datarecord.id], context]).then(this.proxy('display_subtypes')); @@ -193,14 +192,22 @@ openerp_mail_followers = function(session, mail) { /** Display subtypes: {'name': default, followed} */ display_subtypes:function (data) { var self = this; - var subtype_list_ul = this.$('.oe_subtype_list'); + var subtype = []; + var subtype_list_ul = this.$('.oe_subtype_list').empty().hide(); subtype_list_ul.empty(); var records = data[this.view.datarecord.id || this.view.dataset.ids[0]].message_subtype_data; _(records).each(function (record, record_name) { record.name = record_name; record.followed = record.followed || undefined; - $(session.web.qweb.render('mail.followers.subtype', {'record': record})).appendTo( self.$('.oe_subtype_list') ); + subtype.push(record); + }) + subtype.sort(function(a,b){return a.id - b.id}); + _(subtype).each(function (record) { + $(session.web.qweb.render('mail.followers.subtype', {'record': record})).appendTo(subtype_list_ul); }); + if (subtype.length > 1) { + subtype_list_ul.show(); + } }, do_follow: function () { @@ -228,10 +235,12 @@ openerp_mail_followers = function(session, mail) { checklist.push(parseInt($(record).data('id'))); } }); - var context = new session.web.CompoundContext(this.build_context(), {}); return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], [this.session.uid], this.message_is_follower ? checklist : undefined, context]) - .then(this.proxy('read_value')); + .then(this.proxy('read_value')).then(function(){ + if(checklist.length == 0){ + return self.do_unfollow();} + }); }, }); }; diff --git a/addons/mail/static/src/xml/mail_followers.xml b/addons/mail/static/src/xml/mail_followers.xml index 11698c1fb62..f0a603e7f98 100644 --- a/addons/mail/static/src/xml/mail_followers.xml +++ b/addons/mail/static/src/xml/mail_followers.xml @@ -12,11 +12,12 @@ Unfollow Following + +
+
- -
-
+

Followers

Invite others diff --git a/addons/project/project.py b/addons/project/project.py index 817258bba07..bcde18df786 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -317,9 +317,7 @@ class project(osv.osv): task_obj = self.pool.get('project.task') task_ids = task_obj.search(cr, uid, [('project_id', 'in', ids), ('state', '!=', 'done')]) task_obj.case_cancel(cr, uid, task_ids, context=context) - self.write(cr, uid, ids, {'state':'cancelled'}, context=context) - self.set_cancel_send_note(cr, uid, ids, context=context) - return True + return self.write(cr, uid, ids, {'state':'cancelled'}, context=context) def set_pending(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'state':'pending'}, context=context) @@ -565,9 +563,6 @@ def Project(): def set_pending_send_note(self, cr, uid, ids, context=None): return self.message_post(cr, uid, ids, body=_("Project is now pending."), context=context) - def set_cancel_send_note(self, cr, uid, ids, context=None): - return self.message_post(cr, uid, ids, body=_("Project has been canceled."), context=context) - def set_close_send_note(self, cr, uid, ids, context=None): return self.message_post(cr, uid, ids, body=_("Project has been closed."), context=context) @@ -993,7 +988,6 @@ class task(base_stage, osv.osv): self._check_child_task(cr, uid, ids, context=context) for task in tasks: self.case_set(cr, uid, [task.id], 'cancelled', {'remaining_hours': 0.0}, context=context) - self.case_cancel_send_note(cr, uid, [task.id], context=context) return True def do_open(self, cr, uid, ids, context=None): @@ -1085,11 +1079,13 @@ class task(base_stage, osv.osv): def set_kanban_state_blocked(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'kanban_state': 'blocked'}, context=context) - return False + self.case_block_send_note(cr, uid, ids, context=context) + return True def set_kanban_state_normal(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'kanban_state': 'normal'}, context=context) - return False + self.case_open_send_note(cr, uid, ids, context=context) + return True def set_kanban_state_done(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'kanban_state': 'done'}, context=context) @@ -1109,35 +1105,12 @@ class task(base_stage, osv.osv): }, context=context) return True - def _subscribe_project_followers_to_task(self, cr, uid, task_id, context=None): - """ TDE note: not the best way to do this, we could override _get_followers - of task, and perform a better mapping of subtypes than a mapping - based on names. - However we will keep this implementation, maybe to be refactored - in 7.1 of future versions. """ - # task followers are project followers, with matching subtypes - task_record = self.browse(cr, uid, task_id, context=context) - subtype_obj = self.pool.get('mail.message.subtype') - follower_obj = self.pool.get('mail.followers') - if task_record.project_id: - # create mapping - task_subtype_ids = subtype_obj.search(cr, uid, ['|', ('res_model', '=', False), ('res_model', '=', self._name)], context=context) - task_subtypes = subtype_obj.browse(cr, uid, task_subtype_ids, context=context) - # fetch subscriptions - follower_ids = follower_obj.search(cr, uid, [('res_model', '=', 'project.project'), ('res_id', '=', task_record.project_id.id)], context=context) - # copy followers - for follower in follower_obj.browse(cr, uid, follower_ids, context=context): - if not follower.subtype_ids: - continue - project_subtype_names = [project_subtype.name for project_subtype in follower.subtype_ids] - task_subtype_ids = [task_subtype.id for task_subtype in task_subtypes if task_subtype.name in project_subtype_names] - self.message_subscribe(cr, uid, [task_id], [follower.partner_id.id], - subtype_ids=task_subtype_ids, context=context) - def create(self, cr, uid, vals, context=None): task_id = super(task, self).create(cr, uid, vals, context=context) - # subscribe project followers to the task - self._subscribe_project_followers_to_task(cr, uid, task_id, context=context) + project_id = self.browse(cr, uid, task_id, context=context).project_id + if project_id: + # subscribe project followers to the task + self._subscribe_followers_subtype(cr, uid, [task_id], project_id, 'project.project', context=context) self._store_history(cr, uid, [task_id], context=context) return task_id @@ -1147,6 +1120,11 @@ class task(base_stage, osv.osv): def write(self, cr, uid, ids, vals, context=None): if isinstance(ids, (int, long)): ids = [ids] + if vals.get('project_id'): + project_id = self.pool.get('project.project').browse(cr, uid, vals.get('project_id'), context=context) + if project_id: + vals.setdefault('message_follower_ids', []) + vals['message_follower_ids'] += [(6, 0,[follower.id]) for follower in project_id.message_follower_ids] if vals and not 'kanban_state' in vals and 'stage_id' in vals: new_stage = vals.get('stage_id') vals_reset_kstate = dict(vals, kanban_state='normal') @@ -1165,8 +1143,7 @@ class task(base_stage, osv.osv): # subscribe new project followers to the task if vals.get('project_id'): - for id in ids: - self._subscribe_project_followers_to_task(cr, uid, id, context=context) + self._subscribe_followers_subtype(cr, uid, ids, vals.get('project_id'), 'project.project', context=context) return result def unlink(self, cr, uid, ids, context=None): @@ -1266,24 +1243,33 @@ class task(base_stage, osv.osv): res = super(task, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) return res + ['user_id', 'manager_id'] + def create_send_note(self, cr, uid, ids, context=None): + return self.message_post(cr, uid, ids, body=_("Task created."), subtype="project.mt_task_new", context=context) + def stage_set_send_note(self, cr, uid, ids, stage_id, context=None): """ Override of the (void) default notification method. """ stage_name = self.pool.get('project.task.type').name_get(cr, uid, [stage_id], context=context)[0][1] return self.message_post(cr, uid, ids, body=_("Stage changed to %s.") % (stage_name), context=context) - def create_send_note(self, cr, uid, ids, context=None): - return self.message_post(cr, uid, ids, body=_("Task has been created."), context=context) + def case_open_send_note(self, cr, uid, ids, context=None): + return self.message_post(cr, uid, ids, body=_("Task started."), subtype="project.mt_task_started", context=context) + + def case_close_send_note(self, cr, uid, ids, context=None): + return self.message_post(cr, uid, ids, body=_("Task closed."), subtype="project.mt_task_closed", context=context) + + def case_block_send_note(self, cr, uid, ids, context=None): + return self.message_post(cr, uid, ids, body=_("Task blocked."), subtype="project.mt_task_blocked", context=context) def case_draft_send_note(self, cr, uid, ids, context=None): - return self.message_post(cr, uid, ids, body=_('Task has been set as draft.'), context=context) + return self.message_post(cr, uid, ids, body=_('Task set as draft.'), context=context) def do_delegation_send_note(self, cr, uid, ids, context=None): for task in self.browse(cr, uid, ids, context=context): msg = _('Task has been delegated to %s.') % (task.user_id.name) self.message_post(cr, uid, [task.id], body=msg, context=context) return True - + def project_task_reevaluate(self, cr, uid, ids, context=None): if self.pool.get('res.users').has_group(cr, uid, 'project.group_time_work_estimation_tasks'): return { diff --git a/addons/project/project_data.xml b/addons/project/project_data.xml index 2fcb49c3894..cde0cdff974 100644 --- a/addons/project/project_data.xml +++ b/addons/project/project_data.xml @@ -78,39 +78,42 @@ - - - New + + + Task Created project.project - - Closed + + Task Started + project.project + + + + Task Blocked project.project - - Canceled + + Task Done project.project - - Stage Changed - project.project - - + - New + Task Created + project.task + + + + Task Started + project.task + + + + Task Blocked project.task - Closed - project.task - - - Canceled - project.task - - - Stage Changed + Task Done project.task diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index 499ebc3e2c5..587faf295a1 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -115,7 +115,7 @@
- +
diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index e256f263caf..26d58928907 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -233,6 +233,12 @@ class project_issue(base_stage, osv.osv): When the case is over, the status is set to \'Done\'.\ If the case needs to be reviewed then the status is \ set to \'Pending\'.'), + 'kanban_state': fields.selection([('normal', 'Normal'),('blocked', 'Blocked'),('done', 'Ready for next stage')], 'Kanban State', + help="A Issue's kanban state indicates special situations affecting it:\n" + " * Normal is the default situation\n" + " * Blocked indicates something is preventing the progress of this issue\n" + " * Ready for next stage indicates the issue is ready to be pulled to the next stage", + readonly=True, required=False), 'email_from': fields.char('Email', size=128, help="These people will receive email.", select=1), 'email_cc': fields.char('Watchers Emails', size=256, help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"), 'date_open': fields.datetime('Opened', readonly=True,select=True), @@ -279,6 +285,7 @@ class project_issue(base_stage, osv.osv): 'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, c), 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.helpdesk', context=c), 'priority': crm.AVAILABLE_PRIORITIES[2][0], + 'kanban_state': 'normal', } _group_by_full = { @@ -359,31 +366,6 @@ class project_issue(base_stage, osv.osv): return super(project_issue, self).copy(cr, uid, id, default=default, context=context) - def _subscribe_project_followers_to_issue(self, cr, uid, task_id, context=None): - """ TDE note: not the best way to do this, we could override _get_followers - of issue, and perform a better mapping of subtypes than a mapping - based on names. - However we will keep this implementation, maybe to be refactored - in 7.1 of future versions. """ - # task followers are project followers, with matching subtypes - task_record = self.browse(cr, uid, task_id, context=context) - subtype_obj = self.pool.get('mail.message.subtype') - follower_obj = self.pool.get('mail.followers') - if task_record.project_id: - # create mapping - task_subtype_ids = subtype_obj.search(cr, uid, ['|', ('res_model', '=', False), ('res_model', '=', self._name)], context=context) - task_subtypes = subtype_obj.browse(cr, uid, task_subtype_ids, context=context) - # fetch subscriptions - follower_ids = follower_obj.search(cr, uid, [('res_model', '=', 'project.project'), ('res_id', '=', task_record.project_id.id)], context=context) - # copy followers - for follower in follower_obj.browse(cr, uid, follower_ids, context=context): - if not follower.subtype_ids: - continue - project_subtype_names = [project_subtype.name for project_subtype in follower.subtype_ids] - task_subtype_ids = [task_subtype.id for task_subtype in task_subtypes if task_subtype.name in project_subtype_names] - self.message_subscribe(cr, uid, [task_id], [follower.partner_id.id], - subtype_ids=task_subtype_ids, context=context) - def write(self, cr, uid, ids, vals, context=None): #Update last action date every time the user change the stage, the state or send a new email logged_fields = ['stage_id', 'state', 'message_ids'] @@ -392,11 +374,14 @@ class project_issue(base_stage, osv.osv): # subscribe new project followers to the issue if vals.get('project_id'): - for id in ids: - self._subscribe_project_followers_to_issue(cr, uid, id, context=context) - - return super(project_issue, self).write(cr, uid, ids, vals, context) - + project_id = self.pool.get('project.project').browse(cr, uid, vals.get('project_id'), context=context) + vals.setdefault('message_follower_ids', []) + vals['message_follower_ids'] += [(6, 0,[follower.id]) for follower in project_id.message_follower_ids] + res = super(project_issue, self).write(cr, uid, ids, vals, context) + if vals.get('project_id'): + self._subscribe_followers_subtype(cr, uid, ids, vals.get('project_id'), 'project.project', context=context) + return res + def onchange_task_id(self, cr, uid, ids, task_id, context=None): if not task_id: return {'value': {}} @@ -413,8 +398,10 @@ class project_issue(base_stage, osv.osv): def create(self, cr, uid, vals, context=None): obj_id = super(project_issue, self).create(cr, uid, vals, context=context) - # subscribe project follower to the issue - self._subscribe_project_followers_to_issue(cr, uid, obj_id, context=context) + project_id = self.browse(cr, uid, obj_id, context=context).project_id + if project_id: + # subscribe project follower to the issue + self._subscribe_followers_subtype(cr, uid, [obj_id], project_id, 'project.project', context=context) self.create_send_note(cr, uid, [obj_id], context=context) return obj_id @@ -455,7 +442,6 @@ class project_issue(base_stage, osv.osv): def case_cancel(self, cr, uid, ids, context=None): """ Cancels case """ self.case_set(cr, uid, ids, 'cancelled', {'active': True}, context=context) - self.case_cancel_send_note(cr, uid, ids, context=context) return True def case_escalate(self, cr, uid, ids, context=None): @@ -536,7 +522,7 @@ class project_issue(base_stage, osv.osv): def stage_set_send_note(self, cr, uid, ids, stage_id, context=None): """ Override of the (void) default notification method. """ stage_name = self.pool.get('project.task.type').name_get(cr, uid, [stage_id], context=context)[0][1] - return self.message_post(cr, uid, ids, body= _("Stage changed to %s.") % (stage_name), subtype="mt_issue_new", context=context) + return self.message_post(cr, uid, ids, body=_("Stage changed to %s.") % (stage_name), context=context) def case_get_note_msg_prefix(self, cr, uid, id, context=None): """ Override of default prefix for notifications. """ @@ -548,7 +534,10 @@ class project_issue(base_stage, osv.osv): def create_send_note(self, cr, uid, ids, context=None): message = _("Project issue created.") - return self.message_post(cr, uid, ids, body=message, subtype="mt_issue_new", context=context) + return self.message_post(cr, uid, ids, body=message, subtype="project_issue.mt_issue_new", context=context) + + def case_open_send_note(self, cr, uid, ids, context=None): + return self.message_post(cr, uid, ids, body=_("Issue started."), subtype="project_issue.mt_issue_started", context=context) def case_escalate_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): @@ -560,6 +549,25 @@ class project_issue(base_stage, osv.osv): obj.message_post(body=message) return True + def case_block_send_note(self, cr, uid, ids, context=None): + return self.message_post(cr, uid, ids, body=_("Issue blocked."), subtype="project_issue.mt_issue_blocked", context=context) + + def case_close_send_note(self, cr, uid, ids, context=None): + return self.message_post(cr, uid, ids, body=_("Project issue closed."), subtype="project_issue.mt_issue_closed", context=context) + + def set_kanban_state_blocked(self, cr, uid, ids, context=None): + self.write(cr, uid, ids, {'kanban_state': 'blocked'}, context=context) + self.case_block_send_note(cr, uid, ids, context=context) + return True + + def set_kanban_state_normal(self, cr, uid, ids, context=None): + self.write(cr, uid, ids, {'kanban_state': 'normal'}, context=context) + self.case_open_send_note(cr, uid, ids, context=context) + return True + + def set_kanban_state_done(self, cr, uid, ids, context=None): + self.write(cr, uid, ids, {'kanban_state': 'done'}, context=context) + return False project_issue() class project(osv.osv): diff --git a/addons/project_issue/project_issue_data.xml b/addons/project_issue/project_issue_data.xml index c1460167097..395f81e3c3a 100644 --- a/addons/project_issue/project_issue_data.xml +++ b/addons/project_issue/project_issue_data.xml @@ -43,23 +43,46 @@ You can record issues, assign them to a responsible person, and keep track of th Access all issues from the top Project menu, and access the issues of a specific project via the projects gallery view.

]]>
- - - New + + + Issue Created + project.issue + + + + Issue Started + project.issue + + + + Issue Blocked + project.issue + + + + Issue Closed project.issue - - Closed - project.issue + + + Issue Created + project.project + - - Canceled - project.issue + + Issue Started + project.project + - - Stage Changed - project.issue + + Issue Blocked + project.project + - + + Issue Closed + project.project + + diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index 279fec28411..bb7bca59c51 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -203,6 +203,7 @@ +