[MERGE] crm improvement

bzr revid: tfr@openerp.com-20110407092730-bjm0pwq9mewpvzc0
This commit is contained in:
Thibault Francois 2011-04-07 11:27:30 +02:00
commit 1a45ff614d
22 changed files with 638 additions and 159 deletions

View File

@ -98,6 +98,27 @@ class crm_lead(crm_case, osv.osv):
res[lead.id][field] = abs(int(duration))
return res
def _history_search(self, cr, uid, obj, name, args, context=None):
res = []
msg_obj = self.pool.get('mailgate.message')
message_ids = msg_obj.search(cr, uid, [('history','=',True), ('name', args[0][1], args[0][2])], context=context)
lead_ids = self.search(cr, uid, [('message_ids', 'in', message_ids)], context=context)
if lead_ids:
return [('id', 'in', lead_ids)]
else:
return [('id', '=', '0')]
def _get_email_subject(self, cr, uid, ids, fields, args, context=None):
res = {}
for obj in self.browse(cr, uid, ids, context=context):
res[obj.id] = ''
for msg in obj.message_ids:
if msg.history:
res[obj.id] = msg.name
break
return res
_columns = {
# Overridden from res.partner.address:
'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null',
@ -149,6 +170,7 @@ class crm_lead(crm_case, osv.osv):
\nWhen the case is over, the state is set to \'Done\'.\
\nIf the case needs to be reviewed then the state is set to \'Pending\'.'),
'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
'subjects': fields.function(_get_email_subject, fnct_search=_history_search, string='Subject of Email', method=True, type='char', size=64),
}
@ -289,17 +311,6 @@ class crm_lead(crm_case, osv.osv):
self.log(cr, uid, case.id, message)
return super(crm_lead,self).write(cr, uid, ids, vals, context)
def stage_historize(self, cr, uid, ids, stage, context=None):
stage_obj = self.pool.get('crm.case.stage').browse(cr, uid, stage, context=context)
self.history(cr, uid, ids, _('Stage'), details=stage_obj.name)
for case in self.browse(cr, uid, ids, context=context):
if case.type == 'lead':
message = _("The stage of lead '%s' has been changed to '%s'.") % (case.name, stage_obj.name)
elif case.type == 'opportunity':
message = _("The stage of opportunity '%s' has been changed to '%s'.") % (case.name, stage_obj.name)
self.log(cr, uid, case.id, message)
return True
def stage_next(self, cr, uid, ids, context=None):
stage = super(crm_lead, self).stage_next(cr, uid, ids, context=context)
if stage:

View File

@ -183,7 +183,7 @@
<field colspan="4" name="email_cc" widget="char" size="512"/>
</group>
<field name="message_ids" colspan="4" nolabel="1" mode="tree,form">
<tree string="Communication history">
<tree string="History">
<field name="display_text" string="History Information"/>
<field name="history" invisible="1"/>
<button
@ -192,34 +192,33 @@
context="{'mail':'reply', 'model': 'crm.lead', 'include_original' : True}"
icon="terp-mail-replied" type="action" />
</tree>
<form string="Communication history">
<group col="4" colspan="4">
<field name="email_from"/>
<field name="date"/>
<field name="email_to" widget="char" size="512"/>
<field name="email_cc" widget="char" size="512"/>
<field name="name" colspan="4" widget="char" size="512"/>
<field name="history" invisible="1"/>
</group>
<notebook colspan="4">
<page string="Details">
<group attrs="{'invisible': [('history', '!=', True)]}">
<field name="description" colspan="4" nolabel="1" height="250"/>
<button colspan="4"
string="Reply"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'reply', 'model': 'crm.lead', 'include_original' : True}"
icon="terp-mail-replied" type="action" />
</group>
<group attrs="{'invisible': [('history', '=', True)]}">
<field name="display_text" colspan="4" nolabel="1" height="250"/>
</group>
</page>
<page string="Attachments">
<field name="attachment_ids" colspan="4" readonly="1" nolabel="1"/>
</page>
</notebook>
</form>
<form string="History">
<group col="4" colspan="4">
<field name="email_from"/>
<field name="date"/>
<field name="email_to" size="512"/>
<field name="email_cc" size="512"/>
<field name="name" colspan="4" widget="char" attrs="{'invisible': [('history', '=', False)]}" size="512"/>
<field name="display_text" colspan="4" attrs="{'invisible': [('history', '=', True)]}"/>
<field name="history" invisible="1"/>
</group>
<notebook colspan="4">
<page string="Details">
<field name="description" colspan="4" nolabel="1"/>
<group attrs="{'invisible': [('history', '!=', True)]}">
<button colspan="4"
string="Reply"
name="%(crm.action_crm_send_mail)d"
context="{'mail':'reply', 'model': 'crm.lead', 'include_original' : True}"
icon="terp-mail-replied" type="action" />
</group>
</page>
<page string="Attachments">
<field name="attachment_ids" colspan="4" readonly="1" nolabel="1"/>
</page>
</notebook>
</form>
</field>
<button string="Add Internal Note"
name="%(crm.action_crm_add_note)d"
@ -255,6 +254,7 @@
<field name="type_id" invisible="1"/>
<field name="referred" invisible="1"/>
<field name="channel_id" invisible="1"/>
<field name="subjects" invisible="1"/>
<field name="stage_id"/>
<button name="stage_previous" string="Previous Stage"
@ -335,6 +335,7 @@
domain="[('user_id','=', False)]"
help="Unassigned Leads" />
</field>
<field name="subjects"/>
<field name="section_id" widget="selection"
context="{'invisible_section': False}">
<filter icon="terp-personal+" groups="base.group_extended"

View File

@ -156,8 +156,8 @@
<field name="date"/>
<field name="email_to" size="512"/>
<field name="email_cc" size="512"/>
<field name="name" colspan="4" attrs="{'invisible': [('history', '=', True)]}"/>
<field name="display_text" colspan="4" attrs="{'invisible': [('history', '=', False)]}"/>
<field name="name" colspan="4" widget="char" attrs="{'invisible': [('history', '=', False)]}" size="512"/>
<field name="display_text" colspan="4" attrs="{'invisible': [('history', '=', True)]}"/>
<field name="history" invisible="1"/>
</group>
<notebook colspan="4">
@ -230,6 +230,7 @@
<field name="stage_id"/>
<field name="channel_id" invisible="1"/>
<field name="type_id" invisible="1"/>
<field name="subjects" invisible="1"/>
<button name="stage_previous" string="Previous Stage"
states="open,pending" type="object" icon="gtk-go-back" />
<button name="stage_next" string="Next Stage"
@ -309,6 +310,7 @@
domain="[]"
help="Show Sales Team"/>
</field>
<field name="subjects"/>
<newline/>
<group expand="0" string="Extended Filters..." groups="base.group_extended">
<field name="stage_id" widget="selection" domain="[('type', '=', 'opportunity')]"/>

