[IMP] marketing_campaign: another round of reviewing/improvements:

- better form/search views
- improved labels
- added next sync on segments
- added domain to prevent cross-campaign transitions on activities
- ...

bzr revid: odo@openerp.com-20100913004337-0cvl8qd0qaj3fomc
This commit is contained in:
Olivier Dony 2010-09-13 02:43:37 +02:00
parent 27a92be325
commit 33cb2c8c9a
3 changed files with 89 additions and 77 deletions

View File

@ -135,12 +135,12 @@ Normal - the campaign runs normally and automatically sends all emails and repor
if activity.type != 'email':
continue
if not activity.email_template_id.from_account:
raise osv.except_osv(_("Error"), _("The campaign cannot be started: an email account is missing in the email activity '%s'")%activity.name)
raise osv.except_osv(_("Error"), _("The campaign cannot be started: the email account is missing in email activity '%s'")%activity.name)
if activity.email_template_id.from_account.state != 'approved':
raise osv.except_osv(_("Error"), _("The campaign cannot be started: the email account is not approved in the email activity '%s'")%activity.name)
raise osv.except_osv(_("Error"), _("The campaign cannot be started: the email account is not approved in email activity '%s'")%activity.name)
if not has_start and not has_signal_without_from:
raise osv.except_osv(_("Error"), _("The campaign hasn't any starting activity nor any activity with a signal and no previous activity."))
raise osv.except_osv(_("Error"), _("The campaign cannot be started: it doesn't have any starting activity (or any activity with a signal and no previous activity)"))
return self.write(cr, uid, ids, {'state': 'running'})
@ -213,27 +213,31 @@ class marketing_campaign_segment(osv.osv):
_name = "marketing.campaign.segment"
_description = "Campaign Segment"
def _get_next_sync(self, cr, uid, ids, fn, args, context=None):
# next auto sync date is same for all segments
sync_job = self.pool.get('ir.model.data').get_object(cr, uid, 'marketing_campaign', 'ir_cron_marketing_campaign_every_day', context=context)
next_sync = sync_job and sync_job.nextcall or False
return dict.fromkeys(ids, next_sync)
_columns = {
'name': fields.char('Name', size=64,required=True),
'campaign_id': fields.many2one('marketing.campaign', 'Campaign',
required=True, select=1, ondelete="cascade"),
'object_id': fields.related('campaign_id','object_id',
type='many2one', relation='ir.model',
string='Object'),
'ir_filter_id': fields.many2one('ir.filters', 'Filter', help="Filter to select the matching resource records that belong to this segment. New filters can be created and saved using the advanced search on the list view of the Resource"),
'campaign_id': fields.many2one('marketing.campaign', 'Campaign', required=True, select=1, ondelete="cascade"),
'object_id': fields.related('campaign_id','object_id', type='many2one', relation='ir.model', string='Resource'),
'ir_filter_id': fields.many2one('ir.filters', 'Filter', help="Filter to select the matching resource records that belong to this segment. New filters can be created and saved using the advanced search on the list view of the Resource. If no filter is set, all records are selected without filtering. The synchronization mode may also add a criterion to the filter."),
'sync_last_date': fields.datetime('Last Synchronization', help="Date on which this segment was synchronized last time (automatically or manually)"),
'sync_mode': fields.selection([('create_date', 'If record created after last sync'),
('write_date', 'If record modified after last sync (no duplicates)'),
'sync_mode': fields.selection([('create_date', 'Only records created after last sync'),
('write_date', 'Only records modified after last sync (no duplicates)'),
('all', 'All records (no duplicates)')],
'Workitem creation mode',
help="Determines how new campaign workitems are created for resource records matching this segment. This is used when segments are synchronized manually, or automatically via the scheduled job."),
'Synchronization mode',
help="Determines an additional criterion to add to the filter when selecting new records to inject in the campaign."),
'state': fields.selection([('draft', 'Draft'),
('running', 'Running'),
('done', 'Done'),
('cancelled', 'Cancelled')],
'State',),
'date_run': fields.datetime('Launching Date', help="Initial start date of this segment."),
'date_run': fields.datetime('Launch Date', help="Initial start date of this segment."),
'date_done': fields.datetime('End Date', help="Date this segment was last closed or cancelled."),
'date_next_sync': fields.function(_get_next_sync, method=True, string='Next Synchronization', type='datetime', help="Next time the synchronization job is scheduled to run automatically"),
}
_defaults = {
@ -264,7 +268,6 @@ class marketing_campaign_segment(osv.osv):
if model_name:
mod_name = model_name[0]['model']
res['domain'] = {'ir_filter_id': [('model_id', '=', mod_name)]}
res['context'] = {'default_model_id': model_name[0]['model']}
else:
res['value'] = {'ir_filter_id': False}
return res
@ -594,7 +597,7 @@ class marketing_campaign_workitem(osv.osv):
'campaign_id': fields.related('activity_id', 'campaign_id',
type='many2one', relation='marketing.campaign', string='Campaign', readonly=True, store=True),
'object_id': fields.related('activity_id', 'campaign_id', 'object_id',
type='many2one', relation='ir.model', string='Resource', select=1, readonly=True),
type='many2one', relation='ir.model', string='Resource', select=1, readonly=True, store=True),
'res_id': fields.integer('Resource ID', select=1, readonly=True),
'res_name': fields.function(_res_name_get, method=True, string='Resource Name', fnct_search=_resource_search, type="char", size=64),
'date': fields.datetime('Execution Date', help='If date is not set, this workitem has to be run manually', readonly=True),

View File

@ -5,7 +5,7 @@
<!-- Cron -->
<record model="ir.cron" id="ir_cron_marketing_campaign_every_hour">
<field name="name">Check Marketing Campaign Activities</field>
<field name="name">Marketing Campaign: Campaign workitems processing</field>
<field name="interval_number">1</field>
<field name="interval_type">hours</field>
<field name="numbercall">-1</field>
@ -14,9 +14,9 @@
<field name="function" eval="'process_all'"/>
<field name="args" eval="'()'"/>
</record>
<record model="ir.cron" id="ir_cron_marketing_campaign_every_day">
<field name="name">Check Segment</field>
<field name="name">Marketing Campaign: Segment Sync</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field><!-- it s every day -->
<field name="numbercall">-1</field>
@ -25,6 +25,6 @@
<field name="function" eval="'process_segment'"/>
<field name="args" eval="'()'"/>
</record>
</data>
</openerp>

View File

@ -32,14 +32,14 @@
<field name="fixed_cost"/>
</group>
</group>
<field name="activity_ids" nolabel = "1" colspan="4" default_get="{'default_object_id': object_id}" />
<field name="activity_ids" nolabel = "1" colspan="4" context="{'default_object_id': object_id, 'default_campaign_id': active_id}" />
<separator string="" colspan="4" />
<group col="10" colspan="4">
<field name="state" readonly="1" />
<button name="state_running_set" string="Run" states="draft,done,cancelled" icon="gtk-apply"/>
<button name="state_draft_set" string="Set to Draft" states="done,cancelled" icon="gtk-convert"/>
<button name="state_done_set" string="Close" states="running" icon="terp-dialog-close"/>
<button name="state_cancel_set" string="Cancelled" states="running" icon="terp-gtk-stop"/>
<button name="state_cancel_set" string="Cancel" states="running" icon="terp-gtk-stop"/>
</group>
</form>
</field>
@ -139,18 +139,19 @@
<separator string="Segment" colspan="4"/>
<field name="name"/>
<field name="campaign_id" select="1" on_change="onchange_campaign_id(campaign_id)"/>
<field name="object_id" invisible="1"/>
</group>
<group colspan="2" col="2">
<separator string="Filter" colspan="4"/>
<field name="ir_filter_id" select="1"/>
<field name="ir_filter_id" domain="[('model_id', '=', object_id)]"/>
</group>
<newline/>
<group colspan="2" col="2">
<group colspan="2" col="4">
<separator string="Synchronization" colspan="4"/>
<field name="sync_mode" required="True"/>
<field name="sync_last_date"/>
<label string="" colspan="1"/>
<button string="Synchronize" states="running" name="synchroniz" icon="terp-project" type="object"/>
<field name="sync_mode" required="True" colspan="4"/>
<field name="sync_last_date" colspan="3"/>
<button string="Synchronize Manually" states="running" name="synchroniz" icon="terp-project" type="object"/>
<field name="date_next_sync" colspan="3"/>
</group>
<group colspan="2" col="2">
<separator string="History" colspan="2"/>
@ -179,6 +180,7 @@
<field name="campaign_id"/>
<field name="date_run"/>
<field name="sync_last_date"/>
<button string="Synchronize Manually" states="running" name="synchroniz" icon="terp-project" type="object"/>
<field name="state" />
</tree>
</field>
@ -193,9 +195,9 @@
<filter icon="terp-check" string="Running" name="running" domain="[('state','=','running')]"/>
<filter icon="terp-document-new" string="Draft" domain="[('state','=','draft')]"/>
<separator orientation="vertical"/>
<filter icon="terp-project" string="Newly Created" domain="[('sync_mode','=','create_date')]"/>
<filter icon="terp-project" string="Newly Modified" domain="[('sync_mode','=','write_date')]"/>
<filter icon="terp-project" string="All" domain="[('sync_mode','=','all')]"/>
<filter icon="terp-project" string="Newly Created" help="Sync mode: only records created after last sync" domain="[('sync_mode','=','create_date')]"/>
<filter icon="terp-project" string="Newly Modified" help="Sync mode: only records updated after last sync" domain="[('sync_mode','=','write_date')]"/>
<filter icon="terp-project" string="All" help="Sync mode: all records" domain="[('sync_mode','=','all')]"/>
<separator orientation="vertical"/>
<field name="name" select="1"/>
<field name="campaign_id" select="1"/>
@ -207,7 +209,7 @@
<separator orientation="vertical"/>
<filter string="State" name="State" icon="terp-stock_effects-object-colorize" context="{'group_by':'state'}" />
<separator orientation="vertical"/>
<filter string="Run Date" name="Date Run" icon="terp-go-month" context="{'group_by':'date_run'}" />
<filter string="Launch Date" name="Launch Date" icon="terp-go-month" context="{'group_by':'date_run'}" />
</group>
</search>
</field>
@ -246,6 +248,7 @@
<field name="signal" groups="base.group_extended"/>
<field name="start"/>
<field name="object_id" invisible="1"/>
<field name="campaign_id" invisible="1"/>
</group>
<group colspan='2' col='2'>
<separator string="Cost / Revenue" colspan="4"/>
@ -271,37 +274,35 @@
<field name="server_action_id" attrs="{'required':[('type','=','action')],'invisible':[('type','!=','action')]}" domain="[('model_id','=',object_id)]" />
</group>
</group>
<group colspan="4" col="2">
<separator string="Transitions" colspan="2"/>
<field name="from_ids" nolabel="1" mode="tree,form" default_get="{'default_activity_to_id': active_id}">
<tree string="Incoming Transitions" editable="bottom">
<field name="activity_from_id"/>
<field name='trigger'/>
<field name="interval_nbr"/>
<field name="interval_type"/>
</tree>
<form string="Incoming Transitions">
<field name="activity_from_id"/>
<field name='trigger'/>
<field name="interval_nbr"/>
<field name="interval_type"/>
</form>
</field>
<field name="to_ids" nolabel="1" mode="tree,form" default_get="{'default_activity_from_id': active_id}">
<tree string="Outgoing Transitions" editable="bottom">
<field name="activity_to_id"/>
<field name='trigger'/>
<field name="interval_nbr"/>
<field name="interval_type"/>
</tree>
<form string="Outgoing Transitions">
<field name="activity_to_id"/>
<field name='trigger'/>
<field name="interval_nbr"/>
<field name="interval_type"/>
</form>
</field>
</group>
<separator string="Transitions" colspan="4"/>
<field name="from_ids" nolabel="1" mode="tree,form" context="{'default_activity_to_id': active_id}" colspan="2">
<tree string="Incoming Transitions" editable="bottom">
<field name="activity_from_id" domain="[('campaign_id', '=', parent.campaign_id)]"/>
<field name='trigger'/>
<field name="interval_nbr"/>
<field name="interval_type"/>
</tree>
<form string="Incoming Transitions">
<field name="activity_from_id" domain="[('campaign_id', '=', parent.campaign_id)]"/>
<field name='trigger'/>
<field name="interval_nbr"/>
<field name="interval_type"/>
</form>
</field>
<field name="to_ids" nolabel="1" mode="tree,form" context="{'default_activity_from_id': active_id}" colspan="2">
<tree string="Outgoing Transitions" editable="bottom">
<field name="activity_to_id" domain="[('campaign_id', '=', parent.campaign_id)]"/>
<field name='trigger'/>
<field name="interval_nbr"/>
<field name="interval_type"/>
</tree>
<form string="Outgoing Transitions">
<field name="activity_to_id" domain="[('campaign_id', '=', parent.campaign_id)]"/>
<field name='trigger'/>
<field name="interval_nbr"/>
<field name="interval_type"/>
</form>
</field>
</form>
</field>
</record>
@ -333,14 +334,15 @@
<field name="type">tree</field>
<field name="arch" type="xml">
<tree colors="red:state in ('exception');black:state in ('todo');gray:state in ('cancelled')" string="Marketing Campaign Activities">
<field name="campaign_id"/>
<field name="segment_id"/>
<field name="activity_id" />
<field name="object_id" invisible="1"/>
<field name="res_id" invisible="1"/>
<field name="res_name" />
<field name="partner_id"/>
<field name="date"/>
<field name="state"/>
<field name="res_id" invisible="1"/>
<field name="campaign_id" invisible="1"/>
<button string="Preview" states="todo" name="preview" icon="gtk-zoom-fit" type="object"/>
<button string="Process" states="todo" name="process" type="object" icon="terp-gtk-go-back-rtl"/>
<button string="Cancel" states="todo" name="button_cancel" type="object" icon="terp-gtk-stop"/>
@ -394,21 +396,28 @@
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Marketing Campaign Activities">
<filter icon="terp-go-today" string="Today" name="today" domain="[('date','&lt;', time.strftime('%%Y-%%m-%%d 23:59:59')), ('date','&gt;=', time.strftime('%%Y-%%m-%%d 00:00:00'))]"/>
<filter icon="terp-gtk-go-back-rtl" string="To Do" name="todo" domain="[('state','=','todo')]"/>
<filter icon="terp-emblem-important" string="Exception" domain="[('state','=','exception')]"/>
<separator orientation="vertical"/>
<field name="segment_id" select="1"/>
<field name="res_name" select="1"/>
<field name="res_id" select="1"/>
<field name="partner_id" select="1"/>
<field name="date" select="1"/>
<group colspan="4" col="10">
<filter icon="terp-go-today" string="Today" name="today" domain="[('date','&lt;', time.strftime('%%Y-%%m-%%d 23:59:59')), ('date','&gt;=', time.strftime('%%Y-%%m-%%d 00:00:00'))]"/>
<filter icon="terp-gtk-go-back-rtl" string="To Do" name="todo" domain="[('state','=','todo')]"/>
<filter icon="terp-emblem-important" string="Exception" domain="[('state','=','exception')]"/>
<separator orientation="vertical"/>
<field name="campaign_id"/>
<field name="segment_id"/>
<field name="date"/>
<newline/>
<field name="object_id"/>
<field name="res_name"/>
<field name="res_id"/>
<field name="partner_id"/>
</group>
<newline/>
<group expand="0" string="Group By..." colspan="10" col="12">
<filter string="Campaign" name="campaign" icon="terp-gtk-jump-to-rtl" context="{'group_by':'campaign_id'}" />
<filter string="Segment" name="segment" icon="terp-stock_symbol-selection" context="{'group_by':'segment_id'}" />
<filter string="Activity" name="activity" icon="terp-stock_align_left_24" context="{'group_by':'activity_id'}" />
<filter string="Resource" name="resource" icon="terp-accessories-archiver" context="{'group_by':'res_id'}" />
<separator orientation="vertical"/>
<filter string="Resource" name="resource" icon="terp-accessories-archiver" context="{'group_by':'object_id'}" />
<filter string="Resource ID" name="res_id" icon="terp-accessories-archiver" context="{'group_by':'res_id'}" />
<separator orientation="vertical"/>
<filter string="State" name="State" icon="terp-stock_effects-object-colorize" context="{'group_by':'state'}" />
<separator orientation="vertical"/>
@ -426,7 +435,7 @@
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_marketing_campaign_workitem_tree"/>
<field name="search_view_id" ref="view_marketing_campaign_workitem_search"/>
<field name="context">{'group_by': [], 'search_default_todo': 1}</field>
<field name="context">{'group_by': [], 'search_default_todo': 1, 'search_default_today': 1}</field>
</record>
<menuitem id="menu_action_marketing_campaign_workitem" parent="menu_marketing_campaign" action="action_marketing_campaign_workitem" sequence="30"/>
@ -443,7 +452,7 @@
<!-- Campaign Followups -->
<act_window domain="[('campaign_id', '=', active_id)]"
id="act_marketing_campaing_followup"
name="Campaign Follow-ups" res_model="marketing.campaign.workitem"
name="Campaign Follow-up" res_model="marketing.campaign.workitem"
src_model="marketing.campaign" view_mode="tree,form"
view_type="form" />