[MERGE] Merged branch holding subtype work by APA.

bzr revid: tde@openerp.com-20121218131142-2jb6wac6y9q6dc7v
This commit is contained in:
Thibault Delavallée 2012-12-18 14:11:42 +01:00
commit b95b32d644
36 changed files with 528 additions and 206 deletions

View File

@ -373,10 +373,7 @@ class account_invoice(osv.osv):
if context is None: if context is None:
context = {} context = {}
try: try:
res = super(account_invoice, self).create(cr, uid, vals, context) return super(account_invoice, self).create(cr, uid, vals, context)
if res:
self.create_send_note(cr, uid, [res], context=context)
return res
except Exception, e: except Exception, e:
if '"journal_id" viol' in e.args[0]: if '"journal_id" viol' in e.args[0]:
raise orm.except_orm(_('Configuration Error!'), raise orm.except_orm(_('Configuration Error!'),
@ -1079,7 +1076,7 @@ class account_invoice(osv.osv):
if obj_inv.type in ('out_invoice', 'out_refund'): if obj_inv.type in ('out_invoice', 'out_refund'):
ctx = self.get_log_context(cr, uid, context=ctx) ctx = self.get_log_context(cr, uid, context=ctx)
message = _("Invoice '%s' is validated.") % name 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 return True
def action_cancel(self, cr, uid, ids, context=None): def action_cancel(self, cr, uid, ids, context=None):
@ -1335,11 +1332,6 @@ class account_invoice(osv.osv):
} }
return type_dict.get(type, 'Invoice') 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 <b>created</b>.") % (self._get_document_type(obj.type)),
subtype="account.mt_invoice_new", context=context)
def confirm_paid_send_note(self, cr, uid, ids, context=None): def confirm_paid_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context): for obj in self.browse(cr, uid, ids, context=context):
self.message_post(cr, uid, [obj.id], body=_("%s <b>paid</b>.") % (self._get_document_type(obj.type)), self.message_post(cr, uid, [obj.id], body=_("%s <b>paid</b>.") % (self._get_document_type(obj.type)),

View File

@ -151,13 +151,14 @@
<field name="object">account.invoice</field> <field name="object">account.invoice</field>
</record> </record>
<!-- mail: subtypes --> <!-- Account-related subtypes for messaging / Chatter -->
<record id="mt_invoice_new" model="mail.message.subtype"> <record id="mt_invoice_validated" model="mail.message.subtype">
<field name="name">created</field> <field name="name">Validated</field>
<field name="res_model">account.invoice</field> <field name="res_model">account.invoice</field>
<field name="default" eval="False"/>
</record> </record>
<record id="mt_invoice_paid" model="mail.message.subtype"> <record id="mt_invoice_paid" model="mail.message.subtype">
<field name="name">paid</field> <field name="name">Paid</field>
<field name="res_model">account.invoice</field> <field name="res_model">account.invoice</field>
</record> </record>
</data> </data>

View File

@ -13,7 +13,7 @@
<p>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.</p>]]></field> <p>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.</p>]]></field>
</record> </record>
<!-- mail: subtypes --> <!-- Voucher-related subtypes for messaging / Chatter -->
<record id="mt_voucher" model="mail.message.subtype"> <record id="mt_voucher" model="mail.message.subtype">
<field name="name">Status Change</field> <field name="name">Status Change</field>
<field name="res_model">account.voucher</field> <field name="res_model">account.voucher</field>

View File

@ -38,8 +38,7 @@ that have no counterpart in the general financial accounts.
'security/analytic_security.xml', 'security/analytic_security.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'analytic_sequence.xml', 'analytic_sequence.xml',
'analytic_view.xml', 'analytic_view.xml'
'analytic_data.xml'
], ],
'demo': [], 'demo': [],
'installable': True, 'installable': True,

View File

@ -320,7 +320,7 @@ class account_analytic_account(osv.osv):
if obj.partner_id: if obj.partner_id:
message = _("Contract for <em>%s</em> has been <b>created</b>.") % (obj.partner_id.name,) message = _("Contract for <em>%s</em> has been <b>created</b>.") % (obj.partner_id.name,)
self.message_post(cr, uid, [obj.id], body=message, self.message_post(cr, uid, [obj.id], body=message,
subtype="analytic.mt_account_status", context=context) context=context)
account_analytic_account() account_analytic_account()

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<!-- mail: subtypes -->
<record id="mt_account_status" model="mail.message.subtype">
<field name="name">Status Change</field>
<field name="res_model">account.analytic.account</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
import analytic_contract_crm
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'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:

View File

@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
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:

View File

@ -315,8 +315,8 @@ class crm_lead(base_stage, format_address, osv.osv):
obj_id = super(crm_lead, self).create(cr, uid, vals, context) obj_id = super(crm_lead, self).create(cr, uid, vals, context)
section_id = self.browse(cr, uid, obj_id, context=context).section_id section_id = self.browse(cr, uid, obj_id, context=context).section_id
if section_id: if section_id:
followers = [follow.id for follow in section_id.message_follower_ids] # subscribe salesteam followers & subtypes to the lead
self.message_subscribe(cr, uid, [obj_id], followers, context=context) self._subscribe_followers_subtype(cr, uid, [obj_id], section_id, 'crm.case.section', context=context)
return obj_id return obj_id
def onchange_stage_id(self, cr, uid, ids, stage_id, context=None): 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 vals['probability'] = stage.probability
if vals.get('section_id'): if vals.get('section_id'):
section_id = self.pool.get('crm.case.section').browse(cr, uid, vals.get('section_id'), context=context) 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.setdefault('message_follower_ids', []) vals['message_follower_ids'] += [(6, 0,[follower.id]) for follower in section_id.message_follower_ids]
vals['message_follower_ids'] += [(4, follower.id) for follower in section_id.message_follower_ids] res = super(crm_lead,self).write(cr, uid, ids, vals, context)
return 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 # 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): def convert_opportunity_send_note(self, cr, uid, lead, context=None):
message = _("Lead has been <b>converted to an opportunity</b>.") message = _("Lead has been <b>converted to an opportunity</b>.")
lead.message_post(body=message) lead.message_post(body=message, subtype="crm.mt_lead_convert_to_opportunity")
return True return True
def onchange_state(self, cr, uid, ids, state_id, context=None): def onchange_state(self, cr, uid, ids, state_id, context=None):

