[MERGE] Merged mail state feature. Task description :
- clean mailgateway, add a message_state for ex-'pending' state due to new messages read/unread behavior (new message -> unread; when viewing form view -> automatically read) - make the needaction mechanism mainly based on mail.thread: when the message_state is unread, set the object responsible as need_action user_id. Clean get_needaction_user_ids methods through the addons to clean them. - buttons 'mark read / mark unread' in more - track in modules specific behavior of mailgateways to clean and update - clean mail.thread get_subscribers bzr revid: tde@openerp.com-20120702165335-9d34qwkhkz1ng6p9
This commit is contained in:
+54
-73
@@ -273,14 +273,6 @@ class crm_lead(base_stage, osv.osv):
|
||||
'color': 0,
|
||||
}
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
result = dict.fromkeys(ids, [])
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
# salesman must perform an action when in draft mode
|
||||
if obj.state == 'draft' and obj.user_id:
|
||||
result[obj.id] = [obj.user_id.id]
|
||||
return result
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
obj_id = super(crm_lead, self).create(cr, uid, vals, context)
|
||||
self.create_send_note(cr, uid, [obj_id], context=context)
|
||||
@@ -770,64 +762,6 @@ class crm_lead(base_stage, osv.osv):
|
||||
'type': 'ir.actions.act_window',
|
||||
}
|
||||
|
||||
|
||||
def message_new(self, cr, uid, msg, custom_values=None, context=None):
|
||||
"""Automatically calls when new email message arrives"""
|
||||
res_id = super(crm_lead, self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
|
||||
subject = msg.get('subject') or _("No Subject")
|
||||
body = msg.get('body_text')
|
||||
|
||||
msg_from = msg.get('from')
|
||||
priority = msg.get('priority')
|
||||
vals = {
|
||||
'name': subject,
|
||||
'email_from': msg_from,
|
||||
'email_cc': msg.get('cc'),
|
||||
'description': body,
|
||||
'user_id': False,
|
||||
}
|
||||
if priority:
|
||||
vals['priority'] = priority
|
||||
vals.update(self.message_partner_by_email(cr, uid, msg.get('from', False)))
|
||||
self.write(cr, uid, [res_id], vals, context)
|
||||
return res_id
|
||||
|
||||
def message_update(self, cr, uid, ids, msg, vals=None, default_act='pending', context=None):
|
||||
if isinstance(ids, (str, int, long)):
|
||||
ids = [ids]
|
||||
if vals == None:
|
||||
vals = {}
|
||||
super(crm_lead, self).message_update(cr, uid, ids, msg, context=context)
|
||||
|
||||
if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES):
|
||||
vals['priority'] = msg.get('priority')
|
||||
maps = {
|
||||
'cost':'planned_cost',
|
||||
'revenue': 'planned_revenue',
|
||||
'probability':'probability'
|
||||
}
|
||||
vls = {}
|
||||
for line in msg['body_text'].split('\n'):
|
||||
line = line.strip()
|
||||
res = tools.misc.command_re.match(line)
|
||||
if res and maps.get(res.group(1).lower()):
|
||||
key = maps.get(res.group(1).lower())
|
||||
vls[key] = res.group(2).lower()
|
||||
vals.update(vls)
|
||||
|
||||
# Unfortunately the API is based on lists
|
||||
# but we want to update the state based on the
|
||||
# previous state, so we have to loop:
|
||||
for case in self.browse(cr, uid, ids, context=context):
|
||||
values = dict(vals)
|
||||
if case.state in CRM_LEAD_PENDING_STATES:
|
||||
#re-open
|
||||
values.update(state=crm.AVAILABLE_STATES[1][0])
|
||||
if not case.date_open:
|
||||
values['date_open'] = time.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
res = self.write(cr, uid, [case.id], values, context=context)
|
||||
return res
|
||||
|
||||
def action_makeMeeting(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
This opens Meeting's calendar view to schedule meeting on current Opportunity
|
||||
@@ -882,19 +816,66 @@ class crm_lead(base_stage, osv.osv):
|
||||
if stage.on_change:
|
||||
vals['probability'] = stage.probability
|
||||
return super(crm_lead,self).write(cr, uid, ids, vals, context)
|
||||
|
||||
|
||||
# ----------------------------------------
|
||||
# Mail Gateway
|
||||
# ----------------------------------------
|
||||
|
||||
def message_new(self, cr, uid, msg, custom_values=None, context=None):
|
||||
""" Overrides mail_thread message_new that is called by the mailgateway
|
||||
through message_process.
|
||||
This override updates the document according to the email.
|
||||
"""
|
||||
if custom_values is None: custom_values = {}
|
||||
custom_values.update({
|
||||
'name': msg.get('subject') or _("No Subject"),
|
||||
'description': msg.get('body_text'),
|
||||
'email_from': msg.get('from'),
|
||||
'email_cc': msg.get('cc'),
|
||||
'user_id': False,
|
||||
})
|
||||
if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES):
|
||||
custom_values['priority'] = msg.get('priority')
|
||||
custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from', False), context=context))
|
||||
return super(crm_lead, self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
|
||||
|
||||
def message_update(self, cr, uid, ids, msg, update_vals=None, context=None):
|
||||
""" Overrides mail_thread message_update that is called by the mailgateway
|
||||
through message_process.
|
||||
This method updates the document according to the email.
|
||||
"""
|
||||
if isinstance(ids, (str, int, long)):
|
||||
ids = [ids]
|
||||
if update_vals is None: update_vals = {}
|
||||
|
||||
if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES):
|
||||
vals['priority'] = msg.get('priority')
|
||||
maps = {
|
||||
'cost':'planned_cost',
|
||||
'revenue': 'planned_revenue',
|
||||
'probability':'probability',
|
||||
}
|
||||
for line in msg.get('body_text', '').split('\n'):
|
||||
line = line.strip()
|
||||
res = tools.misc.command_re.match(line)
|
||||
if res and maps.get(res.group(1).lower()):
|
||||
key = maps.get(res.group(1).lower())
|
||||
vals[key] = res.group(2).lower()
|
||||
|
||||
return super(crm_lead, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context)
|
||||
|
||||
# ----------------------------------------
|
||||
# OpenChatter methods and notifications
|
||||
# ----------------------------------------
|
||||
|
||||
def message_get_subscribers(self, cr, uid, ids, context=None):
|
||||
sub_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context)
|
||||
# add salesman to the subscribers
|
||||
""" Override to add the salesman. """
|
||||
user_ids = super(crm_lead, self).message_get_subscribers(cr, uid, ids, context=context)
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.user_id:
|
||||
sub_ids.append(obj.user_id.id)
|
||||
return self.pool.get('res.users').read(cr, uid, sub_ids, context=context)
|
||||
|
||||
if obj.user_id and not obj.user_id.id in user_ids:
|
||||
user_ids.append(obj.user_id.id)
|
||||
return user_ids
|
||||
|
||||
def stage_set_send_note(self, cr, uid, ids, stage_id, context=None):
|
||||
""" Override of the (void) default notification method. """
|
||||
stage_name = self.pool.get('crm.case.stage').name_get(cr, uid, [stage_id], context=context)[0][1]
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<!-- Demo Leads -->
|
||||
<data noupdate="1">
|
||||
<!--
|
||||
Demo Leads
|
||||
-->
|
||||
|
||||
<record id="crm_case_itisatelesalescampaign0" model="crm.lead">
|
||||
<field name="type_id" ref="crm.type_lead1"/>
|
||||
<field eval="'3'" name="priority"/>
|
||||
@@ -21,7 +24,6 @@
|
||||
<field eval="'Plan to Attend a Training'" name="name"/>
|
||||
<field eval="'0033 769 703-274'" name="phone"/>
|
||||
</record>
|
||||
|
||||
<record id="crm_case_electonicgoodsdealer0" model="crm.lead">
|
||||
<field name="type_id" ref="crm.type_lead7"/>
|
||||
<field eval="'4'" name="priority"/>
|
||||
@@ -76,7 +78,6 @@
|
||||
<field eval="'(514) 698-4118'" name="phone"/>
|
||||
<field eval="'hmc@thgascompany.com'" name="email_from"/>
|
||||
</record>
|
||||
|
||||
<record id="crm_case_itdeveloper0" model="crm.lead">
|
||||
<field name="type_id" ref="crm.type_lead4"/>
|
||||
<field eval="'3'" name="priority"/>
|
||||
@@ -96,7 +97,6 @@
|
||||
<field eval="'(855) 924-4364'" name="phone"/>
|
||||
<field eval="'helle@stonageit.be'" name="email_from"/>
|
||||
</record>
|
||||
|
||||
<record id="crm_case_mgroperations0" model="crm.lead">
|
||||
<field eval="1" name="active"/>
|
||||
<field name="type_id" ref="crm.type_lead3"/>
|
||||
@@ -209,10 +209,21 @@
|
||||
|
||||
<!-- Call Function to Cancel the leads (set as Dead) -->
|
||||
<function model="crm.lead" name="case_cancel"
|
||||
eval="[ref('crm_case_company_partnership0'), ref('crm_case_vpoperations0'), ref('crm_case_developingwebapplications0'), ref('crm_case_webvisitor0')], {'install_mode': True}"
|
||||
eval="[ ref('crm_case_company_partnership0'), ref('crm_case_vpoperations0'),
|
||||
ref('crm_case_developingwebapplications0'), ref('crm_case_webvisitor0')],
|
||||
{'install_mode': True}"
|
||||
/>
|
||||
<!-- Call Function to set the leads as Unread -->
|
||||
<function model="crm.lead" name="message_mark_as_unread"
|
||||
eval="[ ref('crm_case_itisatelesalescampaign0'), ref('crm_case_electonicgoodsdealer0'),
|
||||
ref('crm_case_itdeveloper0'), ref('crm_case_employee0')], {}"
|
||||
/>
|
||||
|
||||
<!-- Demo Opportunities -->
|
||||
|
||||
<!--
|
||||
Demo Opportunities
|
||||
-->
|
||||
|
||||
<record id="crm_case_construstazunits0" model="crm.lead">
|
||||
<field eval="60" name="probability"/>
|
||||
<field eval="1" name="active"/>
|
||||
@@ -398,7 +409,7 @@
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="categ_id" ref="crm.categ_oppor5"/>
|
||||
<field name="stage_id" ref="crm.stage_lead5"/>
|
||||
<field eval="'Need new design for my website'" name="name"/>
|
||||
<field eval="'Need a new design for my website'" name="name"/>
|
||||
<field eval="time.strftime('%Y-05-01')" name="date_action"/>
|
||||
<field eval="time.strftime('%Y-06-30')" name="date_deadline"/>
|
||||
<field eval="'info@opensides.be'" name="email_from"/>
|
||||
@@ -469,7 +480,7 @@
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="categ_id" ref="crm.categ_oppor1"/>
|
||||
<field name="stage_id" ref="crm.stage_lead4"/>
|
||||
<field eval="'Need customize the solution'" name="name"/>
|
||||
<field eval="'Need to customize the solution'" name="name"/>
|
||||
<field eval="'Conf call with technical service'" name="title_action"/>
|
||||
<field name="partner_name">Thymbra</field>
|
||||
<field name="street">Palermo, Capital Federal</field>
|
||||
@@ -522,5 +533,12 @@
|
||||
<field name="stage_id" ref="crm.stage_lead1"/>
|
||||
<field eval="'Need more info about your pc2'" name="name"/>
|
||||
</record>
|
||||
|
||||
<!-- Call Function to set the opportunities as Unread -->
|
||||
<function model="crm.lead" name="message_mark_as_unread"
|
||||
eval="[ ref('crm_case_rdroundfundingunits25'), ref('crm_case_unifliege'),
|
||||
ref('crm_case_ericdubois4'), ref('crm_case_abcfuelcounits0')], {}"
|
||||
/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -2,6 +2,43 @@
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- Read/Unread actions -->
|
||||
<record id="actions_server_crm_lead_unread" model="ir.actions.server">
|
||||
<field name="name">Mark unread</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_crm_lead"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_unread(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_crm_lead_unread" model="ir.values">
|
||||
<field name="name">action_crm_lead_unread</field>
|
||||
<field name="action_id" ref="actions_server_crm_lead_unread"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_crm_lead_unread'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_crm_lead" />
|
||||
<field name="model">crm.lead</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record id="actions_server_crm_lead_read" model="ir.actions.server">
|
||||
<field name="name">Mark read</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_crm_lead"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_read(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_crm_lead_read" model="ir.values">
|
||||
<field name="name">action_crm_lead_read</field>
|
||||
<field name="action_id" ref="actions_server_crm_lead_read"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_crm_lead_read'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_crm_lead" />
|
||||
<field name="model">crm.lead</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<!--
|
||||
CRM CASE STAGE
|
||||
-->
|
||||
@@ -185,8 +222,7 @@
|
||||
<field name="model">crm.lead</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Leads" fonts="bold:needaction_pending==True" colors="blue:state=='pending';grey:state in ('cancel', 'done')">
|
||||
<field name="needaction_pending" invisible="1"/>
|
||||
<tree string="Leads" fonts="bold:needaction_pending==True" colors="grey:state in ('cancel', 'done')">
|
||||
<field name="date_deadline" invisible="1"/>
|
||||
<field name="create_date" groups="base.group_no_one"/>
|
||||
<field name="name"/>
|
||||
@@ -202,6 +238,7 @@
|
||||
<field name="referred" invisible="1"/>
|
||||
<field name="channel_id" invisible="1"/>
|
||||
<field name="subjects" invisible="1"/>
|
||||
<field name="needaction_pending" invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -235,6 +272,8 @@
|
||||
<field name="user_email"/>
|
||||
<field name="user_id"/>
|
||||
<field name="partner_address_email"/>
|
||||
<field name="message_summary"/>
|
||||
<field name="needaction_pending"/>
|
||||
<templates>
|
||||
<t t-name="lead_details">
|
||||
<ul class="oe_kanban_tooltip">
|
||||
@@ -288,6 +327,8 @@
|
||||
<img t-att-src="kanban_image('res.users', 'avatar', record.user_id.raw_value[0])" t-att-title="record.user_id.value" width="24" height="24" class="oe_kanban_avatar"/>
|
||||
</div>
|
||||
<div class="oe_kanban_footer_left">
|
||||
<t t-if="record.needaction_pending.raw_value"><span class="oe_kanban_mail_new">New</span></t>
|
||||
<t t-raw="record.message_summary.raw_value"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oe_clear"></div>
|
||||
@@ -313,6 +354,10 @@
|
||||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Leads">
|
||||
<filter icon="terp-mail-message-new"
|
||||
string="Inbox" help="Unread messages"
|
||||
name="needaction_pending"
|
||||
domain="[('needaction_pending','=',True)]"/>
|
||||
<field name="name" string="Lead / Customer" filter_domain="['|','|',('partner_name','ilike',self),('email_from','ilike',self),('name','ilike',self)]"/>
|
||||
<!-- subjects is not set as store=True so, it is placed outside filter_domain-->
|
||||
<field name="subjects"/>
|
||||
@@ -327,10 +372,6 @@
|
||||
string="Open"
|
||||
name="open"
|
||||
domain="[('state','=','open')]"/>
|
||||
<filter icon="terp-gtk-media-pause"
|
||||
string="Pending"
|
||||
name="pending"
|
||||
domain="[('state','=','pending')]"/>
|
||||
<filter string="Unassigned Leads"
|
||||
icon="terp-personal-"
|
||||
domain="[('user_id','=', False)]"
|
||||
@@ -424,7 +465,6 @@
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1><field name="name"/></h1>
|
||||
|
||||
<label for="planned_revenue" class="oe_edit_only"/>
|
||||
<h2>
|
||||
<field name="planned_revenue" class="oe_inline"/>
|
||||
@@ -535,8 +575,7 @@
|
||||
<field name="model">crm.lead</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Opportunities" fonts="bold:needaction_pending==True" colors="blue:state=='pending' and not(date_deadline and (date_deadline < current_date));gray:state in ('cancel', 'done');red:date_deadline and (date_deadline < current_date)">
|
||||
<field name="needaction_pending" invisible="1"/>
|
||||
<tree string="Opportunities" fonts="bold:needaction_pending==True" colors="gray:state in ('cancel', 'done');red:date_deadline and (date_deadline < current_date)">
|
||||
<field name="date_deadline" invisible="1"/>
|
||||
<field name="create_date" groups="base.group_no_one"/>
|
||||
<field name="name" string="Opportunity"/>
|
||||
@@ -555,6 +594,7 @@
|
||||
<field name="priority" invisible="1"/>
|
||||
<field name="categ_id" invisible="1"/>
|
||||
<field name="state" groups="base.group_no_one"/>
|
||||
<field name="needaction_pending" invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -567,21 +607,21 @@
|
||||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Opportunities">
|
||||
<filter icon="terp-mail-message-new"
|
||||
string="Inbox" help="Unread messages"
|
||||
name="needaction_pending"
|
||||
domain="[('needaction_pending','=',True)]"/>
|
||||
<field name="name" string="Opportunity / Customer"
|
||||
filter_domain="['|','|','|',('partner_id','ilike',self),('partner_name','ilike',self),('email_from','ilike',self),('name', 'ilike', self)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-check"
|
||||
string="New" help="New Opportunities"
|
||||
string="New" help="New Opportunities"
|
||||
name="new"
|
||||
domain="[('state','=','draft')]"/>
|
||||
<filter icon="terp-camera_test"
|
||||
string="Open" help="Open Opportunities"
|
||||
string="Open" help="Open Opportunities"
|
||||
name="open"
|
||||
domain="[('state','=','open')]"/>
|
||||
<filter icon="terp-gtk-media-pause"
|
||||
string="Pending" help="Pending Opportunities"
|
||||
name="pending"
|
||||
domain="[('state','=','pending')]"/>
|
||||
<filter string="Unassigned Opportunities"
|
||||
icon="terp-personal-"
|
||||
domain="[('user_id','=', False)]"
|
||||
|
||||
@@ -76,13 +76,6 @@ class crm_meeting(base_state, osv.Model):
|
||||
self.create_send_note(cr, uid, [obj_id], context=context)
|
||||
return obj_id
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
result = dict.fromkeys(ids, [])
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if (obj.state == 'draft' and obj.user_id):
|
||||
result[obj.id] = [obj.user_id.id]
|
||||
return result
|
||||
|
||||
def case_open(self, cr, uid, ids, context=None):
|
||||
""" Confirms meeting """
|
||||
res = super(crm_meeting, self).case_open(cr, uid, ids, context)
|
||||
|
||||
@@ -2,6 +2,43 @@
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- Read/Unread actions -->
|
||||
<record id="actions_server_crm_meeting_unread" model="ir.actions.server">
|
||||
<field name="name">Mark unread</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_crm_meeting"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_unread(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_crm_meeting_unread" model="ir.values">
|
||||
<field name="name">action_crm_meeting_unread</field>
|
||||
<field name="action_id" ref="actions_server_crm_meeting_unread"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_crm_meeting_unread'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_crm_meeting" />
|
||||
<field name="model">crm.meeting</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record id="actions_server_crm_meeting_read" model="ir.actions.server">
|
||||
<field name="name">Mark read</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_crm_meeting"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_read(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_crm_meeting_read" model="ir.values">
|
||||
<field name="name">action_crm_meeting_read</field>
|
||||
<field name="action_id" ref="actions_server_crm_meeting_read"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_crm_meeting_read'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_crm_meeting" />
|
||||
<field name="model">crm.meeting</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<!-- CRM Meetings Categories Form View -->
|
||||
|
||||
<record id="crm_meeting_categ_action" model="ir.actions.act_window">
|
||||
@@ -234,7 +271,6 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Meetings" fonts="bold:needaction_pending==True"
|
||||
colors="red:state=='open';black:state in ('draft', 'cancel','done','pending')">
|
||||
<field name="needaction_pending" invisible="1"/>
|
||||
<field name="name" string="Subject" />
|
||||
<field name="user_id"/>
|
||||
<field name="date"/>
|
||||
@@ -243,6 +279,7 @@
|
||||
<field name="partner_id" string="Partner" />
|
||||
<field name="location" />
|
||||
<field name="categ_id"/>
|
||||
<field name="needaction_pending" invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -284,6 +321,11 @@
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Meetings">
|
||||
<group>
|
||||
<filter icon="terp-mail-message-new"
|
||||
string="Inbox" help="Unread messages"
|
||||
name="needaction_pending"
|
||||
domain="[('needaction_pending','=',True)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<field name="name" string="Meeting / Partner"
|
||||
filter_domain="['|',('name','ilike',self),('partner_id','ilike', self)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
|
||||
@@ -288,14 +288,6 @@ class crm_phonecall(base_state, osv.osv):
|
||||
# ----------------------------------------
|
||||
# OpenChatter
|
||||
# ----------------------------------------
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
result = dict.fromkeys(ids)
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
result[obj.id] = []
|
||||
if (obj.state == 'draft' and obj.user_id):
|
||||
result[obj.id] = [obj.user_id.id]
|
||||
return result
|
||||
|
||||
def case_get_note_msg_prefix(self, cr, uid, id, context=None):
|
||||
return 'Phonecall'
|
||||
|
||||
@@ -2,8 +2,44 @@
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- Phonecall Categories Form View -->
|
||||
<!-- Read/Unread actions -->
|
||||
<record id="actions_server_crm_phonecall_unread" model="ir.actions.server">
|
||||
<field name="name">Mark unread</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_crm_phonecall"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_unread(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_crm_phonecall_unread" model="ir.values">
|
||||
<field name="name">action_crm_phonecall_unread</field>
|
||||
<field name="action_id" ref="actions_server_crm_phonecall_unread"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_crm_phonecall_unread'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_crm_phonecall" />
|
||||
<field name="model">crm.phonecall</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record id="actions_server_crm_phonecall_read" model="ir.actions.server">
|
||||
<field name="name">Mark read</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_crm_phonecall"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_read(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_crm_phonecall_read" model="ir.values">
|
||||
<field name="name">action_crm_phonecall_read</field>
|
||||
<field name="action_id" ref="actions_server_crm_phonecall_read"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_crm_phonecall_read'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_crm_phonecall" />
|
||||
<field name="model">crm.phonecall</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<!-- Phonecall Categories Form View -->
|
||||
<record id="crm_phonecall_categ_action" model="ir.actions.act_window">
|
||||
<field name="name">Phonecall Categories</field>
|
||||
<field name="res_model">crm.case.categ</field>
|
||||
@@ -34,6 +70,7 @@
|
||||
<field name="categ_id" invisible="1"/>
|
||||
<field name="create_date" invisible="1"/>
|
||||
<field name="opportunity_id" invisible="1"/>
|
||||
<field name="needaction_pending" invisible="1"/>
|
||||
<button string="Convert to Opportunity"
|
||||
name="%(phonecall2opportunity_act)d"
|
||||
states="open,pending"
|
||||
@@ -123,7 +160,6 @@
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Phone Calls" fonts="bold:needaction_pending==True" editable="top">
|
||||
<field name="needaction_pending" invisible="1"/>
|
||||
<field name="date"/>
|
||||
<field name="name"/>
|
||||
<field name="partner_id"
|
||||
@@ -138,6 +174,7 @@
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="create_date" invisible="1"/>
|
||||
<field name="opportunity_id" invisible="1"/>
|
||||
<field name="needaction_pending" invisible="1"/>
|
||||
<button string="Schedule Other Call"
|
||||
icon="terp-call-start"
|
||||
name="%(phonecall_to_phonecall_act)d"
|
||||
@@ -154,7 +191,7 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Phonecalls Calendar View -->
|
||||
<!-- Phonecalls Calendar View -->
|
||||
|
||||
<record model="ir.ui.view" id="crm_case_phone_calendar_view">
|
||||
<field name="name">CRM - Phone Calls Calendar</field>
|
||||
@@ -169,14 +206,18 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Phonecalls Search View -->
|
||||
|
||||
<!-- Phonecalls Search View -->
|
||||
<record id="view_crm_case_phonecalls_filter" model="ir.ui.view">
|
||||
<field name="name">CRM - Phone Calls Search</field>
|
||||
<field name="model">crm.phonecall</field>
|
||||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Phonecalls">
|
||||
<filter icon="terp-mail-message-new"
|
||||
string="Inbox" help="Unread messages"
|
||||
name="needaction_pending"
|
||||
domain="[('needaction_pending','=',True)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<field name="name" string="Phonecalls"/>
|
||||
<separator orientation="vertical"/>
|
||||
<field name="date"/>
|
||||
|
||||
@@ -183,59 +183,53 @@ class crm_claim(base_stage, osv.osv):
|
||||
}
|
||||
address = self.pool.get('res.partner').browse(cr, uid, part)
|
||||
return {'value': {'email_from': address.email, 'partner_phone': address.phone}}
|
||||
|
||||
def message_new(self, cr, uid, msg, custom_values=None, context=None):
|
||||
"""Automatically called when new email message arrives"""
|
||||
res_id = super(crm_claim,self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
|
||||
subject = msg.get('subject')
|
||||
body = msg.get('body_text')
|
||||
msg_from = msg.get('from')
|
||||
priority = msg.get('priority')
|
||||
vals = {
|
||||
'name': subject,
|
||||
'email_from': msg_from,
|
||||
'email_cc': msg.get('cc'),
|
||||
'description': body,
|
||||
'user_id': False,
|
||||
}
|
||||
if priority:
|
||||
vals['priority'] = priority
|
||||
vals.update(self.message_partner_by_email(cr, uid, msg.get('from', False)))
|
||||
self.write(cr, uid, [res_id], vals, context=context)
|
||||
return res_id
|
||||
|
||||
def message_update(self, cr, uid, ids, msg, vals={}, default_act='pending', context=None):
|
||||
# -------------------------------------------------------
|
||||
# Mail gateway
|
||||
# -------------------------------------------------------
|
||||
|
||||
def message_new(self, cr, uid, msg, custom_values=None, context=None):
|
||||
""" Overrides mail_thread message_new that is called by the mailgateway
|
||||
through message_process.
|
||||
This override updates the document according to the email.
|
||||
"""
|
||||
if custom_values is None: custom_values = {}
|
||||
custom_values.update({
|
||||
'name': msg.get('subject') or _("No Subject"),
|
||||
'description': msg.get('body_text'),
|
||||
'email_from': msg.get('from'),
|
||||
'email_cc': msg.get('cc'),
|
||||
})
|
||||
if msg.get('priority'):
|
||||
custom_values['priority'] = msg.get('priority')
|
||||
custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from'), context=context))
|
||||
return super(crm_claim,self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
|
||||
|
||||
def message_update(self, cr, uid, ids, msg, update_vals=None, context=None):
|
||||
""" Overrides mail_thread message_update that is called by the mailgateway
|
||||
through message_process.
|
||||
This method updates the document according to the email.
|
||||
"""
|
||||
if isinstance(ids, (str, int, long)):
|
||||
ids = [ids]
|
||||
|
||||
res_id = super(crm_claim,self).message_update(cr, uid, ids, msg, context=context)
|
||||
if update_vals is None: update_vals = {}
|
||||
|
||||
if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES):
|
||||
vals['priority'] = msg.get('priority')
|
||||
update_vals['priority'] = msg.get('priority')
|
||||
|
||||
maps = {
|
||||
'cost':'planned_cost',
|
||||
'revenue': 'planned_revenue',
|
||||
'probability':'probability'
|
||||
}
|
||||
vls = {}
|
||||
for line in msg['body_text'].split('\n'):
|
||||
line = line.strip()
|
||||
res = tools.misc.command_re.match(line)
|
||||
if res and maps.get(res.group(1).lower()):
|
||||
key = maps.get(res.group(1).lower())
|
||||
vls[key] = res.group(2).lower()
|
||||
vals.update(vls)
|
||||
update_vals[key] = res.group(2).lower()
|
||||
|
||||
# Unfortunately the API is based on lists
|
||||
# but we want to update the state based on the
|
||||
# previous state, so we have to loop:
|
||||
for case in self.browse(cr, uid, ids, context=context):
|
||||
values = dict(vals)
|
||||
if case.state in CRM_CLAIM_PENDING_STATES:
|
||||
values.update(state=crm.AVAILABLE_STATES[1][0]) #re-open
|
||||
res = self.write(cr, uid, [case.id], values, context=context)
|
||||
return res
|
||||
return super(crm_claim,self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context)
|
||||
|
||||
# ---------------------------------------------------
|
||||
# OpenChatter methods and notifications
|
||||
|
||||
@@ -127,21 +127,26 @@ class crm_fundraising(base_stage, osv.osv):
|
||||
self.create_send_note(cr, uid, [obj_id], context=context)
|
||||
return obj_id
|
||||
|
||||
# -------------------------------------------------------
|
||||
# Mail gateway
|
||||
# -------------------------------------------------------
|
||||
|
||||
def message_new(self, cr, uid, msg, custom_values=None, context=None):
|
||||
"""Automatically called when new email message arrives"""
|
||||
res_id = super(crm_fundraising,self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
|
||||
vals = {
|
||||
'name': msg.get('subject'),
|
||||
""" Overrides mail_thread message_new that is called by the mailgateway
|
||||
through message_process.
|
||||
This override also updates the document according to the email.
|
||||
"""
|
||||
if custom_values is None: custom_values = {}
|
||||
custom_values.update({
|
||||
'name': msg.get('subject') or _("No Subject"),
|
||||
'description': msg.get('body_text'),
|
||||
'email_from': msg.get('from'),
|
||||
'email_cc': msg.get('cc'),
|
||||
'description': msg.get('body_text'),
|
||||
}
|
||||
priority = msg.get('priority')
|
||||
if priority:
|
||||
vals['priority'] = priority
|
||||
vals.update(self.message_partner_by_email(cr, uid, msg.get('from')))
|
||||
self.write(cr, uid, [res_id], vals, context=context)
|
||||
return res_id
|
||||
})
|
||||
if msg.get('priority'):
|
||||
custom_values['priority'] = priority
|
||||
custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from'), context=context))
|
||||
return super(crm_fundraising,self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
|
||||
|
||||
# ---------------------------------------------------
|
||||
# OpenChatter methods and notifications
|
||||
|
||||
@@ -95,55 +95,51 @@ class crm_helpdesk(base_state, osv.osv):
|
||||
self.create_send_note(cr, uid, [obj_id], context=context)
|
||||
return obj_id
|
||||
|
||||
def message_new(self, cr, uid, msg_dict, custom_values=None, context=None):
|
||||
"""Automatically called when new email message arrives"""
|
||||
res_id = super(crm_helpdesk,self).message_new(cr, uid, msg_dict, custom_values=custom_values, context=context)
|
||||
subject = msg_dict.get('subject') or _("No Subject")
|
||||
body = msg_dict.get('body_text')
|
||||
msg_from = msg_dict.get('from')
|
||||
vals = {
|
||||
'name': subject,
|
||||
'email_from': msg_from,
|
||||
'email_cc': msg_dict.get('cc'),
|
||||
'description': body,
|
||||
'user_id': False,
|
||||
}
|
||||
vals.update(self.message_partner_by_email(cr, uid, msg_from))
|
||||
self.write(cr, uid, [res_id], vals, context)
|
||||
return res_id
|
||||
# -------------------------------------------------------
|
||||
# Mail gateway
|
||||
# -------------------------------------------------------
|
||||
|
||||
def message_update(self, cr, uid, ids, msg, vals={}, default_act='pending', context=None):
|
||||
def message_new(self, cr, uid, msg, custom_values=None, context=None):
|
||||
""" Overrides mail_thread message_new that is called by the mailgateway
|
||||
through message_process.
|
||||
This override updates the document according to the email.
|
||||
"""
|
||||
if custom_values is None: custom_values = {}
|
||||
custom_values.update({
|
||||
'name': msg.get('subject') or _("No Subject"),
|
||||
'description': msg.get('body_text'),
|
||||
'email_from': msg.get('from'),
|
||||
'email_cc': msg.get('cc'),
|
||||
'user_id': False,
|
||||
})
|
||||
custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from'), context=context))
|
||||
return super(crm_helpdesk,self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
|
||||
|
||||
def message_update(self, cr, uid, ids, msg, update_vals=None, context=None):
|
||||
""" Overrides mail_thread message_update that is called by the mailgateway
|
||||
through message_process.
|
||||
This method updates the document according to the email.
|
||||
"""
|
||||
if isinstance(ids, (str, int, long)):
|
||||
ids = [ids]
|
||||
|
||||
super(crm_helpdesk,self).message_update(cr, uid, ids, msg, context=context)
|
||||
if update_vals is None: update_vals = {}
|
||||
|
||||
if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES):
|
||||
vals['priority'] = msg.get('priority')
|
||||
update_vals['priority'] = msg.get('priority')
|
||||
|
||||
maps = {
|
||||
'cost':'planned_cost',
|
||||
'revenue': 'planned_revenue',
|
||||
'probability':'probability'
|
||||
}
|
||||
vls = {}
|
||||
for line in msg['body_text'].split('\n'):
|
||||
line = line.strip()
|
||||
res = tools.misc.command_re.match(line)
|
||||
if res and maps.get(res.group(1).lower()):
|
||||
key = maps.get(res.group(1).lower())
|
||||
vls[key] = res.group(2).lower()
|
||||
vals.update(vls)
|
||||
update_vals[key] = res.group(2).lower()
|
||||
|
||||
# Unfortunately the API is based on lists
|
||||
# but we want to update the state based on the
|
||||
# previous state, so we have to loop:
|
||||
for case in self.browse(cr, uid, ids, context=context):
|
||||
values = dict(vals)
|
||||
if case.state in CRM_HELPDESK_STATES:
|
||||
values.update(state=crm.AVAILABLE_STATES[1][0]) #re-open
|
||||
res = self.write(cr, uid, [case.id], values, context=context)
|
||||
return res
|
||||
return super(crm_helpdesk,self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context)
|
||||
|
||||
# ******************************
|
||||
# OpenChatter
|
||||
|
||||
@@ -271,13 +271,6 @@ class event_event(osv.osv):
|
||||
# OpenChatter methods and notifications
|
||||
# ----------------------------------------
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
result = dict.fromkeys(ids, [])
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.state == 'draft' and obj.user_id:
|
||||
result[obj.id] = [obj.user_id.id]
|
||||
return result
|
||||
|
||||
def create_send_note(self, cr, uid, ids, context=None):
|
||||
message = _("Event has been <b>created</b>.")
|
||||
self.message_append_note(cr, uid, ids, body=message, context=context)
|
||||
@@ -451,13 +444,6 @@ class event_registration(osv.osv):
|
||||
# OpenChatter methods and notifications
|
||||
# ----------------------------------------
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
result = dict.fromkeys(ids, [])
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.state == 'draft' and obj.user_id:
|
||||
result[obj.id] = [obj.user_id.id]
|
||||
return result
|
||||
|
||||
def create_send_note(self, cr, uid, ids, context=None):
|
||||
message = _("Registration has been <b>created</b>.")
|
||||
self.message_append_note(cr, uid, ids, body=message, context=context)
|
||||
|
||||
@@ -47,6 +47,43 @@
|
||||
|
||||
<!-- Events Organisation/CONFIGURATION/EVENTS -->
|
||||
|
||||
<!-- Event Read/Unread actions -->
|
||||
<record id="actions_server_event_event_unread" model="ir.actions.server">
|
||||
<field name="name">Mark unread</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_event_event"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_unread(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_event_event_unread" model="ir.values">
|
||||
<field name="name">action_event_event_unread</field>
|
||||
<field name="action_id" ref="actions_server_event_event_unread"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_event_event_unread'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_event_event" />
|
||||
<field name="model">event.event</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record id="actions_server_event_event_read" model="ir.actions.server">
|
||||
<field name="name">Mark read</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_event_event"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_read(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_event_event_read" model="ir.values">
|
||||
<field name="name">action_event_event_read</field>
|
||||
<field name="action_id" ref="actions_server_event_event_read"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_event_event_read'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_event_event" />
|
||||
<field name="model">event.event</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_event_form">
|
||||
<field name="name">Events</field>
|
||||
<field name="model">event.event</field>
|
||||
@@ -144,7 +181,7 @@
|
||||
<field name="model">event.event</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Events" colors="red:(register_min and register_min>register_current) or (register_max and register_max<register_current);grey:state=='cancel'">
|
||||
<tree string="Events" fonts="bold:needaction_pending==True" colors="red:(register_min and register_min>register_current) or (register_max and register_max<register_current);grey:state=='cancel'">
|
||||
<field name="name" string="Name"/>
|
||||
<field name="type"/>
|
||||
<field name="date_begin"/>
|
||||
@@ -155,6 +192,7 @@
|
||||
<field name="main_speaker_id" groups="base.extended"/>
|
||||
<field name="user_id"/>
|
||||
<field name="state"/>
|
||||
<field name="needaction_pending" invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -264,6 +302,11 @@
|
||||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Events">
|
||||
<filter icon="terp-mail-message-new"
|
||||
string="Inbox" help="Unread messages"
|
||||
name="needaction_pending"
|
||||
domain="[('needaction_pending','=',True)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<group>
|
||||
<field name="name" string="Events"/>
|
||||
<separator orientation="vertical"/>
|
||||
@@ -330,12 +373,49 @@
|
||||
|
||||
<!-- EVENTS/REGISTRATIONS/EVENTS -->
|
||||
|
||||
<!-- Registration Read/Unread actions -->
|
||||
<record id="actions_server_event_registration_unread" model="ir.actions.server">
|
||||
<field name="name">Mark unread</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_event_registration"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_unread(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_event_registration_unread" model="ir.values">
|
||||
<field name="name">action_event_registration_unread</field>
|
||||
<field name="action_id" ref="actions_server_event_registration_unread"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_event_registration_unread'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_event_registration" />
|
||||
<field name="model">event.registration</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record id="actions_server_event_registration_read" model="ir.actions.server">
|
||||
<field name="name">Mark read</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_event_registration"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_read(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_event_registration_read" model="ir.values">
|
||||
<field name="name">action_event_registration_read</field>
|
||||
<field name="action_id" ref="actions_server_event_registration_read"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_event_registration_read'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_event_registration" />
|
||||
<field name="model">event.registration</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_event_registration_tree">
|
||||
<field name="name">event.registration.tree</field>
|
||||
<field name="model">event.registration</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Registration" >
|
||||
<tree string="Registration" fonts="bold:needaction_pending==True">
|
||||
<field name="create_date"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="name"/>
|
||||
@@ -345,6 +425,7 @@
|
||||
<field name="user_id"/>
|
||||
<field name="origin"/>
|
||||
<field name="state"/>
|
||||
<field name="needaction_pending" invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -449,6 +530,11 @@
|
||||
<field name="arch" type="xml">
|
||||
<search string="Event Registration">
|
||||
<group>
|
||||
<filter icon="terp-mail-message-new"
|
||||
string="Inbox" help="Unread messages"
|
||||
name="needaction_pending"
|
||||
domain="[('needaction_pending','=',True)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<field name="name" string="Participant"
|
||||
filter_domain="['|','|','|',('name','ilike',self),('partner_id','ilike',self),('email','ilike',self),('origin','ilike',self)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
|
||||
@@ -359,7 +359,7 @@ class hr_holidays(osv.osv):
|
||||
# -----------------------------
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
result = dict.fromkeys(ids, [])
|
||||
result = super(hr_holidays, self).get_needaction_user_ids(cr, uid, ids, context=context)
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.state == 'confirm' and obj.employee_id.parent_id:
|
||||
result[obj.id] = [obj.employee_id.parent_id.user_id.id]
|
||||
@@ -371,14 +371,16 @@ class hr_holidays(osv.osv):
|
||||
hr_manager_group = self.pool.get('res.groups').read(cr, uid, [obj_id], ['users'], context=context)[0]
|
||||
result[obj.id] = hr_manager_group['users']
|
||||
return result
|
||||
|
||||
|
||||
def message_get_subscribers(self, cr, uid, ids, context=None):
|
||||
sub_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context);
|
||||
# add the employee and its manager if specified to the subscribed users
|
||||
""" Override to add employee and its manager. """
|
||||
user_ids = super(hr_holidays, self).message_get_subscribers(cr, uid, ids, context=context)
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.employee_id.parent_id:
|
||||
sub_ids.append(obj.employee_id.parent_id.user_id.id)
|
||||
return self.pool.get('res.users').read(cr, uid, sub_ids, context=context)
|
||||
if obj.user_id and not obj.user_id.id in user_ids:
|
||||
user_ids.append(obj.user_id.id)
|
||||
if obj.employee_id.parent_id and not obj.employee_id.parent_id.user_id.id in user_ids:
|
||||
user_ids.append(obj.employee_id.parent_id.user_id.id)
|
||||
return user_ids
|
||||
|
||||
def create_notificate(self, cr, uid, ids, context=None):
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
|
||||
@@ -1,6 +1,43 @@
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- Hr holidays Read/Unread actions -->
|
||||
<record id="actions_server_holidays_unread" model="ir.actions.server">
|
||||
<field name="name">Mark unread</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_hr_holidays"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_unread(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_holidays_unread" model="ir.values">
|
||||
<field name="name">action_holidays_unread</field>
|
||||
<field name="action_id" ref="actions_server_holidays_unread"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_holidays_unread'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_hr_holidays" />
|
||||
<field name="model">hr.holidays</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record id="actions_server_holidays_read" model="ir.actions.server">
|
||||
<field name="name">Mark read</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_hr_holidays"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_read(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_holidays_read" model="ir.values">
|
||||
<field name="name">action_holidays_read</field>
|
||||
<field name="action_id" ref="actions_server_holidays_read"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_holidays_read'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_hr_holidays" />
|
||||
<field name="model">hr.holidays</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record id="view_hr_holidays_filter" model="ir.ui.view">
|
||||
<field name="name">hr.holidays.filter</field>
|
||||
<field name="model">hr.holidays</field>
|
||||
@@ -10,6 +47,11 @@
|
||||
<group>
|
||||
<field name="date_from"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-mail-message-new"
|
||||
string="Inbox" help="Unread messages"
|
||||
name="needaction_pending"
|
||||
domain="[('needaction_pending','=',True)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter string="My Leaves" icon="terp-personal" name="my_leaves" domain="[('employee_id.user_id','=', uid)]" help="My Leaves"/>
|
||||
<filter string="My Department Leaves" icon="terp-personal+" help="My Department Leaves" domain="[('department_id.manager_id','=',uid)]"/>
|
||||
<filter icon="terp-check" domain="[('state','=','draft')]" string="To Confirm"/>
|
||||
|
||||
@@ -357,54 +357,53 @@ class hr_applicant(base_stage, osv.Model):
|
||||
return value
|
||||
|
||||
def message_new(self, cr, uid, msg, custom_values=None, context=None):
|
||||
"""Automatically called when new email message arrives"""
|
||||
res_id = super(hr_applicant,self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
|
||||
subject = msg.get('subject') or _("No Subject")
|
||||
body = msg.get('body_text')
|
||||
msg_from = msg.get('from')
|
||||
priority = msg.get('priority')
|
||||
vals = {
|
||||
'name': subject,
|
||||
'email_from': msg_from,
|
||||
""" Overrides mail_thread message_new that is called by the mailgateway
|
||||
through message_process.
|
||||
This override updates the document according to the email.
|
||||
"""
|
||||
if custom_values is None: custom_values = {}
|
||||
custom_values.update({
|
||||
'name': msg.get('subject') or _("No Subject"),
|
||||
'description': msg.get('body_text'),
|
||||
'email_from': msg.get('from'),
|
||||
'email_cc': msg.get('cc'),
|
||||
'description': body,
|
||||
'user_id': False,
|
||||
}
|
||||
if priority:
|
||||
vals['priority'] = priority
|
||||
vals.update(self.message_partner_by_email(cr, uid, msg.get('from', False)))
|
||||
self.write(cr, uid, [res_id], vals, context)
|
||||
return res_id
|
||||
})
|
||||
if msg.get('priority'):
|
||||
custom_values['priority'] = msg.get('priority')
|
||||
custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from', False), context=context))
|
||||
return super(hr_applicant,self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
|
||||
|
||||
def message_update(self, cr, uid, ids, msg, vals=None, default_act='pending', context=None):
|
||||
def message_update(self, cr, uid, ids, msg, update_vals=None, context=None):
|
||||
""" Override mail_thread message_update that is called by the mailgateway
|
||||
through message_process.
|
||||
This method updates the document according to the email.
|
||||
"""
|
||||
if isinstance(ids, (str, int, long)):
|
||||
ids = [ids]
|
||||
if vals is None:
|
||||
vals = {}
|
||||
msg_from = msg['from']
|
||||
vals.update({
|
||||
'description': msg['body_text']
|
||||
if update_vals is None: vals = {}
|
||||
|
||||
update_vals.update({
|
||||
'description': msg.get('body'),
|
||||
'email_from': msg.get('from'),
|
||||
'email_cc': msg.get('cc'),
|
||||
})
|
||||
if msg.get('priority', False):
|
||||
vals['priority'] = msg.get('priority')
|
||||
if msg.get('priority'):
|
||||
update_vals['priority'] = msg.get('priority')
|
||||
|
||||
maps = {
|
||||
'cost':'planned_cost',
|
||||
'cost': 'planned_cost',
|
||||
'revenue': 'planned_revenue',
|
||||
'probability':'probability'
|
||||
'probability': 'probability',
|
||||
}
|
||||
vls = { }
|
||||
for line in msg['body_text'].split('\n'):
|
||||
for line in msg.get('body_text', '').split('\n'):
|
||||
line = line.strip()
|
||||
res = tools.misc.command_re.match(line)
|
||||
if res and maps.get(res.group(1).lower(), False):
|
||||
key = maps.get(res.group(1).lower())
|
||||
vls[key] = res.group(2).lower()
|
||||
update_vals[key] = res.group(2).lower()
|
||||
|
||||
vals.update(vls)
|
||||
res = self.write(cr, uid, ids, vals, context=context)
|
||||
self.message_append_dict(cr, uid, ids, msg, context=context)
|
||||
return res
|
||||
return super(hr_applicant, self).message_update(cr, uids, ids, update_vals=update_vals, context=context)
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
obj_id = super(hr_applicant, self).create(cr, uid, vals, context=context)
|
||||
@@ -493,20 +492,14 @@ class hr_applicant(base_stage, osv.Model):
|
||||
# -------------------------------------------------------
|
||||
# OpenChatter methods and notifications
|
||||
# -------------------------------------------------------
|
||||
|
||||
def message_get_subscribers(self, cr, uid, ids, context=None):
|
||||
sub_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context);
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.user_id:
|
||||
sub_ids.append(obj.user_id.id)
|
||||
return self.pool.get('res.users').read(cr, uid, sub_ids, context=context)
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
result = dict.fromkeys(ids, [])
|
||||
def message_get_subscribers(self, cr, uid, ids, context=None):
|
||||
""" Override to add responsible user. """
|
||||
user_ids = super(hr_applicant, self).message_get_subscribers(cr, uid, ids, context=context)
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.state == 'draft' and obj.user_id:
|
||||
result[obj.id] = [obj.user_id.id]
|
||||
return result
|
||||
if obj.user_id and not obj.user_id.id in user_ids:
|
||||
user_ids.append(obj.user_id.id)
|
||||
return user_ids
|
||||
|
||||
def stage_set_send_note(self, cr, uid, ids, stage_id, context=None):
|
||||
""" Override of the (void) default notification method. """
|
||||
|
||||
@@ -45,6 +45,43 @@
|
||||
# Jobs
|
||||
# ------------------------------------------------------
|
||||
|
||||
<!-- Applicant Read/Unread actions -->
|
||||
<record id="actions_server_applicant_unread" model="ir.actions.server">
|
||||
<field name="name">Mark unread</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_hr_applicant"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_unread(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_applicant_unread" model="ir.values">
|
||||
<field name="name">action_project_unread</field>
|
||||
<field name="action_id" ref="actions_server_applicant_unread"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_applicant_unread'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_hr_applicant" />
|
||||
<field name="model">hr.applicant</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record id="actions_server_applicant_read" model="ir.actions.server">
|
||||
<field name="name">Mark read</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_hr_applicant"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_read(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_applicant_read" model="ir.values">
|
||||
<field name="name">action_project_read</field>
|
||||
<field name="action_id" ref="actions_server_applicant_read"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_applicant_read'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_hr_applicant" />
|
||||
<field name="model">hr.applicant</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="crm_case_tree_view_job">
|
||||
<field name="name">Applicants</field>
|
||||
<field name="model">hr.applicant</field>
|
||||
@@ -192,6 +229,12 @@
|
||||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Jobs">
|
||||
<filter icon="terp-mail-message-new"
|
||||
string="Inbox" help="Unread messages"
|
||||
name="needaction_pending"
|
||||
domain="[('needaction_pending','=',True)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-document-new" string="New" name="filter_new"/>
|
||||
<field name="partner_name" filter_domain="['|','|',('name','ilike',self),('partner_name','ilike',self),('email_from','ilike',self)]" string="Subject / Applicant"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-document-new" string="New"
|
||||
|
||||
@@ -23,6 +23,7 @@ import mail_message
|
||||
import mail_thread
|
||||
import mail_group
|
||||
import mail_subscription
|
||||
import ir_needaction
|
||||
import res_users
|
||||
import res_partner
|
||||
import report
|
||||
|
||||
@@ -10,3 +10,4 @@ Mail Module documentation topics
|
||||
mail_openchatter_howto
|
||||
mail_needaction_howto
|
||||
mail_partner
|
||||
mail_state
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
.. _mail_state:
|
||||
|
||||
message_state
|
||||
=============
|
||||
|
||||
``message_state`` is a boolean field that states whether the document
|
||||
has unread messages. In previous versions, some documents were going
|
||||
back to ``pending`` state when receiving an email through the mail
|
||||
gateway. Now the state related to messages differs from the state or
|
||||
stage of the document itself.
|
||||
|
||||
message_state and need action mechanism
|
||||
+++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
The ``mail`` module introduces a default behavior for the need_action
|
||||
mechanism [REF].
|
||||
|
||||
::
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
""" Returns the user_ids that have to perform an action
|
||||
:return: dict { record_id: [user_ids], }
|
||||
"""
|
||||
result = super(ir_needaction_mixin, self).get_needaction_user_ids(cr, uid, ids, context=context)
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.message_state == False and obj.user_id:
|
||||
result[obj.id].append(obj.user_id.id)
|
||||
return result
|
||||
@@ -0,0 +1,46 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2012-today OpenERP SA (<http://www.openerp.com>)
|
||||
#
|
||||
# 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
|
||||
|
||||
class ir_needaction_mixin(osv.Model):
|
||||
""" Update of ir.needaction_mixin class
|
||||
- override the get_needaction_user_ids method to define the default
|
||||
mail gateway need_action: when the object is unread, the object
|
||||
responsible has an action to perform.
|
||||
"""
|
||||
_name = 'ir.needaction_mixin'
|
||||
_inherit = ['ir.needaction_mixin']
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
""" Returns the user_ids that have to perform an action. It the
|
||||
document mail state is unread (False), return object.user_id.id
|
||||
as need_action uid.
|
||||
:return: dict { record_id: [user_ids], }
|
||||
"""
|
||||
result = super(ir_needaction_mixin, self).get_needaction_user_ids(cr, uid, ids, context=context)
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.message_state == False and obj.user_id:
|
||||
result[obj.id].append(obj.user_id.id)
|
||||
return result
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -87,7 +87,7 @@ class mail_group(osv.osv):
|
||||
result = dict.fromkeys(ids)
|
||||
for id in ids:
|
||||
result[id] = {}
|
||||
result[id]['member_ids'] = self.message_get_subscribers_ids(cr, uid, [id], context=context)
|
||||
result[id]['member_ids'] = self.message_get_subscribers(cr, uid, [id], context=context)
|
||||
result[id]['member_count'] = len(result[id]['member_ids'])
|
||||
result[id]['is_subscriber'] = uid in result[id]['member_ids']
|
||||
return result
|
||||
|
||||
+136
-45
@@ -34,7 +34,7 @@ import xmlrpclib
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class mail_thread(osv.osv):
|
||||
class mail_thread(osv.Model):
|
||||
'''Mixin model, meant to be inherited by any model that needs to
|
||||
act as a discussion topic on which messages can be attached.
|
||||
Public methods are prefixed with ``message_`` in order to avoid
|
||||
@@ -62,10 +62,15 @@ class mail_thread(osv.osv):
|
||||
_name = 'mail.thread'
|
||||
_description = 'Email Thread'
|
||||
|
||||
def _get_message_ids(self, cr, uid, ids, name, arg, context=None):
|
||||
def _get_message_ids(self, cr, uid, ids, name, args, context=None):
|
||||
res = {}
|
||||
for id in ids:
|
||||
res[id] = self.message_load_ids(cr, uid, [id], context=context)
|
||||
message_ids = self.message_load_ids(cr, uid, [id], context=context)
|
||||
subscriber_ids = self.message_get_subscribers(cr, uid, [id], context=context)
|
||||
res[id] = {
|
||||
'message_ids': message_ids,
|
||||
'message_summary': "<span>Msg: %d</span> . <span>Fol: %d</span>" % (len(message_ids), len(subscriber_ids)),
|
||||
}
|
||||
return res
|
||||
|
||||
def _search_message_ids(self, cr, uid, obj, name, args, context=None):
|
||||
@@ -73,10 +78,23 @@ class mail_thread(osv.osv):
|
||||
msg_ids = msg_obj.search(cr, uid, ['&', ('res_id', 'in', args[0][2]), ('model', '=', self._name)], context=context)
|
||||
return [('id', 'in', msg_ids)]
|
||||
|
||||
# OpenChatter: message_ids is a dummy field that should not be used
|
||||
_columns = {
|
||||
'message_ids': fields.function(_get_message_ids, method=True, fnct_search=_search_message_ids,
|
||||
type='one2many', obj='mail.message', string='Temp messages', _fields_id = 'res_id'),
|
||||
'message_ids': fields.function(_get_message_ids, method=True,
|
||||
fnct_search=_search_message_ids,
|
||||
type='one2many', obj='mail.message', _fields_id = 'res_id',
|
||||
string='Temp messages', multi="_get_message_ids",
|
||||
help="Functional field holding messages related to the current document."),
|
||||
'message_state': fields.boolean('Read',
|
||||
help="When checked, new messages require your attention."),
|
||||
'message_summary': fields.function(_get_message_ids, method=True,
|
||||
type='text', string='Summary', multi="_get_message_ids",
|
||||
help="Holds the Chatter summary (number of messages, ...). "\
|
||||
"This summary is directly in html format in order to "\
|
||||
"be inserted in kanban views."),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'message_state': True,
|
||||
}
|
||||
|
||||
#------------------------------------------------------
|
||||
@@ -84,10 +102,11 @@ class mail_thread(osv.osv):
|
||||
#------------------------------------------------------
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
"""Automatically subscribe the creator"""
|
||||
thread_id = super(mail_thread, self).create(cr, uid, vals, context=context);
|
||||
self.message_subscribe(cr, uid, [thread_id], [uid], context=context)
|
||||
return thread_id;
|
||||
"""Automatically subscribe the creator """
|
||||
thread_id = super(mail_thread, self).create(cr, uid, vals, context=context)
|
||||
if thread_id:
|
||||
self.message_subscribe(cr, uid, [thread_id], [uid], context=context)
|
||||
return thread_id
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
"""Automatically subscribe the writer"""
|
||||
@@ -106,8 +125,6 @@ class mail_thread(osv.osv):
|
||||
a foreign key with a 'cascade' ondelete attribute.
|
||||
Notifications will be deleted with messages
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
subscr_obj = self.pool.get('mail.subscription')
|
||||
msg_obj = self.pool.get('mail.message')
|
||||
# delete subscriptions
|
||||
@@ -120,17 +137,18 @@ class mail_thread(osv.osv):
|
||||
return super(mail_thread, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
#------------------------------------------------------
|
||||
# Generic message api
|
||||
# mail.message wrappers and tools
|
||||
#------------------------------------------------------
|
||||
|
||||
def message_create(self, cr, uid, thread_id, vals, context=None):
|
||||
"""OpenSocial: wrapper of mail.message create method
|
||||
""" OpenChatter: wrapper of mail.message create method
|
||||
- creates the mail.message
|
||||
- automatically subscribe the message writer
|
||||
- push the message to subscribed users
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
message_obj = self.pool.get('mail.message')
|
||||
subscription_obj = self.pool.get('mail.subscription')
|
||||
notification_obj = self.pool.get('mail.notification')
|
||||
@@ -167,7 +185,10 @@ class mail_thread(osv.osv):
|
||||
|
||||
# create message
|
||||
msg_id = message_obj.create(cr, uid, vals, context=context)
|
||||
|
||||
|
||||
# Set as unread if writer is not the document responsible
|
||||
self.message_create_set_unread(cr, uid, [thread_id], context=context)
|
||||
|
||||
# special: if install mode, do not push demo data
|
||||
if context.get('install_mode', False):
|
||||
return True
|
||||
@@ -186,7 +207,7 @@ class mail_thread(osv.osv):
|
||||
body = new_msg_vals.get('body_html', '') if new_msg_vals.get('subtype', 'plain') == 'html' else new_msg_vals.get('body_text', '')
|
||||
for thread_id in thread_ids:
|
||||
# add subscribers
|
||||
notif_user_ids += [user['id'] for user in self.message_get_subscribers(cr, uid, [thread_id], context=context)]
|
||||
notif_user_ids += self.message_get_subscribers(cr, uid, [thread_id], context=context)
|
||||
# add users requested via parsing message (@login)
|
||||
notif_user_ids += self.message_parse_users(cr, uid, [thread_id], body, context=context)
|
||||
# add users requested to perform an action (need_action mechanism)
|
||||
@@ -214,6 +235,10 @@ class mail_thread(osv.osv):
|
||||
user_ids = self.pool.get('res.users').search(cr, uid, [('login', 'in', login_lst)], context=context)
|
||||
return user_ids
|
||||
|
||||
#------------------------------------------------------
|
||||
# Generic message api
|
||||
#------------------------------------------------------
|
||||
|
||||
def message_capable_models(self, cr, uid, context=None):
|
||||
ret_dict = {}
|
||||
for model_name in self.pool.obj_list():
|
||||
@@ -376,7 +401,6 @@ class mail_thread(osv.osv):
|
||||
original = msg_dict.get('original'),
|
||||
context = context)
|
||||
|
||||
# Message loading
|
||||
def _message_add_ancestor_ids(self, cr, uid, ids, child_ids, root_ids, context=None):
|
||||
""" Given message child_ids
|
||||
Find their ancestors until root ids"""
|
||||
@@ -420,6 +444,9 @@ class mail_thread(osv.osv):
|
||||
msg_ids = self.message_load_ids(cr, uid, ids, limit, offset, domain, ascent, root_ids, context=context)
|
||||
msgs = self.pool.get('mail.message').read(cr, uid, msg_ids, [], context=context)
|
||||
|
||||
# Set as read
|
||||
self.message_check_and_set_read(cr, uid, ids, context=context)
|
||||
|
||||
""" Retrieve all attachments names """
|
||||
map_id_to_name = {}
|
||||
|
||||
@@ -474,7 +501,7 @@ class mail_thread(osv.osv):
|
||||
return msgs
|
||||
|
||||
#------------------------------------------------------
|
||||
# Email specific
|
||||
# Mail gateway
|
||||
#------------------------------------------------------
|
||||
# message_process will call either message_new or message_update.
|
||||
|
||||
@@ -549,7 +576,7 @@ class mail_thread(osv.osv):
|
||||
if res_id:
|
||||
res_id = res_id.group(1)
|
||||
if res_id:
|
||||
res_id = int(res_id)
|
||||
res_id = res_id
|
||||
if model_pool.exists(cr, uid, res_id):
|
||||
if hasattr(model_pool, 'message_update'):
|
||||
model_pool.message_update(cr, uid, [res_id], msg, {}, context=context)
|
||||
@@ -558,7 +585,7 @@ class mail_thread(osv.osv):
|
||||
res_id = False
|
||||
if not res_id:
|
||||
res_id = create_record(msg)
|
||||
#To forward the email to other followers
|
||||
# To forward the email to other followers
|
||||
self.message_forward(cr, uid, model, [res_id], msg_txt, context=context)
|
||||
return res_id
|
||||
|
||||
@@ -594,29 +621,34 @@ class mail_thread(osv.osv):
|
||||
fields = model_pool.fields_get(cr, uid, context=context)
|
||||
data = model_pool.default_get(cr, uid, fields, context=context)
|
||||
if 'name' in fields and not data.get('name'):
|
||||
data['name'] = msg_dict.get('from','')
|
||||
data['name'] = msg_dict.get('from', '')
|
||||
if custom_values and isinstance(custom_values, dict):
|
||||
data.update(custom_values)
|
||||
res_id = model_pool.create(cr, uid, data, context=context)
|
||||
self.message_append_dict(cr, uid, [res_id], msg_dict, context=context)
|
||||
return res_id
|
||||
|
||||
def message_update(self, cr, uid, ids, msg_dict, vals={}, default_act=None, context=None):
|
||||
"""Called by ``message_process`` when a new message is received
|
||||
for an existing thread. The default behavior is to create a
|
||||
new mail.message in the given thread (by calling
|
||||
``message_append_dict``)
|
||||
Additional behavior may be implemented by overriding this
|
||||
method.
|
||||
def message_update(self, cr, uid, ids, msg_dict, update_vals=None, context=None):
|
||||
""" Called by ``message_process`` when a new message is received
|
||||
for an existing thread. The default behavior is to create a
|
||||
new mail.message in the given thread (by calling
|
||||
``message_append_dict``)
|
||||
Additional behavior may be implemented by overriding this
|
||||
method.
|
||||
|
||||
:param dict msg_dict: a map containing the email details and
|
||||
:param dict msg_dict: a map containing the email details and
|
||||
attachments. See ``message_process`` and
|
||||
``mail.message.parse()`` for details.
|
||||
:param dict context: if a ``thread_model`` value is present
|
||||
:param dict vals: a dict containing values to update records
|
||||
given their ids; if the dict is None or is
|
||||
void, no write operation is performed.
|
||||
:param dict context: if a ``thread_model`` value is present
|
||||
in the context, its value will be used
|
||||
to determine the model of the thread to
|
||||
update (instead of the current model).
|
||||
"""
|
||||
if update_vals:
|
||||
self.write(cr, uid, ids, update_vals, context=context)
|
||||
return self.message_append_dict(cr, uid, ids, msg_dict, context=context)
|
||||
|
||||
def message_thread_followers(self, cr, uid, ids, context=None):
|
||||
@@ -760,51 +792,77 @@ class mail_thread(osv.osv):
|
||||
# Subscription mechanism
|
||||
#------------------------------------------------------
|
||||
|
||||
def message_get_subscribers_ids(self, cr, uid, ids, context=None):
|
||||
def message_get_subscribers(self, cr, uid, ids, context=None):
|
||||
""" Returns the current document followers. Basically this method
|
||||
checks in mail.subscription for entries with matching res_model,
|
||||
res_id.
|
||||
|
||||
:param get_ids: if set to True, return the ids of users; if set
|
||||
to False, returns the result of a read in res.users
|
||||
"""
|
||||
subscr_obj = self.pool.get('mail.subscription')
|
||||
subscr_ids = subscr_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', 'in', ids)], context=context)
|
||||
subs = subscr_obj.read(cr, uid, subscr_ids, context=context)
|
||||
return [sub['user_id'][0] for sub in subs]
|
||||
return [sub['user_id'][0] for sub in subscr_obj.read(cr, uid, subscr_ids, ['user_id'], context=context)]
|
||||
|
||||
def message_get_subscribers(self, cr, uid, ids, context=None):
|
||||
user_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context)
|
||||
users = self.pool.get('res.users').read(cr, uid, user_ids, fields=['id', 'name', 'avatar'], context=context)
|
||||
return users
|
||||
def message_read_subscribers(self, cr, uid, ids, fields=['id', 'name', 'avatar'], context=None):
|
||||
""" Returns the current document followers as a read result. Used
|
||||
mainly for Chatter having only one method to call to have
|
||||
details about users.
|
||||
"""
|
||||
user_ids = self.message_get_subscribers(cr, uid, ids, context=context)
|
||||
return self.pool.get('res.users').read(cr, uid, user_ids, fields=fields, context=context)
|
||||
|
||||
def message_is_subscriber(self, cr, uid, ids, user_id = None, context=None):
|
||||
users = self.message_get_subscribers(cr, uid, ids, context=context)
|
||||
""" Check if uid or user_id (if set) is a subscriber to the current
|
||||
document.
|
||||
|
||||
:param user_id: if set, check is done on user_id; if not set
|
||||
check is done on uid
|
||||
"""
|
||||
sub_user_id = uid if user_id is None else user_id
|
||||
if sub_user_id in [user['id'] for user in users]:
|
||||
if sub_user_id in self.message_get_subscribers(cr, uid, ids, context=context):
|
||||
return True
|
||||
return False
|
||||
|
||||
def message_subscribe(self, cr, uid, ids, user_ids = None, context=None):
|
||||
""" Subscribe the user (or user_ids) to the current document.
|
||||
|
||||
:param user_ids: a list of user_ids; if not set, subscribe
|
||||
uid instead
|
||||
"""
|
||||
subscription_obj = self.pool.get('mail.subscription')
|
||||
to_subscribe_uids = [uid] if user_ids is None else user_ids
|
||||
create_ids = []
|
||||
for id in ids:
|
||||
already_subscribed_user_ids = self.message_get_subscribers(cr, uid, [id], context=context)
|
||||
for user_id in to_subscribe_uids:
|
||||
if self.message_is_subscriber(cr, uid, [id], user_id=user_id, context=context): continue
|
||||
if user_id in already_subscribed_user_ids: continue
|
||||
create_ids.append(subscription_obj.create(cr, uid, {'res_model': self._name, 'res_id': id, 'user_id': user_id}, context=context))
|
||||
return create_ids
|
||||
|
||||
def message_unsubscribe(self, cr, uid, ids, user_ids = None, context=None):
|
||||
if not user_ids and not uid in self.message_get_subscribers_ids(cr, uid, ids, context=context):
|
||||
""" Unsubscribe the user (or user_ids) from the current document.
|
||||
|
||||
:param user_ids: a list of user_ids; if not set, subscribe
|
||||
uid instead
|
||||
"""
|
||||
# Trying to unsubscribe somebody not in subscribers: returns False
|
||||
# if special management is needed; allows to know that an automatically
|
||||
# subscribed user tries to unsubscribe and allows to warn him
|
||||
mail_thread_model = self.pool.get('mail.thread')
|
||||
if not user_ids and not uid in mail_thread_model.message_get_subscribers(cr, uid, ids, context=context):
|
||||
return False
|
||||
subscription_obj = self.pool.get('mail.subscription')
|
||||
to_unsubscribe_uids = [uid] if user_ids is None else user_ids
|
||||
to_delete_sub_ids = subscription_obj.search(cr, uid,
|
||||
['&', '&', ('res_model', '=', self._name), ('res_id', 'in', ids), ('user_id', 'in', to_unsubscribe_uids)], context=context)
|
||||
subscription_obj.unlink(cr, uid, to_delete_sub_ids, context=context)
|
||||
return True
|
||||
return subscription_obj.unlink(cr, uid, to_delete_sub_ids, context=context)
|
||||
|
||||
#------------------------------------------------------
|
||||
# Notification API
|
||||
#------------------------------------------------------
|
||||
|
||||
def message_remove_pushed_notifications(self, cr, uid, ids, msg_ids, remove_childs=True, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
notif_obj = self.pool.get('mail.notification')
|
||||
msg_obj = self.pool.get('mail.message')
|
||||
if remove_childs:
|
||||
@@ -814,4 +872,37 @@ class mail_thread(osv.osv):
|
||||
to_del_notif_ids = notif_obj.search(cr, uid, ['&', ('user_id', '=', uid), ('message_id', 'in', notif_msg_ids)], context=context)
|
||||
return notif_obj.unlink(cr, uid, to_del_notif_ids, context=context)
|
||||
|
||||
#------------------------------------------------------
|
||||
# Thread_state
|
||||
#------------------------------------------------------
|
||||
|
||||
def message_create_set_unread(self, cr, uid, ids, context=None):
|
||||
""" When creating a new message, set as unread if uid is not the
|
||||
object responsible. """
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.message_state and hasattr(obj, 'user_id') and (not obj.user_id or obj.user_id.id != uid):
|
||||
self.message_mark_as_unread(cr, uid, [obj.id], context=context)
|
||||
|
||||
def message_check_and_set_unread(self, cr, uid, ids, context=None):
|
||||
""" Set unread if uid is the object responsible or if the object has
|
||||
no responsible. """
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.message_state and hasattr(obj, 'user_id') and (not obj.user_id or obj.user_id.id == uid):
|
||||
self.message_mark_as_unread(cr, uid, [obj.id], context=context)
|
||||
|
||||
def message_mark_as_unread(self, cr, uid, ids, context=None):
|
||||
""" Set as unread. """
|
||||
return self.write(cr, uid, ids, {'message_state': False}, context=context)
|
||||
|
||||
def message_check_and_set_read(self, cr, uid, ids, context=None):
|
||||
""" Set read if uid is the object responsible. """
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if not obj.message_state and hasattr(obj, 'user_id') and obj.user_id and obj.user_id.id == uid:
|
||||
self.message_mark_as_read(cr, uid, [obj.id], context=context)
|
||||
|
||||
def message_mark_as_read(self, cr, uid, ids, context=None):
|
||||
""" Set as read. """
|
||||
return self.write(cr, uid, ids, {'message_state': True}, context=context)
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -554,7 +554,7 @@ openerp.mail = function(session) {
|
||||
},
|
||||
|
||||
fetch_subscribers: function () {
|
||||
return this.ds.call('message_get_subscribers', [[this.view.datarecord.id]]).then(this.proxy('display_subscribers'));
|
||||
return this.ds.call('message_read_subscribers', [[this.view.datarecord.id]]).then(this.proxy('display_subscribers'));
|
||||
},
|
||||
|
||||
display_subscribers: function (records) {
|
||||
|
||||
+5
-11
@@ -1033,20 +1033,14 @@ class mrp_production(osv.osv):
|
||||
# ---------------------------------------------------
|
||||
# OpenChatter methods and notifications
|
||||
# ---------------------------------------------------
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
result = dict.fromkeys(ids, [])
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.state == 'draft' and obj.user_id:
|
||||
result[obj.id] = [obj.user_id.id]
|
||||
return result
|
||||
|
||||
def message_get_subscribers(self, cr, uid, ids, context=None):
|
||||
sub_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context);
|
||||
""" Override to add responsible user. """
|
||||
user_ids = super(mrp_production, self).message_get_subscribers(cr, uid, ids, context=context)
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.user_id:
|
||||
sub_ids.append(obj.user_id.id)
|
||||
return self.pool.get('res.users').read(cr, uid, sub_ids, context=context)
|
||||
if obj.user_id and not obj.user_id.id in user_ids:
|
||||
user_ids.append(obj.user_id.id)
|
||||
return user_ids
|
||||
|
||||
def create_send_note(self, cr, uid, ids, context=None):
|
||||
self.message_append_note(cr, uid, ids, body=_("Manufacturing order has been <b>created</b>."), context=context)
|
||||
|
||||
+19
-20
@@ -480,21 +480,14 @@ def Project():
|
||||
# ------------------------------------------------
|
||||
# OpenChatter methods and notifications
|
||||
# ------------------------------------------------
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
result = dict.fromkeys(ids)
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
result[obj.id] = []
|
||||
if obj.state == 'draft' and obj.user_id:
|
||||
result[obj.id] = [obj.user_id.id]
|
||||
return result
|
||||
|
||||
def message_get_subscribers(self, cr, uid, ids, context=None):
|
||||
sub_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context);
|
||||
""" Override to add responsible user. """
|
||||
user_ids = super(project, self).message_get_subscribers(cr, uid, ids, context=context)
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.user_id:
|
||||
sub_ids.append(obj.user_id.id)
|
||||
return self.pool.get('res.users').read(cr, uid, sub_ids, context=context)
|
||||
if obj.user_id and not obj.user_id.id in user_ids:
|
||||
user_ids.append(obj.user_id.id)
|
||||
return user_ids
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
obj_id = super(project, self).create(cr, uid, vals, context=context)
|
||||
@@ -1145,20 +1138,26 @@ class task(base_stage, osv.osv):
|
||||
return 'Task'
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
result = dict.fromkeys(ids, [])
|
||||
""" Returns the user_ids that have to perform an action.
|
||||
Add to the previous results given by super the document responsible
|
||||
when in draft mode.
|
||||
:return: dict { record_id: [user_ids], }
|
||||
"""
|
||||
result = super(task, self).get_needaction_user_ids(cr, uid, ids, context=context)
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.state == 'draft' and obj.user_id:
|
||||
result[obj.id] = [obj.user_id.id]
|
||||
result[obj.id].append(obj.user_id.id)
|
||||
return result
|
||||
|
||||
def message_get_subscribers(self, cr, uid, ids, context=None):
|
||||
sub_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context);
|
||||
""" Override to add responsible user and project manager. """
|
||||
user_ids = super(task, self).message_get_subscribers(cr, uid, ids, context=context)
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.user_id:
|
||||
sub_ids.append(obj.user_id.id)
|
||||
if obj.manager_id:
|
||||
sub_ids.append(obj.manager_id.id)
|
||||
return self.pool.get('res.users').read(cr, uid, sub_ids, context=context)
|
||||
if obj.user_id and not obj.user_id.id in user_ids:
|
||||
user_ids.append(obj.user_id.id)
|
||||
if obj.manager_id and not obj.manager_id.id in user_ids:
|
||||
user_ids.append(obj.manager_id.id)
|
||||
return user_ids
|
||||
|
||||
def stage_set_send_note(self, cr, uid, ids, stage_id, context=None):
|
||||
""" Override of the (void) default notification method. """
|
||||
|
||||
@@ -21,6 +21,43 @@
|
||||
view_mode="kanban,tree,form,calendar,graph"
|
||||
view_type="form"/>
|
||||
|
||||
<!-- Project Read/Unread actions -->
|
||||
<record id="actions_server_project_unread" model="ir.actions.server">
|
||||
<field name="name">Mark unread</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_project_project"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_unread(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_project_unread" model="ir.values">
|
||||
<field name="name">action_project_unread</field>
|
||||
<field name="action_id" ref="actions_server_project_unread"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_project_unread'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_project_project" />
|
||||
<field name="model">project.project</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record id="actions_server_project_read" model="ir.actions.server">
|
||||
<field name="name">Mark read</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_project_project"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_read(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_project_read" model="ir.values">
|
||||
<field name="name">action_project_read</field>
|
||||
<field name="action_id" ref="actions_server_project_read"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_project_read'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_project_project" />
|
||||
<field name="model">project.project</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<!-- Project -->
|
||||
<record id="edit_project" model="ir.ui.view">
|
||||
<field name="name">project.project.form</field>
|
||||
@@ -126,6 +163,11 @@
|
||||
<group>
|
||||
<field name="complete_name" string="Project Name"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-mail-message-new"
|
||||
string="Inbox" help="Unread messages"
|
||||
name="needaction_pending"
|
||||
domain="[('needaction_pending','=',True)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-check" string="Open" name="Current" domain="[('state', '=','open')]" help="Open Projects"/>
|
||||
<filter icon="gtk-media-pause" string="Pending" name="Pending" domain="[('state', '=','pending')]" help="Pending Projects"/>
|
||||
<filter icon="gtk-media-pause" string="Template" name="Template" domain="[('state', '=','template')]" help="Templates of Projects"/>
|
||||
@@ -308,6 +350,44 @@
|
||||
</record>
|
||||
|
||||
<!-- Task -->
|
||||
|
||||
<!-- Task Read/Unread actions -->
|
||||
<record id="actions_server_project_task_unread" model="ir.actions.server">
|
||||
<field name="name">Mark unread</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_project_task"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_unread(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_project_task_unread" model="ir.values">
|
||||
<field name="name">action_project_task_unread</field>
|
||||
<field name="action_id" ref="actions_server_project_task_unread"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_project_task_unread'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_project_task" />
|
||||
<field name="model">project.task</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record id="actions_server_project_task_read" model="ir.actions.server">
|
||||
<field name="name">Mark read</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_project_task"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_read(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_project_task_read" model="ir.values">
|
||||
<field name="name">action_project_task_read</field>
|
||||
<field name="action_id" ref="actions_server_project_task_read"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_project_task_read'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_project_task" />
|
||||
<field name="model">project.task</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record id="view_task_form2" model="ir.ui.view">
|
||||
<field name="name">project.task.form</field>
|
||||
<field name="model">project.task</field>
|
||||
@@ -440,6 +520,8 @@
|
||||
<field name="kanban_state"/>
|
||||
<field name="remaining_hours" sum="Remaining Time" groups="project.group_time_work_estimation_tasks"/>
|
||||
<field name="date_deadline"/>
|
||||
<field name="message_summary"/>
|
||||
<field name="needaction_pending"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click">
|
||||
@@ -568,6 +650,11 @@
|
||||
<group>
|
||||
<field name="name" string="Tasks"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-mail-message-new"
|
||||
string="Inbox" help="Unread messages"
|
||||
name="needaction_pending"
|
||||
domain="[('needaction_pending','=',True)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter name="draft" string="New" domain="[('state','=','draft')]" help="New Tasks" icon="terp-check"/>
|
||||
<filter name="open" string="In Progress" domain="[('state','=','open')]" help="In Progress Tasks" icon="terp-camera_test"/>
|
||||
<filter string="Pending" domain="[('state','=','pending')]" context="{'show_delegated':False}" help="Pending Tasks" icon="terp-gtk-media-pause"/>
|
||||
|
||||
@@ -467,87 +467,68 @@ class project_issue(base_stage, osv.osv):
|
||||
# -------------------------------------------------------
|
||||
|
||||
def message_new(self, cr, uid, msg, custom_values=None, context=None):
|
||||
"""Automatically called when new email message arrives"""
|
||||
if context is None:
|
||||
context = {}
|
||||
subject = msg.get('subject') or _('No Title')
|
||||
body = msg.get('body_text')
|
||||
msg_from = msg.get('from')
|
||||
priority = msg.get('priority')
|
||||
vals = {
|
||||
'name': subject,
|
||||
'email_from': msg_from,
|
||||
""" Overrides mail_thread message_new that is called by the mailgateway
|
||||
through message_process.
|
||||
This override updates the document according to the email.
|
||||
"""
|
||||
if custom_values is None: custom_values = {}
|
||||
if context is None: context = {}
|
||||
context['state_to'] = 'draft'
|
||||
|
||||
custom_values.update({
|
||||
'name': msg.get('subject') or _("No Subject"),
|
||||
'description': msg.get('body_text'),
|
||||
'email_from': msg.get('from'),
|
||||
'email_cc': msg.get('cc'),
|
||||
'description': body,
|
||||
'user_id': False,
|
||||
}
|
||||
if priority:
|
||||
vals['priority'] = priority
|
||||
vals.update(self.message_partner_by_email(cr, uid, msg_from))
|
||||
context.update({'state_to' : 'draft'})
|
||||
})
|
||||
if msg.get('priority'):
|
||||
custom_values['priority'] = msg.get('priority')
|
||||
custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from'), context=context))
|
||||
|
||||
if custom_values and isinstance(custom_values, dict):
|
||||
vals.update(custom_values)
|
||||
|
||||
res_id = self.create(cr, uid, vals, context)
|
||||
self.message_append_dict(cr, uid, [res_id], msg, context=context)
|
||||
res_id = super(project_issue, self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
|
||||
self.convert_to_bug(cr, uid, [res_id], context=context)
|
||||
return res_id
|
||||
|
||||
def message_update(self, cr, uid, ids, msg, vals=None, default_act='pending', context=None):
|
||||
if vals is None:
|
||||
vals = {}
|
||||
|
||||
def message_update(self, cr, uid, ids, msg, update_vals=None, context=None):
|
||||
""" Overrides mail_thread message_update that is called by the mailgateway
|
||||
through message_process.
|
||||
This method updates the document according to the email.
|
||||
"""
|
||||
if isinstance(ids, (str, int, long)):
|
||||
ids = [ids]
|
||||
if update_vals is None: update_vals = {}
|
||||
|
||||
vals.update({
|
||||
'description': msg['body_text']
|
||||
})
|
||||
if msg.get('priority', False):
|
||||
vals['priority'] = msg.get('priority')
|
||||
|
||||
# Update doc values according to the message
|
||||
update_vals['description'] = msg.get('body_text', '')
|
||||
if msg.get('priority'):
|
||||
update_vals['priority'] = msg.get('priority')
|
||||
# Parse 'body_text' to find values to update
|
||||
maps = {
|
||||
'cost': 'planned_cost',
|
||||
'revenue': 'planned_revenue',
|
||||
'probability': 'probability'
|
||||
'probability': 'probability',
|
||||
}
|
||||
|
||||
# Reassign the 'open' state to the case if this one is in pending or done
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
if record.state in ('pending', 'done'):
|
||||
self.case_set(cr, uid, ids, 'open', {}, context=context)
|
||||
|
||||
vls = { }
|
||||
for line in msg['body_text'].split('\n'):
|
||||
for line in msg.get('body_text', '').split('\n'):
|
||||
line = line.strip()
|
||||
res = tools.misc.command_re.match(line)
|
||||
if res and maps.get(res.group(1).lower(), False):
|
||||
key = maps.get(res.group(1).lower())
|
||||
vls[key] = res.group(2).lower()
|
||||
update_vals[key] = res.group(2).lower()
|
||||
|
||||
vals.update(vls)
|
||||
res = self.write(cr, uid, ids, vals)
|
||||
self.message_append_dict(cr, uid, ids, msg, context=context)
|
||||
return res
|
||||
return super(project_issue, self).message_update(cr, uid, ids, update_vals=update_vals, context=context)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# OpenChatter methods and notifications
|
||||
# -------------------------------------------------------
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
result = dict.fromkeys(ids, [])
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.state == 'draft' and obj.user_id:
|
||||
result[obj.id] = [obj.user_id.id]
|
||||
return result
|
||||
|
||||
def message_get_subscribers(self, cr, uid, ids, context=None):
|
||||
sub_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context);
|
||||
""" Override to add responsible user. """
|
||||
user_ids = super(project_issue, self).message_get_subscribers(cr, uid, ids, context=context)
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.user_id:
|
||||
sub_ids.append(obj.user_id.id)
|
||||
return self.pool.get('res.users').read(cr, uid, sub_ids, context=context)
|
||||
if obj.user_id and not obj.user_id.id in user_ids:
|
||||
user_ids.append(obj.user_id.id)
|
||||
return user_ids
|
||||
|
||||
def stage_set_send_note(self, cr, uid, ids, stage_id, context=None):
|
||||
""" Override of the (void) default notification method. """
|
||||
|
||||
@@ -227,5 +227,10 @@
|
||||
<field name="stage_id" ref="project.project_tt_specification"/>
|
||||
</record>
|
||||
|
||||
<!-- Call Function to set some issues as Unread -->
|
||||
<function model="project.issue" name="message_mark_as_unread"
|
||||
eval="[ ref('crm_case_improvereportsinhrms0'), ref('crm_case_createnewobject0')], {}"
|
||||
/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -4,6 +4,43 @@
|
||||
|
||||
<menuitem id="menu_project_confi" name="Issues" parent="base.menu_definitions" sequence="2"/>
|
||||
|
||||
<!-- Project issue Read/Unread actions -->
|
||||
<record id="actions_server_project_issue_unread" model="ir.actions.server">
|
||||
<field name="name">Mark unread</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_project_issue"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_unread(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_project_issue_unread" model="ir.values">
|
||||
<field name="name">action_project_issue_unread</field>
|
||||
<field name="action_id" ref="actions_server_project_issue_unread"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_project_issue_unread'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_project_issue" />
|
||||
<field name="model">project.issue</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record id="actions_server_project_issue_read" model="ir.actions.server">
|
||||
<field name="name">Mark read</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_project_issue"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_read(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_project_issue_read" model="ir.values">
|
||||
<field name="name">action_project_issue_read</field>
|
||||
<field name="action_id" ref="actions_server_project_issue_read"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_project_issue_read'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_project_issue" />
|
||||
<field name="model">project.issue</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="project_issue_version_search_view">
|
||||
<field name="name">Issue Version</field>
|
||||
<field name="model">project.issue.version</field>
|
||||
@@ -159,9 +196,13 @@
|
||||
<group>
|
||||
<field name="name" string="Issue" filter_domain="['|', '|',('partner_id','ilike',self),('email_from','ilike',self),('name','ilike',self)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-mail-message-new"
|
||||
string="Inbox" help="Unread messages"
|
||||
name="needaction_pending"
|
||||
domain="[('needaction_pending','=',True)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter string="New" icon="terp-document-new" domain="[('state','=','draft')]" help="New Issues"/>
|
||||
<filter string="To Do" domain="[('state','=','open')]" help="To Do Issues" icon="terp-check"/>
|
||||
<filter string="Pending" domain="[('state','=','pending')]" help="Pending Issues" icon="terp-gtk-media-pause"/>
|
||||
<filter string="Unassigned Issues" domain="[('user_id','=',False)]" help="Unassigned Issues" icon="terp-personal-" />
|
||||
<separator orientation="vertical"/>
|
||||
<field name="user_id"/>
|
||||
|
||||
@@ -19,30 +19,35 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import binascii
|
||||
from osv import fields, osv
|
||||
from tools.translate import _
|
||||
import tools
|
||||
import binascii
|
||||
|
||||
|
||||
class project_tasks(osv.osv):
|
||||
_inherit = 'project.task'
|
||||
|
||||
def message_new(self, cr, uid, msg, custom_values=None, context=None):
|
||||
res_id = super(project_tasks,self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
|
||||
subject = msg.get('subject')
|
||||
msg_from = msg.get('from')
|
||||
data = {
|
||||
""" Overrides mail_thread message_new that is called by the mailgateway
|
||||
through message_process.
|
||||
This override updates the document according to the email.
|
||||
"""
|
||||
if custom_values is None: custom_values = {}
|
||||
custom_values.update({
|
||||
'name': subject,
|
||||
'planned_hours': 0.0,
|
||||
}
|
||||
data.update(self.message_partner_by_email(cr, uid, msg_from))
|
||||
self.write(cr, uid, [res_id], data, context)
|
||||
return res_id
|
||||
|
||||
def message_update(self, cr, uid, ids, msg, data={}, default_act='pending'):
|
||||
act = 'do_'+default_act
|
||||
'subject': msg.get('subject'),
|
||||
})
|
||||
custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from', False), context=context))
|
||||
return super(project_tasks,self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
|
||||
|
||||
def message_update(self, cr, uid, ids, msg, update_vals=None, context=None):
|
||||
""" Overrides mail_thread message_update that is called by the mailgateway
|
||||
through message_process.
|
||||
This method updates the task according to the email.
|
||||
"""
|
||||
if update_vals is None: update_vals = {}
|
||||
act = False
|
||||
maps = {
|
||||
'cost':'planned_hours',
|
||||
}
|
||||
@@ -54,17 +59,15 @@ class project_tasks(osv.osv):
|
||||
field = maps.get(match)
|
||||
if field:
|
||||
try:
|
||||
data[field] = float(res.group(2).lower())
|
||||
update_vals[field] = float(res.group(2).lower())
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
elif match.lower() == 'state' \
|
||||
and res.group(2).lower() in ['cancel','close','draft','open','pending']:
|
||||
act = 'do_%s' % res.group(2).lower()
|
||||
|
||||
self.write(cr, uid, ids, data, context=context)
|
||||
getattr(self,act)(cr, uid, ids, context=context)
|
||||
self.message_append_note(cr, uid, [res_id], body=msg, context=context)
|
||||
return True
|
||||
if act:
|
||||
getattr(self,act)(cr, uid, ids, context=context)
|
||||
return super(project_tasks,self).message_update(cr, uid, msg, update_vals=update_vals, context=context)
|
||||
|
||||
def message_thread_followers(self, cr, uid, ids, context=None):
|
||||
followers = super(project_tasks,self).message_thread_followers(cr, uid, ids, context=context)
|
||||
@@ -74,5 +77,5 @@ class project_tasks(osv.osv):
|
||||
followers[task.id] = filter(None, task_followers)
|
||||
return followers
|
||||
|
||||
project_tasks()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -733,10 +733,10 @@ class purchase_order(osv.osv):
|
||||
# --------------------------------------
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
result = dict.fromkeys(ids, [])
|
||||
result = super(purchase_order, self).get_needaction_user_ids(cr, uid, ids, context=context)
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.state == 'approved':
|
||||
result[obj.id] = [obj.validator.id]
|
||||
result[obj.id].append(obj.validator.id)
|
||||
return result
|
||||
|
||||
def create_send_note(self, cr, uid, ids, context=None):
|
||||
|
||||
@@ -52,10 +52,10 @@
|
||||
action="base.action_partner_category_form" id="menu_partner_categories_in_form" name="Partner Categories"
|
||||
parent="purchase.menu_purchase_partner_cat" groups="base.group_no_one"/>
|
||||
|
||||
<!--supplier menu-->
|
||||
<!--supplier menu-->
|
||||
<menuitem id="base.menu_procurement_management_supplier_name" name="Suppliers"
|
||||
parent="menu_procurement_management"
|
||||
action="base.action_partner_supplier_form" sequence="15"/>
|
||||
parent="menu_procurement_management"
|
||||
action="base.action_partner_supplier_form" sequence="15"/>
|
||||
|
||||
<!--Inventory control-->
|
||||
<menuitem id="menu_procurement_management_inventory" name="Receive Products"
|
||||
@@ -65,9 +65,9 @@
|
||||
<menuitem action="stock.action_reception_picking_move" id="menu_action_picking_tree_in_move"
|
||||
parent="menu_procurement_management_inventory" sequence="11"/>
|
||||
|
||||
<!--Invoice control-->
|
||||
<menuitem id="menu_procurement_management_invoice" name="Invoice Control"
|
||||
parent="base.menu_purchase_root" sequence="6"/>
|
||||
<!--Invoice control-->
|
||||
<menuitem id="menu_procurement_management_invoice" name="Invoice Control"
|
||||
parent="base.menu_purchase_root" sequence="6"/>
|
||||
|
||||
<record id="action_invoice_pending" model="ir.actions.act_window">
|
||||
<field name="name">Based on Draft Invoices</field>
|
||||
@@ -101,6 +101,43 @@
|
||||
<menuitem name="Products" id="menu_procurement_partner_contact_form" action="product.product_normal_action_puchased"
|
||||
parent="menu_procurement_management_product"/>
|
||||
|
||||
<!-- Purchase order Read/Unread actions -->
|
||||
<record id="actions_server_purchase_order_unread" model="ir.actions.server">
|
||||
<field name="name">Mark unread</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_purchase_order"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_unread(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_purchase_order_unread" model="ir.values">
|
||||
<field name="name">action_purchase_order_unread</field>
|
||||
<field name="action_id" ref="actions_server_purchase_order_unread"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_purchase_order_unread'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_purchase_order" />
|
||||
<field name="model">purchase.order</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record id="actions_server_purchase_order_read" model="ir.actions.server">
|
||||
<field name="name">Mark read</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_purchase_order"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_read(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_purchase_order_read" model="ir.values">
|
||||
<field name="name">action_purchase_order_read</field>
|
||||
<field name="action_id" ref="actions_server_purchase_order_read"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_purchase_order_read'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_purchase_order" />
|
||||
<field name="model">purchase.order</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="purchase_order_calendar">
|
||||
<field name="name">purchase.order.calendar</field>
|
||||
<field name="model">purchase.order</field>
|
||||
@@ -267,6 +304,11 @@
|
||||
<group>
|
||||
<field name="name" string="Reference"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-mail-message-new"
|
||||
string="Inbox" help="Unread messages"
|
||||
name="needaction_pending"
|
||||
domain="[('needaction_pending','=',True)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-document-new" name="draft" string="Quotations" domain="[('state','=','draft')]" help="Purchase orders which are in draft state"/>
|
||||
<filter icon="terp-check" name="approved" string="Purchase Orders" domain="[('state','not in',('draft','cancel'))]" help="Approved purchase orders"/>
|
||||
<filter icon="terp-gtk-go-back-rtl" name="not_invoiced" string="Not Invoiced" domain="[('invoice_ids','=', False)]" help="Purchase orders that include lines not invoiced."/>
|
||||
|
||||
@@ -183,13 +183,6 @@ class purchase_requisition(osv.osv):
|
||||
|
||||
return res
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
result = dict.fromkeys(ids, [])
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if (obj.state == 'draft') and obj.user_id:
|
||||
result[obj.id] = [obj.user_id.id]
|
||||
return result
|
||||
|
||||
def create_send_note(self, cr, uid, ids, context=None):
|
||||
return self.message_append_note(cr, uid, ids, body=_("Purchase Requisition has been <b>created</b>."), context=context)
|
||||
|
||||
|
||||
+2
-2
@@ -1022,10 +1022,10 @@ class sale_order(osv.osv):
|
||||
# ------------------------------------------------
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
result = dict.fromkeys(ids, [])
|
||||
result = super(sale_order, self).get_needaction_user_ids(cr, uid, ids, context=context)
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if (obj.state == 'manual' or obj.state == 'progress'):
|
||||
result[obj.id] = [obj.user_id.id]
|
||||
result[obj.id].append(obj.user_id.id)
|
||||
return result
|
||||
|
||||
def create_send_note(self, cr, uid, ids, context=None):
|
||||
|
||||
@@ -53,6 +53,43 @@
|
||||
<menuitem action="action_shop_form" id="menu_action_shop_form" parent="base.menu_base_config" sequence="35"
|
||||
groups="stock.group_locations"/>
|
||||
|
||||
<!-- Sale order Read/Unread actions -->
|
||||
<record id="actions_server_sale_order_unread" model="ir.actions.server">
|
||||
<field name="name">Mark unread</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_sale_order"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_unread(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_sale_order_unread" model="ir.values">
|
||||
<field name="name">action_sale_order_unread</field>
|
||||
<field name="action_id" ref="actions_server_sale_order_unread"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_sale_order_unread'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_sale_order" />
|
||||
<field name="model">sale.order</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record id="actions_server_sale_order_read" model="ir.actions.server">
|
||||
<field name="name">Mark read</field>
|
||||
<field name="condition">True</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_sale_order"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">self.message_check_and_set_read(cr, uid, context.get('active_ids'), context=context)</field>
|
||||
</record>
|
||||
<record id="action_sale_order_read" model="ir.values">
|
||||
<field name="name">action_sale_order_read</field>
|
||||
<field name="action_id" ref="actions_server_sale_order_read"/>
|
||||
<field name="value" eval="'ir.actions.server,' + str(ref('actions_server_sale_order_read'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model_id" ref="model_sale_order" />
|
||||
<field name="model">sale.order</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
</record>
|
||||
|
||||
<record id="view_sale_order_calendar" model="ir.ui.view">
|
||||
<field name="name">sale.order.calendar</field>
|
||||
<field name="model">sale.order</field>
|
||||
@@ -297,6 +334,11 @@
|
||||
<search string="Search Sales Order">
|
||||
<field name="name" string="Sales Order" filter_domain="['|',('name','ilike',self),('client_order_ref','ilike',self)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-mail-message-new"
|
||||
string="Inbox" help="Unread messages"
|
||||
name="needaction_pending"
|
||||
domain="[('needaction_pending','=',True)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-document-new" string="Quotations" name="draft" domain="[('state','in',('draft','sent'))]" help="Sales Order that haven't yet been confirmed"/>
|
||||
<filter icon="terp-check" string="Sales" name="sales" domain="[('state','in',('manual','progress'))]"/>
|
||||
<filter icon="terp-dolar_ok!" string="To Invoice" domain="[('state','=','manual')]" help="Sales Order ready to be invoiced"/>
|
||||
|
||||
Reference in New Issue
Block a user