View File

@ -21,6 +21,8 @@
from osv import osv, fields
from tools.translate import _
import tools
import re
import time
@ -53,14 +55,29 @@ class crm_lead2opportunity_partner(osv.osv_memory):
partner_id = False
for lead in lead_obj.browse(cr, uid, opportunities, context=context):
partner_id = lead.partner_id and lead.partner_id.id or False
email = re.findall(r'([^ ,<@]+@[^> ,]+)', lead.email_from or '')
email = map(lambda x: "'" + x + "'", email)
if not partner_id and res.get('partner_id'):
partner_id = res.get('partner_id')
ids = []
if partner_id:
ids = lead_obj.search(cr, uid, [('partner_id', '=', partner_id), ('type', '=', 'opportunity')])
opportunities += ids
ids = lead_obj.search(cr, uid, [('partner_id', '=', partner_id), ('type', '=', 'opportunity'), '!', ('state', 'in', ['done', 'cancel'])])
if ids:
opportunities.append(ids[0])
if not partner_id:
label = False
opp_ids = []
if email:
# Find email of existing opportunity matches the email_from of the lead
cr.execute("""select id from crm_lead where type='opportunity' and
substring(email_from from '([^ ,<@]+@[^> ,]+)') in (%s)""" % (','.join(email)))
ids = map(lambda x:x[0], cr.fetchall())
if ids:
opportunities.append(ids[0])
if 'action' in fields:
res.update({'action' : partner_id and 'exist' or 'create'})
@ -82,7 +99,6 @@ class crm_lead2opportunity_partner(osv.osv_memory):
@param uid: the current users ID for security checks,
@param fields: List of fields for default value
@param context: A standard dictionary for contextual values
"""
if context is None:
context = {}
@ -90,13 +106,14 @@ class crm_lead2opportunity_partner(osv.osv_memory):
for lead in lead_obj.browse(cr, uid, context.get('active_ids', []), context=context):
if lead.state in ['done', 'cancel']:
raise osv.except_osv(_("Warning !"), _("Closed/Cancelled \
Leads Could not convert into Opportunity"))
raise osv.except_osv(_("Warning !"), _("Closed/Cancelled Leads Could not convert into Opportunity"))
return False
def _convert(self, cr, uid, ids, lead, partner_id, stage_ids, context=None):
leads = self.pool.get('crm.lead')
address_id = self.pool.get('res.partner.address').search(cr, uid,
address_id = False
if partner_id:
address_id = self.pool.get('res.partner.address').search(cr, uid,
[('partner_id', '=', partner_id)],
order='create_date desc',
limit=1)
@ -110,8 +127,10 @@ Leads Could not convert into Opportunity"))
'stage_id': stage_ids and stage_ids[0] or False,
'date_action': time.strftime('%Y-%m-%d %H:%M:%S'),
}
if address_id:
if partner_id and address_id:
vals['partner_address_id'] = address_id[0]
else:
vals['partner_address_id'] = False
lead.write(vals, context=context)
leads.history(cr, uid, [lead], _('Converted to opportunity'), details='Converted to Opportunity', context=context)
@ -122,6 +141,18 @@ Leads Could not convert into Opportunity"))
}, context=context)
leads.log(cr, uid, lead.id, _("Lead '%s' has been converted to an opportunity.") % lead.name)
def send_mail_to_salesman(self, lead):
email_to = lead.user_id and lead.user_id.user_email
if not email_to:
return
email_from = lead.section_id and lead.section_id.user_id and lead.section_id.user_id.user_email or email_to
partner = lead.partner_id and lead.partner_id.name or lead.partner_name
subject = "lead %s converted into opportunity" % lead.name
body = "Info \n Id : %s \n Subject: %s \n Partner: %s \n Description : %s " % (lead.id, lead.name, lead.partner_id.name, lead.description)
try :
tools.email_send(email_from, email_to, subject, body)
except:
pass
def action_apply(self, cr, uid, ids, context=None):
"""
@ -130,6 +161,9 @@ Leads Could not convert into Opportunity"))
@return : View dictionary opening the Opportunity form view
"""
if not context:
context = {}
record_id = context and context.get('active_ids') or False
if not record_id:
return {'type': 'ir.actions.act_window_close'}
@ -154,19 +188,27 @@ Leads Could not convert into Opportunity"))
cr, uid, opportunity_view_tree, context=context).res_id
for lead in leads.browse(cr, uid, record_id, context=context):
if(lead.section_id):
if lead.section_id:
stage_ids = self.pool.get('crm.case.stage').search(cr, uid, [('type','=','opportunity'),('sequence','>=',1), ('section_ids','=', lead.section_id.id)])
else:
stage_ids = self.pool.get('crm.case.stage').search(cr, uid, [('type','=','opportunity'),('sequence','>=',1)])
data = self.browse(cr, uid, ids[0], context=context)
partner_ids = []
if data.action == 'create':
partner_ids = self._create_partner(cr, uid, ids, context=context)
partner_id = partner_ids and partner_ids[0] or data.partner_id.id
if data.action == 'create':
partner_ids = []
partner_ids = self._create_partner(cr, uid, ids, context=context)
partner_id = partner_ids and partner_ids[0]
elif data.action == 'exist':
partner_id = data.partner_id and data.partner_id.id
else:
partner_id = False
self._convert(cr, uid, ids, lead, partner_id, stage_ids, context=context)
if data.name == 'merge':
self.send_mail_to_salesman(lead)
#If we convert in mass, don't merge if there is no other opportunity but no warning
if data.name == 'merge' and (len(data.opportunity_ids) > 1 or not context.get('mass_convert') ):
merge_obj = self.pool.get('crm.merge.opportunity')
self.write(cr, uid, ids, {'opportunity_ids' : [(6,0, [data.opportunity_ids[0].id])]}, context=context)
context.update({'lead_ids' : record_id})
@ -189,4 +231,66 @@ Leads Could not convert into Opportunity"))
crm_lead2opportunity_partner()
class crm_lead2opportunity_mass_convert(osv.osv_memory):
_name = 'crm.lead2opportunity.partner.mass'
_description = 'Mass Lead To Opportunity Partner'
_inherit = 'crm.lead2opportunity.partner'
_columns = {
'user_ids': fields.many2many('res.users', 'mass_convert_rel', 'user_id', 'wizard_id', 'Salesmans'),
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
}
def mass_convert(self, cr, uid, ids, context=None):
lead_obj = self.pool.get('crm.lead')
if not context:
context = {}
active_ids = context.get('active_ids')
data = self.browse(cr, uid, ids, context=context)[0]
salesteam = data.section_id and data.section_id.id
if data.user_ids:
salesmans = map(lambda x : x.id, data.user_ids)
index = 0
else:
salesmans = False
for lead_id in active_ids:
value = {}
if salesteam:
value['section_id'] = salesteam
if salesmans:
value['user_id'] = salesmans[index]
index += 1
index = index < len(salesmans) and index or 0
if value:
lead_obj.write(cr, uid, [lead_id], value, context=context)
context['active_ids'] = [lead_id]
value = self.default_get(cr, uid, ['partner_id', 'opportunity_ids'], context=context)
value['opportunity_ids'] = [(6, 0, value['opportunity_ids'])]
self.write(cr, uid, ids, value, context=context)
self.action_apply(cr, uid, ids, context=context)
models_data = self.pool.get('ir.model.data')
result = models_data._get_id(cr, uid, 'crm', 'view_crm_case_opportunities_filter')
opportunity_view_search = models_data.browse(cr, uid, result, context=context).res_id
return {
'name': _('Opportunity'),
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'crm.lead',
'domain': [('type', '=', 'opportunity'), ('id', 'in', active_ids)],
'type': 'ir.actions.act_window',
'search_view_id': opportunity_view_search,
}
crm_lead2opportunity_mass_convert()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -36,5 +36,42 @@
</field>
</record>
<record id="view_crm_lead2opportunity_partner_mass" model="ir.ui.view">
<field name="name">crm.lead2opportunity.partner.mass.form</field>
<field name="model">crm.lead2opportunity.partner.mass</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Convert to Opportunity">
<field name="action"/>
<field name="name" colspan="4"/>
<separator string="Assigned opportunities to" colspan="4" />
<field name="section_id" />
<group col="4" colspan="4">
<separator string="Select Salesman" colspan="4" />
</group>
<field name="user_ids" nolabel="1" colspan="4">
<tree>
<field name="name" />
</tree>
</field>
<separator string="" colspan="4" />
<group col="4" colspan="4">
<button special="cancel" string="Cancel" icon="gtk-cancel"/>
<button name="mass_convert" string="Convert into Opportunities" type="object" icon="gtk-ok"/>
</group>
</form>
</field>
</record>
<act_window id="action_crm_send_mass_convert"
multi="True"
key2="client_action_multi" name="Convert opportunities"
res_model="crm.lead2opportunity.partner.mass" src_model="crm.lead"
view_mode="form" target="new" view_type="form"
context="{'mass_convert' : True}"
view_id="view_crm_lead2opportunity_partner_mass"/>
</data>
</openerp>

View File

@ -71,7 +71,7 @@ class crm_lead2partner(osv.osv_memory):
contact_obj = self.pool.get('res.partner.address')
partner_id = False
data = context and context.get('active_ids', []) or []
data = list(context and context.get('active_ids', []) or [])
res = super(crm_lead2partner, self).default_get(cr, uid, fields, context=context)
for lead in lead_obj.browse(cr, uid, data, context=context):
@ -85,17 +85,15 @@ class crm_lead2partner(osv.osv_memory):
substring(email from '([^ ,<@]+@[^> ,]+)') in (%s)""" % (','.join(email)))
address_ids = map(lambda x: x[0], cr.fetchall())
if address_ids:
addresses = contact_obj.browse(cr, uid, address_ids)
partner_ids = addresses and [addresses[0].partner_id.id] or False
partner_ids = partner_obj.search(cr, uid, [('address', 'in', address_ids)], context=context)
# Find partner name that matches the name of the lead
if not partner_ids and lead.partner_name:
partner_ids = partner_obj.search(cr, uid, [('name', '=', lead.partner_name)], context=context)
if not partner_ids:
cr.execute("""SELECT p.id from res_partner p
where regexp_replace(lower(p.name), '[^a-z]*', '', 'g') = regexp_replace(%s, '[^a-z]*', '', 'g')""", (lead.name.lower(), ))
partner_ids = map(lambda x: x[0], cr.fetchall())
partner_id = partner_ids and partner_ids[0] or False
if 'partner_id' in fields:
res.update({'partner_id': partner_id})