View File

@ -154,23 +154,60 @@
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/> <field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
</record> </record>
<!-- mail subtype --> <!-- CRM-related subtypes for messaging / Chatter -->
<record id="crm.mt_crm_won" model="mail.message.subtype"> <record id="mt_lead_create" model="mail.message.subtype">
<field name="name">Won</field> <field name="name">Lead Created</field>
<field name="res_model">crm.lead</field>
<field name="description">Opportunity &lt;b&gt;won&lt;/&gt;</field>
</record>
<record id="crm.mt_crm_lost" model="mail.message.subtype">
<field name="name">Lost</field>
<field name="res_model">crm.lead</field> <field name="res_model">crm.lead</field>
<field name="default" eval="False"/> <field name="default" eval="False"/>
<field name="description">Opportunity &lt;b&gt;lost&lt;/&gt;</field>
</record> </record>
<record id="crm.mt_crm_stage" model="mail.message.subtype"> <record id="mt_lead_lost" model="mail.message.subtype">
<field name="name">Opportunity Lost</field>
<field name="res_model">crm.lead</field>
<field name="default" eval="False"/>
<field name="description">Opportunity &lt;b&gt;lost&lt;/b&gt;</field>
</record>
<record id="mt_lead_won" model="mail.message.subtype">
<field name="name">Opportunity Won</field>
<field name="res_model">crm.lead</field>
<field name="description">Opportunity &lt;b&gt;won&lt;/b&gt;</field>
</record>
<record id="mt_lead_stage" model="mail.message.subtype">
<field name="name">Stage Changed</field> <field name="name">Stage Changed</field>
<field name="res_model">crm.lead</field> <field name="res_model">crm.lead</field>
<field name="default" eval="False"/> <field name="default" eval="False"/>
<field name="description">Stage &lt;b&gt;changed&lt;/&gt;</field> <field name="description">Stage &lt;b&gt;changed&lt;/b&gt;</field>
</record>
<record id="mt_lead_convert_to_opportunity" model="mail.message.subtype">
<field name="name">Lead to Opportunity</field>
<field name="res_model">crm.lead</field>
<field name="default" eval="False"/>
</record>
<!-- Salesteam-related subtypes for messaging / Chatter -->
<record id="mt_salesteam_lead" model="mail.message.subtype">
<field name="name">Lead Created</field>
<field name="res_model">crm.case.section</field>
<field name="default" eval="False"/>
</record>
<record id="mt_salesteam_lead_opportunity" model="mail.message.subtype">
<field name="name">Lead to Opportunity</field>
<field name="res_model">crm.case.section</field>
<field name="default" eval="False"/>
</record>
<record id="mt_salesteam_stage" model="mail.message.subtype">
<field name="name">Lead Stage Changed</field>
<field name="res_model">crm.case.section</field>
<field name="default" eval="False"/>
</record>
<record id="mt_salesteam_lost" model="mail.message.subtype">
<field name="name">Opportunity Lost</field>
<field name="res_model">crm.case.section</field>
<field name="default" eval="False"/>
</record>
<record id="mt_salesteam_won" model="mail.message.subtype">
<field name="name">Opportunity Won</field>
<field name="res_model">crm.case.section</field>
>>>>>>> MERGE-SOURCE
</record> </record>
</data> </data>

View File

@ -128,7 +128,7 @@
</page> </page>
</notebook> </notebook>
<div class="oe_chatter"> <div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers" help="Followers of this salesteam follow automatically all opportunities related to this salesteam."/> <field name="message_follower_ids" widget="mail_followers" help="Follow this salesteam to automatically track the events associated to users of this team."/>
<field name="message_ids" widget="mail_thread"/> <field name="message_ids" widget="mail_thread"/>
</div> </div>
</form> </form>

View File

@ -12,9 +12,9 @@
<field name="state">open</field> <field name="state">open</field>
</record> </record>
<!-- mail: event subtype --> <!-- Event-related subtypes for messaging / Chatter -->
<record id="event.mt_event_registration" model="mail.message.subtype"> <record id="event.mt_event_registration" model="mail.message.subtype">
<field name="name">New Registrations</field> <field name="name">New Registration</field>
<field name="res_model">event.event</field> <field name="res_model">event.event</field>
<field name="default" eval="False"/> <field name="default" eval="False"/>
</record> </record>

View File