View File

@ -27,13 +27,19 @@ class crm_merge_opportunity(osv.osv_memory):
_name = 'crm.merge.opportunity'
_description = 'Merge two Opportunities'
def _get_first_not_null_id(self, attr, ops):
def _get_first_not_null_id(self, attr, ops, oldest):
if hasattr(oldest, attr) and getattr(oldest, attr):
return getattr(oldest, attr).id
for op in ops:
if hasattr(op, attr) and getattr(op, attr):
return getattr(op, attr).id
return False
def _get_first_not_null(self, attr, ops):
def _get_first_not_null(self, attr, ops, oldest):
if hasattr(oldest, attr) and getattr(oldest, attr):
return getattr(oldest, attr)
for op in ops:
if hasattr(op, attr) and getattr(op, attr):
return getattr(op, attr)
@ -54,6 +60,12 @@ class crm_merge_opportunity(osv.osv_memory):
attach_obj.write(cr, uid, attach_ids, {'res_id' : op_id})
def find_oldest(self, cr, uid, op_ids, context=None):
ids = [op_id.id for op_id in op_ids]
lead_obj = self.pool.get('crm.lead')
op_id = lead_obj.search(cr, uid, [('id', 'in', ids)], order='create_date' , context=context)
opps = lead_obj.browse(cr, uid, [op_id[0]], context=context)
return opps[0]
def merge(self, cr, uid, op_ids, context=None):
"""
@ -64,59 +76,66 @@ class crm_merge_opportunity(osv.osv_memory):
lead_ids = context and context.pop('lead_ids', []) or []
if len(op_ids) <= 1:
raise osv.except_osv(_('Warning !'),_('Please select more than one opportunities.'))
opportunities = opp_obj.browse(cr, uid, lead_ids, context=context)
opportunities_list = list(set(op_ids) - set(opportunities))
oldest_opp = self.find_oldest(cr, uid, op_ids, context=context)
if opportunities :
first_opportunity = opportunities[0]
tail_opportunities = opportunities_list
else:
first_opportunity = opportunities_list[0]
tail_opportunities = opportunities_list[1:]
data = {
'partner_id': self._get_first_not_null_id('partner_id', op_ids), # !!
'title': self._get_first_not_null_id('title', op_ids),
'name' : self._concat_all('name', op_ids), #not lost
'categ_id' : self._get_first_not_null_id('categ_id', op_ids), # !!
'channel_id' : self._get_first_not_null_id('channel_id', op_ids), # !!
'city' : self._get_first_not_null('city', op_ids), # !!
'company_id' : self._get_first_not_null_id('company_id', op_ids), #!!
'contact_name' : self._concat_all('contact_name', op_ids), #not lost
'country_id' : self._get_first_not_null_id('country_id', op_ids), #!!
'partner_address_id' : self._get_first_not_null_id('partner_address_id', op_ids), #!!
'partner_assigned_id' : hasattr(opp_obj,'partner_assigned_id') and self._get_first_not_null_id('partner_assigned_id', op_ids), #!!
'type_id' : self._get_first_not_null_id('type_id', op_ids), #!!
'user_id' : self._get_first_not_null_id('user_id', op_ids), #!!
'section_id' : self._get_first_not_null_id('section_id', op_ids), #!!
'state_id' : self._get_first_not_null_id('state_id', op_ids),
'partner_id': self._get_first_not_null_id('partner_id', op_ids, oldest_opp), # !!
'title': self._get_first_not_null_id('title', op_ids, oldest_opp),
'name' : self._get_first_not_null('name', op_ids, oldest_opp), #not lost
'categ_id' : self._get_first_not_null_id('categ_id', op_ids, oldest_opp), # !!
'channel_id' : self._get_first_not_null_id('channel_id', op_ids, oldest_opp), # !!
'city' : self._get_first_not_null('city', op_ids, oldest_opp), # !!
'company_id' : self._get_first_not_null_id('company_id', op_ids, oldest_opp), #!!
'contact_name' : self._get_first_not_null('contact_name', op_ids, oldest_opp), #not lost
'country_id' : self._get_first_not_null_id('country_id', op_ids, oldest_opp), #!!
'partner_address_id' : self._get_first_not_null_id('partner_address_id', op_ids, oldest_opp), #!!
'partner_assigned_id' : hasattr(opp_obj,'partner_assigned_id') and self._get_first_not_null_id('partner_assigned_id', op_ids, oldest_opp), #!!
'type_id' : self._get_first_not_null_id('type_id', op_ids, oldest_opp), #!!
'user_id' : self._get_first_not_null_id('user_id', op_ids, oldest_opp), #!!
'section_id' : self._get_first_not_null_id('section_id', op_ids, oldest_opp), #!!
'state_id' : self._get_first_not_null_id('state_id', op_ids, oldest_opp),
'description' : self._concat_all('description', op_ids), #not lost
'email' : self._get_first_not_null('email', op_ids), # !!
'fax' : self._get_first_not_null('fax', op_ids),
'mobile' : self._get_first_not_null('mobile', op_ids),
'partner_latitude' : hasattr(opp_obj,'partner_latitude') and self._get_first_not_null('partner_latitude', op_ids),
'partner_longitude' : hasattr(opp_obj,'partner_longitude') and self._get_first_not_null('partner_longitude', op_ids),
'partner_name' : self._get_first_not_null('partner_name', op_ids),
'phone' : self._get_first_not_null('phone', op_ids),
'probability' : self._get_first_not_null('probability', op_ids),
'planned_revenue' : self._get_first_not_null('planned_revenue', op_ids),
'street' : self._get_first_not_null('street', op_ids),
'street2' : self._get_first_not_null('street2', op_ids),
'zip' : self._get_first_not_null('zip', op_ids),
'email' : self._get_first_not_null('email', op_ids, oldest_opp), # !!
'fax' : self._get_first_not_null('fax', op_ids, oldest_opp),
'mobile' : self._get_first_not_null('mobile', op_ids, oldest_opp),
'partner_latitude' : hasattr(opp_obj,'partner_latitude') and self._get_first_not_null('partner_latitude', op_ids, oldest_opp),
'partner_longitude' : hasattr(opp_obj,'partner_longitude') and self._get_first_not_null('partner_longitude', op_ids, oldest_opp),
'partner_name' : self._get_first_not_null('partner_name', op_ids, oldest_opp),
'phone' : self._get_first_not_null('phone', op_ids, oldest_opp),
'probability' : self._get_first_not_null('probability', op_ids, oldest_opp),
'planned_revenue' : self._get_first_not_null('planned_revenue', op_ids, oldest_opp),
'street' : self._get_first_not_null('street', op_ids, oldest_opp),
'street2' : self._get_first_not_null('street2', op_ids, oldest_opp),
'zip' : self._get_first_not_null('zip', op_ids, oldest_opp),
'state' : 'open',
'create_date' : self._get_first_not_null('create_date', op_ids, oldest_opp),
'date_action_last': self._get_first_not_null('date_action_last', op_ids, oldest_opp),
'date_action_next': self._get_first_not_null('date_action_nexte', op_ids, oldest_opp),
'email_from' : self._get_first_not_null('email_from', op_ids, oldest_opp),
'email_cc' : self._get_first_not_null('email_cc', op_ids, oldest_opp),
'partner_name' : self._get_first_not_null('partner_name', op_ids, oldest_opp),
}
#copy message into the first opportunity + merge attachement
for opp in tail_opportunities:
for opp in tail_opportunities + [first_opportunity]:
attach_ids = self.get_attachments(cr, uid, opp, context=context)
self.set_attachements_res_id(cr, uid, first_opportunity.id, attach_ids)
for history in opp.message_ids:
new_history = message_obj.copy(cr, uid, history.id, default={'res_id': opp.id})
new_history = message_obj.write(cr, uid, history.id, {'res_id': first_opportunity.id, 'name' : _("From %s : %s") % (opp.name, history.name) }, context=context)
#Notification about loss of information
details = []
@ -214,8 +233,13 @@ class crm_merge_opportunity(osv.osv_memory):
res = super(crm_merge_opportunity, self).default_get(cr, uid, fields, context=context)
if record_ids:
opp_ids = []
opps = self.pool.get('crm.lead').browse(cr, uid, record_ids, context=context)
for opp in opps:
if opp.state not in ('done', 'cancel'):
opp_ids.append(opp.id)
if 'opportunity_ids' in fields:
res.update({'opportunity_ids': record_ids})
res.update({'opportunity_ids': opp_ids})
return res

View File

@ -57,9 +57,25 @@ class crm_send_new_email(osv.osv_memory):
'body': fields.text('Message Body', required=True),
'state': fields.selection(AVAILABLE_STATES, string='Set New State To', required=True),
'attachment_ids' : fields.one2many('crm.send.mail.attachment', 'wizard_id', 'Attachment'),
'html': fields.boolean('HTML formatting?', help="Select this if you want to send email with HTML formatting."),
'html': fields.boolean('HTML formatting?', help="Select this if you want to send email with HTML formatting."),
}
def action_mass_send(self, cr, uid, ids, context=None):
if not context:
context = {}
context.update({'mail' : 'new'})
actives_ids = context.get('active_ids')
print "mass_mail", context.get('mass_mail')
model = context.get('active_model')
case_pool = self.pool.get(model)
for id in actives_ids:
context.update({'active_id' : id})
self.action_send(cr, uid, ids, context=context)
return {'type': 'ir.actions.act_window_close'}
def action_send(self, cr, uid, ids, context=None):
""" This sends an email to ALL the addresses of the selected partners.
"""
@ -75,6 +91,8 @@ class crm_send_new_email(osv.osv_memory):
case_pool = self.pool.get(model)
res_id = context and context.get('active_id', False) or False
for obj in self.browse(cr, uid, ids, context=context):
attach = [
(x.name, base64.decodestring(x.binary)) for x in obj.attachment_ids
@ -110,8 +128,15 @@ class crm_send_new_email(osv.osv_memory):
res_id = hist.res_id
ref_id = hist.ref_id
case = case_pool.browse(cr, uid, res_id, context=context)
emails = re.findall(r'([^ ,<@]+@[^> ,]+)', obj.email_to or '')
if context.get('mass_mail'):
email_temp = case.email_from and tools.ustr(case.email_from) or ''
emails = re.findall(r'([^ ,<@]+@[^> ,]+)', email_temp)
else:
emails = re.findall(r'([^ ,<@]+@[^> ,]+)', obj.email_to or '')
email_cc = re.findall(r'([^ ,<@]+@[^> ,]+)', obj.email_cc or '')
emails = filter(None, emails)
body = obj.body
@ -180,10 +205,11 @@ class crm_send_new_email(osv.osv_memory):
user_obj = self.pool.get('res.users')
user_mail_from = user_obj._get_email_from(cr, uid, [uid], context=context)[uid]
for case in mod_obj.browse(cr, uid, res_id, context=context):
if 'email_to' in fields:
res.update({'email_to': case.email_from and tools.ustr(case.email_from) or ''})
if context.get('mass_mail'):
res.update({'email_to': ''})
if 'email_from' in fields:
res.update({'email_from': user_mail_from and tools.ustr(user_mail_from) or ''})
if 'reply_to' in fields:
@ -191,8 +217,12 @@ class crm_send_new_email(osv.osv_memory):
res.update({'reply_to': case.section_id and case.section_id.reply_to or False})
if 'subject' in fields:
res.update({'subject': tools.ustr(context.get('subject', case.name) or '')})
if context.get('mass_mail'):
res.update({'subject': ''})
if 'email_cc' in fields:
res.update({'email_cc': tools.ustr(case.email_cc or '')})
if context.get('mass_mail'):
res.update({'email_cc': ''})
if 'body' in fields:
res.update({'body': u'\n'+(tools.ustr(case.user_id.signature or ''))})
if 'state' in fields:

View File

@ -44,6 +44,45 @@
</field>
</record>
<record model="ir.ui.view" id="crm_send_new_mass_mail_view">
<field name="name">crm.new.send.mass.mail.form</field>
<field name="model">crm.send.mail</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Send Mail" col="4">
<group colspan="4" col="2">
<field name="email_from"/>
<field name="reply_to"/>
<field name="email_cc"/>
<field name="subject" />
<field name="html"/>
</group>
<notebook colspan="6">
<page string="Message">
<field name="body" nolabel="1" colspan="4" default_focus="1"/>
</page>
<page string="Attachments">
<field name="attachment_ids" colspan="4" nolabel="1">
<form string="Attachment">
<field name="binary" filename="name" />
<field name="name" />
</form>
<tree string="Attachments">
<field name="name" />
</tree>
</field>
</page>
</notebook>
<separator string="" colspan="6"/>
<group colspan="6" col="4" >
<field name="state" />
<button string="_Cancel" icon="gtk-cancel" special="cancel" />
<button name="action_mass_send" type="object" string="_Send to All" icon="gtk-go-forward" />
</group>
</form>
</field>
</record>
<!-- Send New Mail action -->
<record model="ir.actions.act_window" id="action_crm_send_mail">
@ -56,6 +95,14 @@
</record>
<act_window id="action_crm_send_mass_mail"
multi="True"
key2="client_action_multi" name="Send emails"
res_model="crm.send.mail" src_model="crm.lead"
view_mode="form" target="new" view_type="form"
context="{'mass_mail' : True}"
view_id="crm_send_new_mass_mail_view"/>
<!-- Reply to Mail view -->
<record model="ir.ui.view" id="crm_reply_mail_view">
@ -89,6 +136,9 @@
</field>
</record>
<!-- Reply to Mail action -->
<record model="ir.actions.act_window" id="action_crm_reply_mail">

View File

@ -43,6 +43,7 @@ You can also use the geolocalization without using the GPS coordinates.
'wizard/crm_forward_to_partner_view.xml',
'crm_lead_view.xml',
'report/crm_lead_report_view.xml',
'report/crm_partner_report_view.xml',
],
'test': ['test/test_crm_partner_assign.yml'],
'installable': True,

View File

@ -142,21 +142,6 @@ class crm_lead(osv.osv):
('country', '=', part.country_id.id),
], context=context)
# 3. third way: other countries, small area
if not part_ids:
part_ids = self.pool.get('res.partner').search(cr, uid, [
('partner_weight','>',0),
('partner_latitude','>',result[0]-2), ('partner_latitude','<',result[0]+2),
('partner_longitude','>',result[1]-1.5), ('partner_longitude','<',result[1]+1.5)
], context=context)
# 4. fourth way: other countries, big area
if not part_ids:
part_ids = self.pool.get('res.partner').search(cr, uid, [
('partner_weight','>',0),
('partner_latitude','>',result[0]-4), ('partner_latitude','<',result[0]+4),
('partner_longitude','>',result[1]-3), ('partner_longitude','<',result[1]+3)
], context=context)
# 5. fifth way: anywhere in same country
if not part_ids:

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
@ -15,9 +15,10 @@
# 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/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import crm_lead_report
import crm_partner_report

View File

@ -37,7 +37,7 @@ class crm_lead_report_assign(osv.osv):
_auto = False
_description = "CRM Lead Report"
_columns = {
'name': fields.char('Year', size=64, required=False, readonly=True),
'year': fields.char('Year', size=64, required=False, readonly=True),
'partner_assigned_id':fields.many2one('res.partner', 'Partner', readonly=True),
'grade_id':fields.many2one('res.partner.grade', 'Grade', readonly=True),
'user_id':fields.many2one('res.users', 'User', readonly=True),
@ -51,7 +51,7 @@ class crm_lead_report_assign(osv.osv):
('09', 'September'), ('10', 'October'),\
('11', 'November'), ('12', 'December')], 'Month', readonly=True),
'company_id': fields.many2one('res.company', 'Company', readonly=True),
'partner_date': fields.date('Partner Date', readonly=True),
'date_assign': fields.date('Partner Date', readonly=True),
'create_date': fields.datetime('Create Date', readonly=True),
'day': fields.char('Day', size=128, readonly=True),
'delay_open': fields.float('Delay to Open',digits=(16,2),readonly=True, group_operator="avg",help="Number of Days to open the case"),
@ -82,18 +82,20 @@ class crm_lead_report_assign(osv.osv):
CRM Lead Report
@param cr: the current row, from the database cursor
"""
print "WHATTT "
tools.drop_view_if_exists(cr, 'crm_lead_report_assign')
cr.execute("""
CREATE OR REPLACE VIEW crm_lead_report_assign AS (
SELECT
c.id,
to_char(c.create_date, 'YYYY') as name,
to_char(c.create_date, 'MM') as month,
to_char(c.create_date, 'YYYY-MM-DD') as day,
to_char(c.date_assign, 'YYYY') as year,
to_char(c.date_assign, 'MM') as month,
to_char(c.date_assign, 'YYYY-MM-DD') as day,
to_char(c.create_date, 'YYYY-MM-DD') as creation_date,
to_char(c.date_open, 'YYYY-MM-DD') as opening_date,
to_char(c.date_closed, 'YYYY-mm-dd') as date_closed,
c.state,
c.date_assign,
c.user_id,
c.probability,
c.probability as probability_max,

View File

@ -69,17 +69,17 @@
domain="[]"
context="{'group_by':'company_id'}" />
<separator orientation="vertical" />
<filter string="Partner Date" icon="terp-go-today"
<filter string="Assign Date" icon="terp-go-today"
domain="[]"
name="group_partner_date"
context="{'group_by':'partner_date'}"/>
context="{'group_by':'date_assign'}"/>
<separator orientation="vertical"/>
<filter string="Day" icon="terp-go-today"
domain="[]" context="{'group_by':'day'}"/>
<filter string="Month" icon="terp-go-month"
domain="[]" context="{'group_by':'month'}" />
<filter string="Year" icon="terp-go-year"
domain="[]" context="{'group_by':'name'}" />
domain="[]" context="{'group_by':'year'}" />
</group>
</search>
@ -107,9 +107,9 @@
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Opportunities Assignment Analysis">
<field name="name" invisible="1"/>
<field name="year" invisible="1"/>
<field name="month" invisible="1"/>
<field name="partner_date" invisible="1"/>
<field name="date_assign" invisible="1"/>
<field name="section_id" invisible="1" groups="base.group_extended"/>
<field name="user_id" invisible="1"/>
<field name="grade_id" invisible="1" widget="selection"/>

View File

@ -0,0 +1,63 @@
# -*- 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 fields,osv
import tools
class crm_partner_report_assign(osv.osv):
""" CRM Lead Report """
_name = "crm.partner.report.assign"
_auto = False
_description = "CRM Partner Report"
_columns = {
'name': fields.char('Partner name', size=64, required=False, readonly=True),
'grade_id':fields.many2one('res.partner.grade', 'Grade', readonly=True),
'user_id':fields.many2one('res.users', 'User', readonly=True),
'country_id':fields.many2one('res.country', 'Country', readonly=True),
'section_id':fields.many2one('crm.case.section', 'Sales Team', readonly=True),
'nbr': fields.integer('# of Partner', readonly=True),
'opp': fields.integer('# of Opportunity', readonly=True),
}
def init(self, cr):
"""
CRM Lead Report
@param cr: the current row, from the database cursor
"""
tools.drop_view_if_exists(cr, 'crm_partner_report_assign')
cr.execute("""
CREATE OR REPLACE VIEW crm_partner_report_assign AS (
SELECT
p.id,
p.name,
(SELECT country_id FROM res_partner_address a WHERE a.partner_id=p.id AND country_id is not null limit 1) as country_id,
p.grade_id,
p.user_id,
p.section_id,
1 as nbr,
(SELECT count(id) FROM crm_lead WHERE partner_assigned_id=p.id) AS opp
FROM
res_partner p
)""")
crm_partner_report_assign()

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Opportunity tree view -->
<record id="view_report_crm_partner_assign_filter" model="ir.ui.view">
<field name="name">crm.partner.report.assign.select</field>
<field name="model">crm.partner.report.assign</field>
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Partner assigned Analysis">
<group col="20" colspan="8">
<field name="country_id" />
<field name="grade_id"/>
<field name="user_id"/>
<field name="section_id"/>
</group>
<newline/>
<group expand="1" string="Group By...">
<filter string="Salesman" name="user" icon="terp-personal"
domain="[]" context="{'group_by':'user_id'}" />
<filter string="Country" icon="terp-go-home" name="group_country" context="{'group_by':'country_id'}" />
<separator orientation="vertical" />
<filter string="Section" icon="terp-personal+"
domain="[]"
context="{'group_by':'section_id'}" />
<filter string="Grade" name="group_grade" icon="terp-stock_symbol-selection"
domain="[]" context="{'group_by':'grade_id'}" />
<filter string="Name" name="" icon="terp-stock_symbol-selection"
domain="[]" context="{'group_by':'name'}" />
<separator orientation="vertical" />
</group>
</search>
</field>
</record>
<!-- Crm Lead Assign report Graph View -->
<record id="view_report_crm_partner_assign_tree" model="ir.ui.view">
<field name="name">crm.partner.assign.report.tree</field>
<field name="model">crm.partner.report.assign</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Opportunities Assignment Analysis">
<field name="name" invisible="1"/>
<field name="country_id" invisible="1"/>
<field name="grade_id" invisible="1"/>
<field name="section_id" invisible="1" groups="base.group_extended"/>
<field name="user_id" invisible="1"/>
<field name="nbr" string="#Partner" sum="#Partner"/>
<field name="opp"/>
</tree>
</field>
</record>
<!-- Leads by user and section Action -->
<record id="action_report_crm_partner_assign" model="ir.actions.act_window">
<field name="name">Partnership Analysis</field>
<field name="res_model">crm.partner.report.assign</field>
<field name="context">{'search_default_group_country': 1, 'search_default_group_grade': 1, 'group_by_no_leaf':1,'group_by':[]}</field>
<field name="view_mode">tree</field>
<field name="domain">[('grade_id', '!=', False)]</field>
</record>
<menuitem id="menu_report_crm_partner_assign_tree"
groups="base.group_extended"
parent="base.next_id_64" action="action_report_crm_partner_assign" sequence="5"/>
</data>
</openerp>

View File

@ -1,5 +1,6 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_ crm_lead_report_assign"," crm.lead.report.assign","model_crm_lead_report_assign","base.group_sale_salesman",1,1,1,0
"access_ crm_lead_report_assign","crm.lead.report.assign","model_crm_lead_report_assign","base.group_sale_salesman",1,1,1,0
"access_ crm_lead_report_assign_all","crm.lead.report.assign.all","model_crm_lead_report_assign","base.group_user",1,0,0,0
"access_crm_partner_report","crm.partner.report.assign.all","model_crm_partner_report_assign","base.group_sale_salesman",1,0,0,0
"access_res_partner_grade","res.partner.grade","model_res_partner_grade","base.group_sale_salesman",1,1,1,0
"access_res_partner_grade_manager","res.partner.grade.manager","model_res_partner_grade","base.group_sale_manager",1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_ crm_lead_report_assign crm.lead.report.assign crm.lead.report.assign model_crm_lead_report_assign base.group_sale_salesman 1 1 1 0
3 access_ crm_lead_report_assign_all crm.lead.report.assign.all model_crm_lead_report_assign base.group_user 1 0 0 0
4 access_crm_partner_report crm.partner.report.assign.all model_crm_partner_report_assign base.group_sale_salesman 1 0 0 0
5 access_res_partner_grade res.partner.grade model_res_partner_grade base.group_sale_salesman 1 1 1 0
6 access_res_partner_grade_manager res.partner.grade.manager model_res_partner_grade base.group_sale_manager 1 1 1 1

View File

@ -44,9 +44,10 @@ class crm_lead_forward_to_partner(osv.osv_memory):
_defaults = {
'name' : 'email',
'history': 'latest',
'email_from': lambda self, cr, uid, *a: self.pool.get('res.users')._get_email_from(cr, uid, uid)[uid]
'email_from': lambda self, cr, uid, *a: self.pool.get('res.users')._get_email_from(cr, uid, uid)[uid],
}
def get_whole_history(self, cr, uid, ids, context=None):
"""This function gets whole communication history and returns as top posting style
@param self: The object pointer
@ -87,7 +88,7 @@ class crm_lead_forward_to_partner(osv.osv_memory):
@param uid: the current users ID for security checks,
@param ids: List of Mails IDs
@param user: Changed User id
@param partner: Changed Partner id
@param partner: Changed Partner id
"""
if not user:
return {'value': {'email_to': False}}
@ -146,7 +147,7 @@ class crm_lead_forward_to_partner(osv.osv_memory):
@param uid: the current users ID for security checks,
@param ids: List of Mails IDs
@param user: Changed User id
@param partner: Changed Partner id
@param partner: Changed Partner id
"""
if not partner_id:
return {'value' : {'email_to' : False, 'address_id': False}}
@ -155,13 +156,13 @@ class crm_lead_forward_to_partner(osv.osv_memory):
addr = partner_obj.address_get(cr, uid, [partner_id], ['contact'])
data = {'address_id': addr['contact']}
data.update(self.on_change_address(cr, uid, ids, addr['contact'])['value'])
partner = partner_obj.browse(cr, uid, [partner_id])
user_id = partner and partner[0].user_id or False
email = user_id and user_id.user_email or ''
data.update({'email_cc' : email})
return {
'value' : data,
'value' : data,
'domain' : {'address_id' : partner_id and "[('partner_id', '=', partner_id)]" or "[]"}
}
@ -236,23 +237,23 @@ class crm_lead_forward_to_partner(osv.osv_memory):
body.append("%s: %s" % (field_definition.string, value or ''))
elif lead.type == 'opportunity':
pa = lead.partner_address_id
body = [
"Partner: %s" % (lead.partner_id and lead.partner_id.name_get()[0][1]),
"Contact: %s" % (pa.name or ''),
"Title: %s" % (pa.title or ''),
"Function: %s" % (pa.function or ''),
"Street: %s" % (pa.street or ''),
"Street2: %s" % (pa.street2 or ''),
"Zip: %s" % (pa.zip or ''),
"City: %s" % (pa.city or ''),
"Country: %s" % (pa.country_id and pa.country_id.name_get()[0][1] or ''),
"State: %s" % (pa.state_id and pa.state_id.name_get()[0][1] or ''),
"Email: %s" % (pa.email or ''),
"Phone: %s" % (pa.phone or ''),
"Fax: %s" % (pa.fax or ''),
"Mobile: %s" % (pa.mobile or ''),
"Lead Category: %s" % (lead.categ_id and lead.categ_id.name or ''),
"Details: %s" % (lead.description or ''),
body += [
"Partner: %s" % (lead.partner_id and lead.partner_id.name_get()[0][1]),
"Contact: %s" % (pa.name or ''),
"Title: %s" % (pa.title or ''),
"Function: %s" % (pa.function or ''),
"Street: %s" % (pa.street or ''),
"Street2: %s" % (pa.street2 or ''),
"Zip: %s" % (pa.zip or ''),
"City: %s" % (pa.city or ''),
"Country: %s" % (pa.country_id and pa.country_id.name_get()[0][1] or ''),
"State: %s" % (pa.state_id and pa.state_id.name_get()[0][1] or ''),
"Email: %s" % (pa.email or ''),
"Phone: %s" % (pa.phone or ''),
"Fax: %s" % (pa.fax or ''),
"Mobile: %s" % (pa.mobile or ''),
"Lead Category: %s" % (lead.categ_id and lead.categ_id.name or ''),
"Details: %s" % (lead.description or ''),
]
return "\n".join(body + ['---'])
@ -260,26 +261,66 @@ class crm_lead_forward_to_partner(osv.osv_memory):
"""
This function gets default values
"""
if context is None:
context = {}
defaults = super(crm_lead_forward_to_partner, self).default_get(cr, uid, fields, context=context)
active_id = context.get('active_id')
if not active_id:
return defaults
lead_proxy = self.pool.get('crm.lead')
partner_obj = self.pool.get('res.partner')
lead = lead_proxy.browse(cr, uid, active_id, context=context)
email_cc = ''
email = ''
if lead.partner_assigned_id:
partner = partner_obj.browse(cr, uid, [lead.partner_assigned_id.id])
user_id = partner and partner[0].user_id or False
email_cc = user_id and user_id.user_email or ''
addr = partner_obj.address_get(cr, uid, [partner[0].id], ['contact'])
email = self.pool.get('res.partner.address').browse(cr, uid, addr['contact']).email
body = self._get_case_history(cr, uid, defaults.get('history', 'latest'), lead.id, context=context)
defaults.update({
'subject' : '%s: %s' % (_('Fwd'), lead.name),
'subject' : '%s: %s - %s' % (_('Fwd'), 'Openerp lead forward', lead.name),
'body' : body,
'email_cc' : ''
'email_cc' : email_cc,
'email_to' : email or 'dummy@dummy.ly'
})
return defaults
crm_lead_forward_to_partner()
class crm_lead_mass_forward_to_partner(osv.osv_memory):
_name = 'crm.lead.mass.forward.to.partner'
_inherit = 'crm.lead.forward.to.partner'
def action_mass_forward(self, cr, uid, ids, context=None):
if not context:
context = {}
active_ids = context.get('active_ids')
case_obj = self.pool.get('crm.lead')
for case in case_obj.browse(cr, uid, active_ids, context=context):
if not case.partner_assigned_id:
case_obj.assign_partner(cr,uid, [case.id], context=context)
case = case_obj.browse(cr, uid, case.id, context=context)
if not case.partner_assigned_id:
continue
context.update({'active_id' : case.id})
value = self.default_get(cr, uid, ['body', 'email_to', 'email_cc', 'subject', 'history'], context=context)
self.write(cr, uid, ids, value, context=context)
self.action_forward(cr,uid, ids, context=context)
return {'type': 'ir.actions.act_window_close'}
crm_lead_mass_forward_to_partner()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -38,14 +38,70 @@
</button>
</field>
</record>
<record model="ir.actions.act_window" id="crm_lead_forward_to_partner_act">
<field name="name">Forward to Partner</field>
<field name="res_model">crm.lead.forward.to.partner</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="crm_lead_forward_to_partner_form1"/>
<field name="view_id" ref="crm_lead_forward_to_partner_form"/>
<field name="target">new</field>
</record>
<record model="ir.ui.view" id="crm_forward_mass_mail_view">
<field name="name">crm.new.mass.forward.mail.form</field>
<field name="model">crm.lead.mass.forward.to.partner</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Send Mail" col="4">
<group colspan="4" col="2">
<separator string="Forward to Partner" colspan="4" />
<field name="history" colspan="2" on_change="on_change_history(history, context)" invisible="1"/>
<field name="email_from"/>
<field name="reply_to"/>
<field name="email_to" invisible="1" />
<field name="email_cc" invisible="1" />
<field name="subject" invisible="1" />
<field name="html"/>
</group>
<notebook colspan="6" >
<page string="Message" >
<field name="body" nolabel="1" colspan="4" default_focus="1" readonly="1"/>
</page>
<page string="Attachments" >
<field name="attachment_ids" colspan="4" nolabel="1">
<form string="Attachment">
<field name="binary" filename="name" />
<field name="name" />
</form>
<tree string="Attachments">
<field name="name" />
</tree>
</field>
</page>
</notebook>
<separator string="" colspan="6"/>
<group colspan="6" col="4" >
<field name="state" />
<button string="_Cancel" icon="gtk-cancel" special="cancel" />
<button name="action_mass_forward" type="object" string="_Mass forward" icon="gtk-go-forward" />
</group>
</form>
</field>
</record>
<act_window id="action_crm_send_mass_forward"
multi="True"
key2="client_action_multi" name="Mass forward to partner"
res_model="crm.lead.mass.forward.to.partner" src_model="crm.lead"
view_mode="form" target="new" view_type="form"
context="{'mass_forward' : True}"
view_id="crm_forward_mass_mail_view"
/>
</data>
</openerp>

View File

@ -151,7 +151,7 @@ class hr_employee(osv.osv):
'work_email': fields.char('Work E-mail', size=240),
'work_location': fields.char('Office Location', size=32),
'notes': fields.text('Notes'),
'parent_id': fields.many2one('hr.employee', 'Manager'),
'parent_id': fields.many2one('hr.employee', 'Manager'),
'category_ids': fields.many2many('hr.employee.category', 'employee_category_rel', 'emp_id', 'category_id', 'Category'),
'child_ids': fields.one2many('hr.employee', 'parent_id', 'Subordinates'),
'resource_id': fields.many2one('resource.resource', 'Resource', ondelete='cascade', required=True),
@ -240,7 +240,7 @@ class res_users(osv.osv):
except:
# Tolerate a missing shortcut. See product/product.py for similar code.
logging.getLogger('orm').debug('Skipped meetings shortcut for user "%s"', data.get('name','<new'))
return user_id
res_users()

View File

@ -251,7 +251,7 @@ class mailgate_message(osv.osv):
for message in self.browse(cr, uid, ids, context=context):
msg_txt = ''
if message.history:
msg_txt += _('%s wrote on %s:\n\t') % (message.email_from or '/', format_date_tz(message.date, tz))
msg_txt += _('%s wrote on %s: \n Subject: %s \n\t') % (message.email_from or '/', format_date_tz(message.date, tz), message.name)
if message.description:
msg_txt += self.truncate_data(cr, uid, message.description, context=context)
else:

View File

@ -21,6 +21,7 @@
import time
import base64
import itertools
from datetime import datetime
from dateutil.relativedelta import relativedelta
from operator import itemgetter