@ -128,6 +128,8 @@ class hr_expense_expense(osv.osv):
for expense in self.browse(cr, uid, ids): for expense in self.browse(cr, uid, ids):
if expense.employee_id and expense.employee_id.parent_id.user_id: 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_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 <b>waiting for Approval</b>"),
subtype="hr_expense.mt_expense_approve")
self.write(cr, uid, ids, { self.write(cr, uid, ids, {
'state':'confirm', 'state':'confirm',
'date_confirm': time.strftime('%Y-%m-%d') 'date_confirm': time.strftime('%Y-%m-%d')
@ -135,6 +137,8 @@ class hr_expense_expense(osv.osv):
return True return True
def expense_accept(self, cr, uid, ids, *args): def expense_accept(self, cr, uid, ids, *args):
self.message_post(cr, uid, ids, body=_("The request has been <b>approved</b>"),
subtype="hr_expense.mt_expense_approved")
self.write(cr, uid, ids, { self.write(cr, uid, ids, {
'state':'accepted', 'state':'accepted',
'date_valid':time.strftime('%Y-%m-%d'), 'date_valid':time.strftime('%Y-%m-%d'),
@ -143,6 +147,8 @@ class hr_expense_expense(osv.osv):
return True return True
def expense_canceled(self, cr, uid, ids, *args): def expense_canceled(self, cr, uid, ids, *args):
self.message_post(cr, uid, ids, body=_("Request <b>refused</b>"),
subtype="hr_expense.mt_expense_refused")
self.write(cr, uid, ids, {'state':'cancelled'}) self.write(cr, uid, ids, {'state':'cancelled'})
return True return True

View File

@ -17,5 +17,25 @@
<field name="parent_id" ref="product.product_category_all"/> <field name="parent_id" ref="product.product_category_all"/>
<field name="name">Expenses</field> <field name="name">Expenses</field>
</record> </record>
<!--subtype for expense -->
<record id="mt_expense_approve" model="mail.message.subtype">
<field name="name">To Approve</field>
<field name="res_model">hr.expense.expense</field>
<field name="default" eval="False"/>
</record>
<!-- Expense-related subtypes for messaging / Chatter -->
<record id="mt_expense_approved" model="mail.message.subtype">
<field name="name">Approved</field>
<field name="res_model">hr.expense.expense</field>
<field name="default" eval="False"/>
</record>
<record id="mt_expense_refused" model="mail.message.subtype">
<field name="name">Refused</field>
<field name="res_model">hr.expense.expense</field>
<field name="default" eval="False"/>
</record>
</data> </data>
</openerp> </openerp>

View File

@ -431,7 +431,7 @@ class hr_holidays(osv.osv):
def holidays_confirm_notificate(self, cr, uid, ids, context=None): def holidays_confirm_notificate(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids): for obj in self.browse(cr, uid, ids):
self.message_post(cr, uid, [obj.id], self.message_post(cr, uid, [obj.id],
_("Request <b>submitted</b>, waiting for validation by the manager."), context=context) _("Request <b>submitted</b>, waiting for validation by the manager."), subtype="hr_holidays.mt_approve", context=context)
def holidays_first_validate_notificate(self, cr, uid, ids, context=None): def holidays_first_validate_notificate(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context): for obj in self.browse(cr, uid, ids, context=context):
@ -445,12 +445,12 @@ class hr_holidays(osv.osv):
_("Request <b>validated</b>."), context=context) _("Request <b>validated</b>."), context=context)
else: else:
self.message_post(cr, uid, [obj.id], self.message_post(cr, uid, [obj.id],
_("The request has been <b>approved</b>."), context=context) _("The request has been <b>approved</b>."), subtype="hr_holidays.mt_approved", context=context)
def holidays_refuse_notificate(self, cr, uid, ids, context=None): def holidays_refuse_notificate(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids): for obj in self.browse(cr, uid, ids):
self.message_post(cr, uid, [obj.id], self.message_post(cr, uid, [obj.id],
_("Request <b>refused</b>"), context=context) _("Request <b>refused</b>"), subtype="hr_holidays.mt_refused", context=context)
class resource_calendar_leaves(osv.osv): class resource_calendar_leaves(osv.osv):

View File

@ -44,5 +44,23 @@
<field name="limit">True</field> <field name="limit">True</field>
<field name="color_name">brown</field> <field name="color_name">brown</field>
</record> </record>
<!-- Holidays-related subtypes for messaging / Chatter -->
<record id="mt_approve" model="mail.message.subtype">
<field name="name">To Approve</field>
<field name="res_model">hr.holidays</field>
<field name="default" eval="False"/>
</record>
<record id="mt_approved" model="mail.message.subtype">
<field name="name">Approved</field>
<field name="res_model">hr.holidays</field>
<field name="default" eval="False"/>
</record>
<record id="mt_refused" model="mail.message.subtype">
<field name="name">Refused</field>
<field name="res_model">hr.holidays</field>
<field name="default" eval="False"/>
</record>
</data> </data>
</openerp> </openerp>

View File

@ -469,12 +469,6 @@ class hr_applicant(base_stage, osv.Model):
# OpenChatter methods and notifications # 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 <b>%s</b>.") % (stage_name), context=context)
def case_get_note_msg_prefix(self, cr, uid, id, context=None): def case_get_note_msg_prefix(self, cr, uid, id, context=None):
return 'Applicant' return 'Applicant'
@ -487,18 +481,18 @@ class hr_applicant(base_stage, osv.Model):
context = {} context = {}
for applicant in self.browse(cr, uid, ids, context=context): for applicant in self.browse(cr, uid, ids, context=context):
if applicant.job_id: 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: if applicant.emp_id:
message = _("Applicant has been <b>hired</b> and created as an employee.") message = _("Applicant has been <b>hired</b> 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: else:
message = _("Applicant has been <b>hired</b>.") message = _("Applicant has been <b>hired</b>.")
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 return True
def case_cancel_send_note(self, cr, uid, ids, context=None): def case_cancel_send_note(self, cr, uid, ids, context=None):
msg = 'Applicant <b>refused</b>.' msg = 'Applicant <b>refused</b>.'
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): def case_reset_send_note(self, cr, uid, ids, context=None):
message =_("Applicant has been set as <b>new</b>.") message =_("Applicant has been set as <b>new</b>.")

View File

@ -461,14 +461,42 @@
<field name="alias_user_id" ref="base.user_root"/> <field name="alias_user_id" ref="base.user_root"/>
</record> </record>
<!-- mail: subtypes --> <!-- Job-related subtypes for messaging / Chatter -->
<record id="mt_hired" model="mail.message.subtype">
<field name="name">Employee Hired</field>
<field name="res_model">hr.job</field>
</record>
<record id="mt_applicant_new" model="mail.message.subtype"> <record id="mt_applicant_new" model="mail.message.subtype">
<field name="name">New Applicant</field> <field name="name">New Applicant</field>
<field name="res_model">hr.job</field> <field name="res_model">hr.job</field>
</record> </record>
<record id="mt_job_stage_changed" model="mail.message.subtype">
<field name="name">Stage Changed</field>
<field name="res_model">hr.job</field>
<field name="default" eval="False"/>
</record>
<record id="mt_job_applicant_hired" model="mail.message.subtype">
<field name="name">Applicant Hired</field>
<field name="res_model">hr.job</field>
<field name="default" eval="False"/>
</record>
<record id="mt_job_applicant_refused" model="mail.message.subtype">
<field name="name">Refused</field>
<field name="res_model">hr.job</field>
<field name="default" eval="False"/>
</record>
<!-- Applicant-related subtypes for messaging / Chatter -->
<record id="mt_stage_changed" model="mail.message.subtype">
<field name="name">Stage Changed</field>
<field name="res_model">hr.applicant</field>
<field name="default" eval="False"/>
</record>
<record id="mt_applicant_hired" model="mail.message.subtype">
<field name="name">Applicant Hired</field>
<field name="res_model">hr.applicant</field>
<field name="default" eval="False"/>
</record>
<record id="mt_applicant_refused" model="mail.message.subtype">
<field name="name">Applicant Refused</field>
<field name="res_model">hr.applicant</field>
<field name="default" eval="False"/>
</record>
</data> </data>
</openerp> </openerp>

View File

@ -95,19 +95,19 @@ class account_analytic_account(osv.osv):
def set_cancel(self, cr, uid, ids, context=None): def set_cancel(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state': 'cancelled'}, context=context) self.write(cr, uid, ids, {'state': 'cancelled'}, context=context)
message = _("Contract has been <b>canceled</b>.") message = _("Contract has been <b>canceled</b>.")
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 return True
def set_open(self, cr, uid, ids, context=None): def set_open(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state': 'open'}, context=context) self.write(cr, uid, ids, {'state': 'open'}, context=context)
message = _("Contract has been <b>opened</b>.") message = _("Contract has been <b>opened</b>.")
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 return True
def set_pending(self, cr, uid, ids, context=None): def set_pending(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state': 'pending'}, context=context) self.write(cr, uid, ids, {'state': 'pending'}, context=context)
message = _("Contract has been set as <b>pending</b>.") message = _("Contract has been set as <b>pending</b>.")
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 return True
account_analytic_account() account_analytic_account()

View File

@ -22,15 +22,35 @@
<field name="factor">20.0</field> <field name="factor">20.0</field>
</record> </record>
<!-- mail: subtypes --> <!-- Analytic-account-related subtypes for messaging / Chatter -->
<record id="mt_account_closed" model="mail.message.subtype"> <record id="mt_account_renew" model="mail.message.subtype">
<field name="name">finished</field> <field name="name">Contract to Renew</field>
<field name="res_model">account.analytic.account</field> <field name="res_model">account.analytic.account</field>
</record> </record>
<record id="mt_account_canceled" model="mail.message.subtype"> <record id="mt_account_closed" model="mail.message.subtype">
<field name="name">canceled</field> <field name="name">Contract Finished</field>
<field name="res_model">account.analytic.account</field> <field name="res_model">account.analytic.account</field>
</record> </record>
<record id="mt_account_renewed" model="mail.message.subtype">
<field name="name">Contract Renewed</field>
<field name="res_model">account.analytic.account</field>
</record>
<!-- Salesteam-related subtypes for messaging / Chatter -->
<record id="mt_salesteam_renew" model="mail.message.subtype">
<field name="name">Contract to Renew</field>
<field name="res_model">crm.case.section</field>
<field name="default" eval="False"/>
</record>
<record id="mt_salesteam_finished" model="mail.message.subtype">
<field name="name">Contract Finished</field>
<field name="res_model">crm.case.section</field>
<field name="default" eval="False"/>
</record>
<record id="mt_salesteam_renewed" model="mail.message.subtype">
<field name="name">Contract Renewed</field>
<field name="res_model">crm.case.section</field>
<field name="default" eval="False"/>
</record>
</data> </data>
</openerp> </openerp>

View File

@ -27,6 +27,7 @@
<field name="priority">1000</field> <field name="priority">1000</field>
</record> </record>
<!-- Discussion subtype for messaging / Chatter -->
<record id="mt_comment" model="mail.message.subtype"> <record id="mt_comment" model="mail.message.subtype">
<field name="name">Discussions</field> <field name="name">Discussions</field>
</record> </record>

View File

@ -952,6 +952,28 @@ class mail_thread(osv.AbstractModel):
self.check_access_rights(cr, uid, 'read') 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) 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 # Thread state
#------------------------------------------------------ #------------------------------------------------------

View File

@ -184,7 +184,6 @@ openerp_mail_followers = function(session, mail) {
/** Fetch subtypes, only if current user is follower */ /** Fetch subtypes, only if current user is follower */
fetch_subtypes: function () { fetch_subtypes: function () {
var subtype_list_ul = this.$('.oe_subtype_list').empty();
if (! this.message_is_follower) return; if (! this.message_is_follower) return;
var context = new session.web.CompoundContext(this.build_context(), {}); 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')); 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: {'name': default, followed} */
display_subtypes:function (data) { display_subtypes:function (data) {
var self = this; 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(); subtype_list_ul.empty();
var records = data[this.view.datarecord.id || this.view.dataset.ids[0]].message_subtype_data; var records = data[this.view.datarecord.id || this.view.dataset.ids[0]].message_subtype_data;
_(records).each(function (record, record_name) { _(records).each(function (record, record_name) {
record.name = record_name; record.name = record_name;
record.followed = record.followed || undefined; 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 () { do_follow: function () {
@ -228,10 +235,12 @@ openerp_mail_followers = function(session, mail) {
checklist.push(parseInt($(record).data('id'))); checklist.push(parseInt($(record).data('id')));
} }
}); });
var context = new session.web.CompoundContext(this.build_context(), {}); 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]) 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();}
});
}, },
}); });
}; };

View File

@ -12,11 +12,12 @@
<span class="oe_unfollow">Unfollow</span> <span class="oe_unfollow">Unfollow</span>
<span class="oe_following">Following</span> <span class="oe_following">Following</span>
</button> </button>
<t t-if="widget.comment">
<h5 class="oe_comment"><t t-raw="widget.comment"/></h5>
</t>
<div class="oe_subtype_list"></div> <div class="oe_subtype_list"></div>
</div> </div>
<t t-if="widget.comment"> <hr size="2"></hr>
<h5 class="oe_comment"><t t-raw="widget.comment"/></h5>
</t>
<div class='oe_follower_title_box'> <div class='oe_follower_title_box'>
<h4 class='oe_follower_title'>Followers</h4> <h4 class='oe_follower_title'>Followers</h4>
<a href='#' class="oe_invite">Invite others</a> <a href='#' class="oe_invite">Invite others</a>

View File

@ -317,9 +317,7 @@ class project(osv.osv):
task_obj = self.pool.get('project.task') task_obj = self.pool.get('project.task')
task_ids = task_obj.search(cr, uid, [('project_id', 'in', ids), ('state', '!=', 'done')]) task_ids = task_obj.search(cr, uid, [('project_id', 'in', ids), ('state', '!=', 'done')])
task_obj.case_cancel(cr, uid, task_ids, context=context) task_obj.case_cancel(cr, uid, task_ids, context=context)
self.write(cr, uid, ids, {'state':'cancelled'}, context=context) return self.write(cr, uid, ids, {'state':'cancelled'}, context=context)
self.set_cancel_send_note(cr, uid, ids, context=context)
return True
def set_pending(self, cr, uid, ids, context=None): def set_pending(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state':'pending'}, context=context) 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): def set_pending_send_note(self, cr, uid, ids, context=None):
return self.message_post(cr, uid, ids, body=_("Project is now <b>pending</b>."), context=context) return self.message_post(cr, uid, ids, body=_("Project is now <b>pending</b>."), context=context)
def set_cancel_send_note(self, cr, uid, ids, context=None):
return self.message_post(cr, uid, ids, body=_("Project has been <b>canceled</b>."), context=context)
def set_close_send_note(self, cr, uid, ids, context=None): def set_close_send_note(self, cr, uid, ids, context=None):
return self.message_post(cr, uid, ids, body=_("Project has been <b>closed</b>."), context=context) return self.message_post(cr, uid, ids, body=_("Project has been <b>closed</b>."), context=context)
@ -993,7 +988,6 @@ class task(base_stage, osv.osv):
self._check_child_task(cr, uid, ids, context=context) self._check_child_task(cr, uid, ids, context=context)
for task in tasks: for task in tasks:
self.case_set(cr, uid, [task.id], 'cancelled', {'remaining_hours': 0.0}, context=context) 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 return True
def do_open(self, cr, uid, ids, context=None): 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): def set_kanban_state_blocked(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'kanban_state': 'blocked'}, context=context) 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): def set_kanban_state_normal(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'kanban_state': 'normal'}, context=context) 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): def set_kanban_state_done(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'kanban_state': 'done'}, context=context) self.write(cr, uid, ids, {'kanban_state': 'done'}, context=context)
@ -1109,35 +1105,12 @@ class task(base_stage, osv.osv):
}, context=context) }, context=context)
return True 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): def create(self, cr, uid, vals, context=None):
task_id = super(task, self).create(cr, uid, vals, context=context) task_id = super(task, self).create(cr, uid, vals, context=context)
# subscribe project followers to the task project_id = self.browse(cr, uid, task_id, context=context).project_id
self._subscribe_project_followers_to_task(cr, uid, task_id, context=context) 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) self._store_history(cr, uid, [task_id], context=context)
return task_id return task_id
@ -1147,6 +1120,11 @@ class task(base_stage, osv.osv):
def write(self, cr, uid, ids, vals, context=None): def write(self, cr, uid, ids, vals, context=None):
if isinstance(ids, (int, long)): if isinstance(ids, (int, long)):
ids = [ids] 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: if vals and not 'kanban_state' in vals and 'stage_id' in vals:
new_stage = vals.get('stage_id') new_stage = vals.get('stage_id')
vals_reset_kstate = dict(vals, kanban_state='normal') 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 # subscribe new project followers to the task
if vals.get('project_id'): if vals.get('project_id'):
for id in ids: self._subscribe_followers_subtype(cr, uid, ids, vals.get('project_id'), 'project.project', context=context)
self._subscribe_project_followers_to_task(cr, uid, id, context=context)
return result return result
def unlink(self, cr, uid, ids, context=None): 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) res = super(task, self).message_get_monitored_follower_fields(cr, uid, ids, context=context)
return res + ['user_id', 'manager_id'] 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 <b>created</b>."), subtype="project.mt_task_new", context=context)
def stage_set_send_note(self, cr, uid, ids, stage_id, context=None): def stage_set_send_note(self, cr, uid, ids, stage_id, context=None):
""" Override of the (void) default notification method. """ """ 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] 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 <b>%s</b>.") % (stage_name), return self.message_post(cr, uid, ids, body=_("Stage changed to <b>%s</b>.") % (stage_name),
context=context) context=context)
def create_send_note(self, cr, uid, ids, context=None): def case_open_send_note(self, cr, uid, ids, context=None):
return self.message_post(cr, uid, ids, body=_("Task has been <b>created</b>."), context=context) return self.message_post(cr, uid, ids, body=_("Task <b>started</b>."), 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 <b>closed</b>."), 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 <b>blocked</b>."), subtype="project.mt_task_blocked", context=context)
def case_draft_send_note(self, cr, uid, ids, context=None): def case_draft_send_note(self, cr, uid, ids, context=None):
return self.message_post(cr, uid, ids, body=_('Task has been set as <b>draft</b>.'), context=context) return self.message_post(cr, uid, ids, body=_('Task set as <b>draft</b>.'), context=context)
def do_delegation_send_note(self, cr, uid, ids, context=None): def do_delegation_send_note(self, cr, uid, ids, context=None):
for task in self.browse(cr, uid, ids, context=context): for task in self.browse(cr, uid, ids, context=context):
msg = _('Task has been <b>delegated</b> to <em>%s</em>.') % (task.user_id.name) msg = _('Task has been <b>delegated</b> to <em>%s</em>.') % (task.user_id.name)
self.message_post(cr, uid, [task.id], body=msg, context=context) self.message_post(cr, uid, [task.id], body=msg, context=context)
return True return True
def project_task_reevaluate(self, cr, uid, ids, context=None): 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'): if self.pool.get('res.users').has_group(cr, uid, 'project.group_time_work_estimation_tasks'):
return { return {

View File

@ -78,39 +78,42 @@
<field name="fold" eval="True"/> <field name="fold" eval="True"/>
</record> </record>
<!-- mail: subtypes --> <!-- Project-related subtypes for messaging / Chatter -->
<record id="mt_project_new" model="mail.message.subtype"> <record id="mt_project_task_new" model="mail.message.subtype">
<field name="name">New</field> <field name="name">Task Created</field>
<field name="res_model">project.project</field> <field name="res_model">project.project</field>
<field name="default" eval="False"/> <field name="default" eval="False"/>
</record> </record>
<record id="mt_project_closed" model="mail.message.subtype"> <record id="mt_project_task_started" model="mail.message.subtype">
<field name="name">Closed</field> <field name="name">Task Started</field>
<field name="res_model">project.project</field>
<field name="default" eval="False"/>
</record>
<record id="mt_project_task_blocked" model="mail.message.subtype">
<field name="name">Task Blocked</field>
<field name="res_model">project.project</field> <field name="res_model">project.project</field>
</record> </record>
<record id="mt_project_canceled" model="mail.message.subtype"> <record id="mt_project_task_closed" model="mail.message.subtype">
<field name="name">Canceled</field> <field name="name">Task Done</field>
<field name="res_model">project.project</field> <field name="res_model">project.project</field>
</record> </record>
<record id="mt_project_stage" model="mail.message.subtype"> <!-- Task-related subtypes for messaging / Chatter -->
<field name="name">Stage Changed</field>
<field name="res_model">project.project</field>
</record>
<record id="mt_task_new" model="mail.message.subtype"> <record id="mt_task_new" model="mail.message.subtype">
<field name="name">New</field> <field name="name">Task Created</field>
<field name="res_model">project.task</field>
<field name="default" eval="False"/>
</record>
<record id="mt_task_started" model="mail.message.subtype">
<field name="name">Task Started</field>
<field name="res_model">project.task</field>
<field name="default" eval="False"/>
</record>
<record id="mt_task_blocked" model="mail.message.subtype">
<field name="name">Task Blocked</field>
<field name="res_model">project.task</field> <field name="res_model">project.task</field>
</record> </record>
<record id="mt_task_closed" model="mail.message.subtype"> <record id="mt_task_closed" model="mail.message.subtype">
<field name="name">Closed</field> <field name="name">Task Done</field>
<field name="res_model">project.task</field>
</record>
<record id="mt_task_canceled" model="mail.message.subtype">
<field name="name">Canceled</field>
<field name="res_model">project.task</field>
</record>
<record id="mt_task_change" model="mail.message.subtype">
<field name="name">Stage Changed</field>
<field name="res_model">project.task</field> <field name="res_model">project.task</field>
</record> </record>

View File

@ -115,7 +115,7 @@
</notebook> </notebook>
</sheet> </sheet>
<div class="oe_chatter"> <div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers" help="Follow this project to automatically follow all related tasks and issues." groups="base.group_user"/> <field name="message_follower_ids" widget="mail_followers" help="Follow this project to automatically track the events associated to tasks and issues of this project." groups="base.group_user"/>
<field name="message_ids" widget="mail_thread"/> <field name="message_ids" widget="mail_thread"/>
</div> </div>
</form> </form>

View File

@ -233,6 +233,12 @@ class project_issue(base_stage, osv.osv):
When the case is over, the status is set to \'Done\'.\ When the case is over, the status is set to \'Done\'.\
If the case needs to be reviewed then the status is \ If the case needs to be reviewed then the status is \
set to \'Pending\'.'), 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_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"), '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), '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), '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), '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], 'priority': crm.AVAILABLE_PRIORITIES[2][0],
'kanban_state': 'normal',
} }
_group_by_full = { _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, return super(project_issue, self).copy(cr, uid, id, default=default,
context=context) 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): 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 #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'] 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 # subscribe new project followers to the issue
if vals.get('project_id'): if vals.get('project_id'):
for id in ids: project_id = self.pool.get('project.project').browse(cr, uid, vals.get('project_id'), context=context)
self._subscribe_project_followers_to_issue(cr, uid, id, context=context) vals.setdefault('message_follower_ids', [])
vals['message_follower_ids'] += [(6, 0,[follower.id]) for follower in project_id.message_follower_ids]
return super(project_issue, self).write(cr, uid, ids, vals, context) 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): def onchange_task_id(self, cr, uid, ids, task_id, context=None):
if not task_id: if not task_id:
return {'value': {}} return {'value': {}}
@ -413,8 +398,10 @@ class project_issue(base_stage, osv.osv):
def create(self, cr, uid, vals, context=None): def create(self, cr, uid, vals, context=None):
obj_id = super(project_issue, self).create(cr, uid, vals, context=context) obj_id = super(project_issue, self).create(cr, uid, vals, context=context)
# subscribe project follower to the issue project_id = self.browse(cr, uid, obj_id, context=context).project_id
self._subscribe_project_followers_to_issue(cr, uid, obj_id, context=context) 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) self.create_send_note(cr, uid, [obj_id], context=context)
return obj_id return obj_id
@ -455,7 +442,6 @@ class project_issue(base_stage, osv.osv):
def case_cancel(self, cr, uid, ids, context=None): def case_cancel(self, cr, uid, ids, context=None):
""" Cancels case """ """ Cancels case """
self.case_set(cr, uid, ids, 'cancelled', {'active': True}, context=context) self.case_set(cr, uid, ids, 'cancelled', {'active': True}, context=context)
self.case_cancel_send_note(cr, uid, ids, context=context)
return True return True
def case_escalate(self, cr, uid, ids, context=None): 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): def stage_set_send_note(self, cr, uid, ids, stage_id, context=None):
""" Override of the (void) default notification method. """ """ 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] 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 <b>%s</b>.") % (stage_name), subtype="mt_issue_new", context=context) return self.message_post(cr, uid, ids, body=_("Stage changed to <b>%s</b>.") % (stage_name), context=context)
def case_get_note_msg_prefix(self, cr, uid, id, context=None): def case_get_note_msg_prefix(self, cr, uid, id, context=None):
""" Override of default prefix for notifications. """ """ 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): def create_send_note(self, cr, uid, ids, context=None):
message = _("Project issue <b>created</b>.") message = _("Project issue <b>created</b>.")
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 <b>started</b>."), subtype="project_issue.mt_issue_started", context=context)
def case_escalate_send_note(self, cr, uid, ids, context=None): def case_escalate_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context): 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) obj.message_post(body=message)
return True return True
def case_block_send_note(self, cr, uid, ids, context=None):
return self.message_post(cr, uid, ids, body=_("Issue <b>blocked</b>."), 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 <b>closed</b>."), 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() project_issue()
class project(osv.osv): class project(osv.osv):

View File

@ -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.</p>]]></field> Access all issues from the top Project menu, and access the issues of a specific project via the projects gallery view.</p>]]></field>
</record> </record>
<!-- Mail subtypes --> <!-- Issue-related subtypes for messaging / Chatter -->
<record id="mail.mt_issue_new" model="mail.message.subtype"> <record id="mt_issue_new" model="mail.message.subtype">
<field name="name">New</field> <field name="name">Issue Created</field>
<field name="res_model">project.issue</field>
<field name="default" eval="False"/>
</record>
<record id="mt_issue_started" model="mail.message.subtype">
<field name="name">Issue Started</field>
<field name="res_model">project.issue</field>
<field name="default" eval="False"/>
</record>
<record id="mt_issue_blocked" model="mail.message.subtype">
<field name="name">Issue Blocked</field>
<field name="res_model">project.issue</field>
<field name="default" eval="False"/>
</record>
<record id="mt_issue_closed" model="mail.message.subtype">
<field name="name">Issue Closed</field>
<field name="res_model">project.issue</field> <field name="res_model">project.issue</field>
</record> </record>
<record id="mail.mt_issue_closed" model="mail.message.subtype"> <!-- Project-related subtypes for messaging / Chatter -->
<field name="name">Closed</field> <record id="mt_project_issue_new" model="mail.message.subtype">
<field name="res_model">project.issue</field> <field name="name">Issue Created</field>
<field name="res_model">project.project</field>
<field name="default" eval="False"/>
</record> </record>
<record id="mail.mt_issue_canceled" model="mail.message.subtype"> <record id="mt_project_issue_started" model="mail.message.subtype">
<field name="name">Canceled</field> <field name="name">Issue Started</field>
<field name="res_model">project.issue</field> <field name="res_model">project.project</field>
<field name="default" eval="False"/>
</record> </record>
<record id="mail.mt_issue_change" model="mail.message.subtype"> <record id="mt_project_issue_blocked" model="mail.message.subtype">
<field name="name">Stage Changed</field> <field name="name">Issue Blocked</field>
<field name="res_model">project.issue</field> <field name="res_model">project.project</field>
<field name="default" eval="False"/>
</record> </record>
<record id="mt_project_issue_closed" model="mail.message.subtype">
<field name="name">Issue Closed</field>
<field name="res_model">project.project</field>
</record>
</data> </data>
</openerp> </openerp>

View File

@ -203,6 +203,7 @@
<field name="user_email"/> <field name="user_email"/>
<field name="user_id"/> <field name="user_id"/>
<field name="date_deadline"/> <field name="date_deadline"/>
<field name="kanban_state"/>
<templates> <templates>
<t t-name="kanban-tooltip"> <t t-name="kanban-tooltip">
<ul class="oe_kanban_tooltip"> <ul class="oe_kanban_tooltip">
@ -230,6 +231,9 @@
<field name="categ_ids"/> <field name="categ_ids"/>
<div class="oe_right"> <div class="oe_right">
<span class="oe_kanban_highlight" groups="base.group_user"> <span class="oe_kanban_highlight" groups="base.group_user">
<a t-if="record.kanban_state.raw_value === 'normal'" type="object" string="In Progress" name="set_kanban_state_done" class="oe_kanban_status"> </a>
<a t-if="record.kanban_state.raw_value === 'done'" type="object" string="Ready for next stage" name="set_kanban_state_blocked" class="oe_kanban_status oe_kanban_status_green"> </a>
<a t-if="record.kanban_state.raw_value === 'blocked'" type="object" string="Blocked" name="set_kanban_state_normal" class="oe_kanban_status oe_kanban_status_red"> </a>
<t t-set="priority" t-value="record.priority.raw_value || 5"/> <t t-set="priority" t-value="record.priority.raw_value || 5"/>
<a type="object" name="set_priority" args="['3']" t-if="priority gt 3" title="Normal Priority"> <a type="object" name="set_priority" args="['3']" t-if="priority gt 3" title="Normal Priority">
<img src="/web/static/src/img/icons/star-off.png" width="16" height="16"/> <img src="/web/static/src/img/icons/star-off.png" width="16" height="16"/>

View File

@ -822,7 +822,7 @@ class purchase_order(osv.osv):
def confirm_send_note(self, cr, uid, ids, context=None): def confirm_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context): for obj in self.browse(cr, uid, ids, context=context):
self.message_post(cr, uid, [obj.id], body=_("Quotation for <em>%s</em> <b>converted</b> to a Purchase Order of %s %s.") % (obj.partner_id.name, obj.amount_total, obj.pricelist_id.currency_id.symbol), context=context) self.message_post(cr, uid, [obj.id], body=_("Quotation for <em>%s</em> <b>converted</b> to a Purchase Order of %s %s.") % (obj.partner_id.name, obj.amount_total, obj.pricelist_id.currency_id.symbol), subtype="purchase.mt_rfq_confirmed", context=context)
def shipment_send_note(self, cr, uid, ids, picking_id, context=None): def shipment_send_note(self, cr, uid, ids, picking_id, context=None):
for order in self.browse(cr, uid, ids, context=context): for order in self.browse(cr, uid, ids, context=context):

View File

@ -48,6 +48,18 @@
id="purchase_default_set" id="purchase_default_set"
model="ir.values" model="ir.values"
name="set"/> name="set"/>
<!-- Purchase-related subtypes for messaging / Chatter -->
<record id="mt_rfq_confirmed" model="mail.message.subtype">
<field name="name">RFQ Confirmed</field>
<field name="res_model">purchase.order</field>
<field name="default" eval="False"/>
</record>
<record id="mt_rfq_approved" model="mail.message.subtype">
<field name="name">RFQ Approved</field>
<field name="res_model">purchase.order</field>
<field name="default" eval="False"/>
</record>
</data> </data>
</openerp> </openerp>

View File

@ -650,7 +650,7 @@ class sale_order(osv.osv):
def confirm_send_note(self, cr, uid, ids, context=None): def confirm_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context): for obj in self.browse(cr, uid, ids, context=context):
self.message_post(cr, uid, [obj.id], body=_("Quotation for <em>%s</em> <b>converted</b> to Sale Order of %s %s.") % (obj.partner_id.name, obj.amount_total, obj.pricelist_id.currency_id.symbol), context=context) self.message_post(cr, uid, [obj.id], body=_("Quotation for <em>%s</em> <b>converted</b> to Sale Order of %s %s.") % (obj.partner_id.name, obj.amount_total, obj.pricelist_id.currency_id.symbol), subtype="sale.mt_order_confirmed", context=context)
def cancel_send_note(self, cr, uid, ids, context=None): def cancel_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context): for obj in self.browse(cr, uid, ids, context=context):

View File

@ -43,5 +43,28 @@
<field name="body"><![CDATA[<p>This application lets you create and send quotations and process your sales orders; from delivery to invoicing.</p> <field name="body"><![CDATA[<p>This application lets you create and send quotations and process your sales orders; from delivery to invoicing.</p>
<p>If you need to manage your sales pipeline (leads, opportunities, phonecalls), the <i>CRM</i> application may be useful. Use the Settings menu to install it.</p>]]></field> <p>If you need to manage your sales pipeline (leads, opportunities, phonecalls), the <i>CRM</i> application may be useful. Use the Settings menu to install it.</p>]]></field>
</record> </record>
<!-- Sale-related subtypes for messaging / Chatter -->
<record id="mt_quotation_sent" model="mail.message.subtype">
<field name="name">Quotation sent</field>
<field name="res_model">sale.order</field>
</record>
<record id="mt_order_confirmed" model="mail.message.subtype">
<field name="name">Sale Order Confirmed</field>
<field name="res_model">sale.order</field>
</record>
<!-- Salesteam-related subtypes for messaging / Chatter -->
<record id="mt_salesteam_sent" model="mail.message.subtype">
<field name="name">Quotation sent</field>
<field name="res_model">crm.case.section</field>
<field name="default" eval="False"/>
</record>
<record id="mt_salesteam_confirmed" model="mail.message.subtype">
<field name="name">Sale Order Confirmed</field>
<field name="res_model">crm.case.section</field>
<field name="default" eval="False"/>
</record>
</data> </data>
</openerp> </openerp>

View File

@ -33,16 +33,19 @@ class sale_order(osv.osv):
order = super(sale_order, self).create(cr, uid, vals, context=context) order = super(sale_order, self).create(cr, uid, vals, context=context)
section_id = self.browse(cr, uid, order, context=context).section_id section_id = self.browse(cr, uid, order, context=context).section_id
if section_id: if section_id:
followers = [follow.id for follow in section_id.message_follower_ids] # subscribe salesteam followers & subtypes to the sale order
self.message_subscribe(cr, uid, [order], followers, context=context) self._subscribe_followers_subtype(cr, uid, [order], section_id, 'crm.case.section', context=context)
return order return order
def write(self, cr, uid, ids, vals, context=None): def write(self, cr, uid, ids, vals, context=None):
if vals.get('section_id'): if vals.get('section_id'):
section_id = self.pool.get('crm.case.section').browse(cr, uid, vals.get('section_id'), context=context) section_id = self.pool.get('crm.case.section').browse(cr, uid, vals.get('section_id'), context=context)
if section_id: vals['message_follower_ids'] = [(6, 0, [follower.id]) for follower in section_id.message_follower_ids]
vals['message_follower_ids'] = [(4, follower.id) for follower in section_id.message_follower_ids] res = super(sale_order, self).write(cr, uid, ids, vals, context=context)
return super(sale_order, self).write(cr, uid, ids, vals, context=context) # subscribe new salesteam followers & subtypes to the sale order
if vals.get('section_id'):
self._subscribe_followers_subtype(cr, uid, ids, vals.get('section_id'), 'crm.case.section', context=context)
return res
sale_order() sale_order()