[MIG] marketing_campaign: Migrate to new API

This commit is contained in:
Yannick Tivisse
2016-08-01 17:34:36 +02:00
parent 531b9dfaa5
commit 0fa2ac1d5d
14 changed files with 583 additions and 738 deletions
+1 -8
View File
@@ -1,14 +1,10 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Marketing Campaigns',
'version': '1.1',
'depends': ['document',
'mail',
'decimal_precision'
],
'depends': ['document', 'mail', 'decimal_precision'],
'category': 'Marketing',
'description': """
This module provides leads automation through marketing campaigns (campaigns can in fact be defined on any resource, not just CRM Leads).
@@ -44,7 +40,4 @@ input segments, workflow.
'report/campaign_analysis_view.xml',
],
'demo': ['data/marketing_campaign_demo.xml'],
'test': ['test/marketing_campaign.yml'],
'installable': True,
'auto_install': False,
}
@@ -1,5 +1,5 @@
<?xml version="1.0" ?>
<openerp>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- Cron -->
@@ -27,4 +27,4 @@
</record>
</data>
</openerp>
</odoo>
@@ -1,5 +1,5 @@
<openerp>
<data>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Email tempalte -->
<record id="email_template_1" model="mail.template">
<field name="name">welcome new partner</field>
@@ -100,5 +100,4 @@
<field name="campaign_id" ref="marketing_campaign_openerppartnerchannel"/>
</record>
</data>
</openerp>
</odoo>
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<odoo>
<!-- Marketing Campaigns -->
<record id="wkf_marketing_campaign" model="workflow">
@@ -140,5 +139,4 @@
<field name="signal">state_draft_set</field>
</record>
</data>
</openerp>
</odoo>
+10 -9
View File
@@ -1,15 +1,16 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.osv import osv
from odoo import api, models
class report_xml(osv.osv):
class IrActionsReportXml(models.Model):
_inherit = 'ir.actions.report.xml'
def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
if context is None:
context = {}
object_id = context.get('object_id')
if object_id:
model = self.pool.get('ir.model').browse(cr, uid, object_id, context=context).model
@api.model
def search(self, args, offset=0, limit=None, order=None, count=False):
model_id = self.env.context.get('object_id')
if model_id:
model = self.env['ir.model'].browse(model_id).model
args.append(('model', '=', model))
return super(report_xml, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count)
return super(IrActionsReportXml, self).search(args, offset=offset, limit=limit, order=order, count=count)
@@ -1,14 +1,12 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.osv import osv
from odoo import fields, models
class mail_template(osv.osv):
class MailTemplate(models.Model):
_inherit = "mail.template"
_defaults = {
'model_id': lambda obj, cr, uid, context: context.get('object_id',False),
}
model_id = fields.Many2one('ir.model', default=lambda self: self.env.context.get('object_id', False))
# TODO: add constraint to prevent disabling / disapproving an email account used in a running campaign
File diff suppressed because it is too large Load Diff
@@ -1,80 +1,79 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp import tools
from openerp.osv import fields, osv
from odoo import tools
from odoo import api, fields, models
class campaign_analysis(osv.osv):
class CampaignAnalysis(models.Model):
_name = "campaign.analysis"
_description = "Campaign Analysis"
_auto = False
_rec_name = 'date'
def _total_cost(self, cr, uid, ids, field_name, arg, context=None):
"""
@param cr: the current row, from the database cursor,
@param uid: the current user’s ID for security checks,
@param ids: List of case and section Data’s IDs
@param context: A standard dictionary for contextual values
"""
result = {}
for ca_obj in self.browse(cr, uid, ids, context=context):
wi_ids = self.pool.get('marketing.campaign.workitem').search(cr, uid,
[('segment_id.campaign_id', '=', ca_obj.campaign_id.id)])
total_cost = ca_obj.activity_id.variable_cost + \
((ca_obj.campaign_id.fixed_cost or 1.00) / len(wi_ids))
result[ca_obj.id] = total_cost
return result
_columns = {
'res_id' : fields.integer('Resource', readonly=True),
'year': fields.char('Execution Year', size=4, readonly=True),
'month': fields.selection([('01','January'), ('02','February'),
('03','March'), ('04','April'),('05','May'), ('06','June'),
('07','July'), ('08','August'), ('09','September'),
('10','October'), ('11','November'), ('12','December')],
'Execution Month', readonly=True),
'day': fields.char('Execution Day', size=10, readonly=True),
'date': fields.date('Execution Date', readonly=True, select=True),
'campaign_id': fields.many2one('marketing.campaign', 'Campaign',
readonly=True),
'activity_id': fields.many2one('marketing.campaign.activity', 'Activity',
readonly=True),
'segment_id': fields.many2one('marketing.campaign.segment', 'Segment',
readonly=True),
'partner_id': fields.many2one('res.partner', 'Partner', readonly=True),
'country_id': fields.related('partner_id', 'country_id',
type='many2one', relation='res.country',string='Country'),
'total_cost' : fields.function(_total_cost, string='Cost',
type="float"),
'revenue': fields.float('Revenue', readonly=True, digits=0),
'count' : fields.integer('# of Actions', readonly=True),
'state': fields.selection([('todo', 'To Do'),
('exception', 'Exception'), ('done', 'Done'),
('cancelled', 'Cancelled')], 'Status', readonly=True),
}
def init(self, cr):
tools.drop_view_if_exists(cr, 'campaign_analysis')
cr.execute("""
create or replace view campaign_analysis as (
select
min(wi.id) as id,
min(wi.res_id) as res_id,
to_char(wi.date::date, 'YYYY') as year,
to_char(wi.date::date, 'MM') as month,
to_char(wi.date::date, 'YYYY-MM-DD') as day,
wi.date::date as date,
s.campaign_id as campaign_id,
wi.activity_id as activity_id,
wi.segment_id as segment_id,
wi.partner_id as partner_id ,
wi.state as state,
sum(act.revenue) as revenue,
count(*) as count
from
res_id = fields.Integer('Resource', readonly=True)
year = fields.Char('Execution Year', readonly=True)
month = fields.Selection([
('01', 'January'),
('02', 'February'),
('03', 'March'),
('04', 'April'),
('05', 'May'),
('06', 'June'),
('07', 'July'),
('08', 'August'),
('09', 'September'),
('10', 'October'),
('11', 'November'),
('12', 'December')
], 'Execution Month', readonly=True)
day = fields.Char('Execution Day', readonly=True)
date = fields.Date('Execution Date', readonly=True, index=True)
campaign_id = fields.Many2one('marketing.campaign', 'Campaign', readonly=True)
activity_id = fields.Many2one('marketing.campaign.activity', 'Activity', readonly=True)
segment_id = fields.Many2one('marketing.campaign.segment', 'Segment', readonly=True)
partner_id = fields.Many2one('res.partner', 'Partner', readonly=True)
country_id = fields.Many2one('res.country', related='partner_id.country_id', string='Country')
total_cost = fields.Float(compute='_compute_total_cost', string='Cost')
revenue = fields.Float('Revenue', readonly=True, digits=0)
count = fields.Integer('# of Actions', readonly=True)
state = fields.Selection([
('todo', 'To Do'),
('exception', 'Exception'),
('done', 'Done'),
('cancelled', 'Cancelled')
], 'Status', readonly=True)
@api.multi
def _compute_total_cost(self):
for analysis in self:
wi_count = self.env['marketing.campaign.workitem'].search_count([('segment_id.campaign_id', '=', analysis.campaign_id.id)])
analysis.total_cost = analysis.activity.variable_cost + ((analysis.campaign_id.fixed_cost or 1.00) / wi_count)
@api.model_cr
def init(self):
tools.drop_view_if_exists(self.env.cr, 'campaign_analysis')
self.env.cr.execute("""
CREATE OR REPLACE VIEW campaign_analysis AS (
SELECT
min(wi.id) AS id,
min(wi.res_id) AS res_id,
to_char(wi.date::date, 'YYYY') AS year,
to_char(wi.date::date, 'MM') AS month,
to_char(wi.date::date, 'YYYY-MM-DD') AS day,
wi.date::date AS date,
s.campaign_id AS campaign_id,
wi.activity_id AS activity_id,
wi.segment_id AS segment_id,
wi.partner_id AS partner_id ,
wi.state AS state,
sum(act.revenue) AS revenue,
count(*) AS count
FROM
marketing_campaign_workitem wi
left join res_partner p on (p.id=wi.partner_id)
left join marketing_campaign_segment s on (s.id=wi.segment_id)
left join marketing_campaign_activity act on (act.id= wi.activity_id)
group by
LEFT JOIN res_partner p ON (p.id=wi.partner_id)
LEFT JOIN marketing_campaign_segment s ON (s.id=wi.segment_id)
LEFT JOIN marketing_campaign_activity act ON (act.id= wi.activity_id)
GROUP BY
s.campaign_id,wi.activity_id,wi.segment_id,wi.partner_id,wi.state,
wi.date::date
)
@@ -1,7 +1,5 @@
<?xml version="1.0"?>
<openerp>
<data>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_report_campaign_analysis_pivot" model="ir.ui.view">
<field name="name">campaign.analysis.pivot</field>
<field name="model">campaign.analysis</field>
@@ -83,6 +81,4 @@
</record>
<menuitem name="Campaign" action="action_campaign_analysis_all" id="menu_action_campaign_analysis_all" parent="report_marketing_campaign" sequence="20"/>
</data>
</openerp>
</odoo>
@@ -1,6 +1,5 @@
<?xml version="1.0"?>
<openerp>
<data>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="marketing_campaign.group_lead_automation_user" model="res.groups">
<field name="name">User</field>
<field name="category_id" ref="base.module_category_lead_automation"/>
@@ -16,6 +15,4 @@
<record id="base.default_user" model="res.users">
<field name="groups_id" eval="[(4,ref('marketing_campaign.group_lead_automation_manager'))]"/>
</record>
</data>
</openerp>
</odoo>
@@ -1,145 +0,0 @@
-
In order to test process of compaign, I start compaign.
-
!workflow {model: marketing.campaign, action: state_running_set, ref: marketing_campaign_openerppartnerchannel}
-
I check the campaign on Running mode after started.
-
!assert {model: marketing.campaign, id: marketing_campaign_openerppartnerchannel}:
- state == 'running'
-
I start this segment after assinged campaign.
-
!workflow {model: marketing.campaign.segment, action: state_running_set, ref: marketing_campaign_segment0}
-
I check the segment on Running mode after started.
-
!assert {model: marketing.campaign.segment, id: marketing_campaign_segment0}:
- state == 'running'
-
I synchronized segment manually to see all step of activity and process covered on this campaign.
-
!python {model: marketing.campaign.segment}: |
segment_id = self.browse(cr ,uid ,ref("marketing_campaign_segment0") ,context)
assert segment_id.date_next_sync, 'Next Synchronization date is not calculated.'
self.synchroniz(cr, uid, [ref("marketing_campaign_segment0")])
-
I cancel Marketing Workitems.
-
!python {model: marketing.campaign.workitem}: |
ids = self.search(cr, uid, [('segment_id', '=', ref('marketing_campaign_segment0')),
('campaign_id', '=', ref('marketing_campaign_openerppartnerchannel'))])
self.button_cancel(cr, uid, ids)
record = self.browse(cr, uid, ids[0])
assert record.state == 'cancelled' or record.state == 'done' , 'Marketing Workitem shoud be in cancel state.'
-
I set Marketing Workitems in draft state.
-
!python {model: marketing.campaign.workitem}: |
ids = self.search(cr, uid, [('segment_id', '=', ref('marketing_campaign_segment0')),
('campaign_id', '=', ref('marketing_campaign_openerppartnerchannel'))])
self.button_draft(cr, uid, ids)
record = self.browse(cr, uid, ids[0])
assert record.state == 'todo' or record.state == 'done' , 'Marketing Workitem shoud be in draft state.'
-
I process follow-up of first activity.
-
!python {model: marketing.campaign.workitem}: |
ids = self.search(cr, uid, [('segment_id', '=', ref('marketing_campaign_segment0')),
('campaign_id', '=', ref('marketing_campaign_openerppartnerchannel')), ('activity_id', '=', ref('marketing_campaign_activity_0'))])
assert ids, 'Follow-up item is not created for first activity.'
work_item_id = self.browse(cr ,uid ,ids[0] ,context)
assert work_item_id.res_name, 'Resource Name is not defined.'
self.process(cr, uid, ids)
record = self.browse(cr, uid, ids)[0]
assert record.state == "done", "Follow-up item should be closed after process."
-
I check follow-up detail of second activity after process of first activity.
-
!python {model: marketing.campaign.workitem}: |
ids = self.search(cr, uid, [('segment_id', '=', ref('marketing_campaign_segment0')),
('campaign_id', '=', ref('marketing_campaign_openerppartnerchannel')), ('activity_id', '=', ref('marketing_campaign_activity_1'))])
assert ids, 'Follow-up item is not created for second activity.'
-
Now I increase credit limit of customer
-
!python {model: res.partner}: |
self.write(cr, uid, [ref("base.res_partner_2")], {'credit_limit':41000}, context=context)
-
I process follow-up of second activity after set draft.
-
!python {model: marketing.campaign.workitem}: |
ids = self.search(cr, uid, [('segment_id', '=', ref('marketing_campaign_segment0')),
('campaign_id', '=', ref('marketing_campaign_openerppartnerchannel')), ('activity_id', '=', ref('marketing_campaign_activity_1'))])
self.button_draft(cr, uid, ids, context=context)
self.process(cr, uid, ids, context=context)
record = self.browse(cr, uid, ids[0], context=context)
assert record.state == "done", "Follow-up item should be closed after process."
-
I check follow-up detail of third activity after process of second activity.
-
!python {model: marketing.campaign.workitem}: |
ids = self.search(cr, uid, [('segment_id', '=', ref('marketing_campaign_segment0')),
('campaign_id', '=', ref('marketing_campaign_openerppartnerchannel')), ('activity_id', '=', ref('marketing_campaign_activity_2'))])
assert ids, 'Follow-up item is not created for third activity.'
-
Now I increase credit limit of customer
-
!python {model: res.partner}: |
self.write(cr, uid, [ref("base.res_partner_2")], {'credit_limit':151000}, context=context)
-
I process follow-up of third activity after set draft.
-
!python {model: marketing.campaign.workitem}: |
ids = self.search(cr, uid, [('segment_id', '=', ref('marketing_campaign_segment0')),
('campaign_id', '=', ref('marketing_campaign_openerppartnerchannel')), ('activity_id', '=', ref('marketing_campaign_activity_2'))])
self.button_draft(cr, uid, ids, context=context)
self.process(cr, uid, ids, context=context)
record = self.browse(cr, uid, ids[0], context=context)
assert record.state == "done", "Follow-up item should be closed after process."
-
I print workitem report.
-
!python {model: marketing.campaign.workitem}: |
ids = self.search(cr, uid, [('segment_id', '=', ref('marketing_campaign_segment0')),
('campaign_id', '=', ref('marketing_campaign_openerppartnerchannel')), ('activity_id', '=', ref('marketing_campaign_activity_2'))])
self.preview(cr, uid, ids)
-
I cancel segmentation because of some activity.
-
!workflow {model: marketing.campaign.segment, action: state_cancel_set, ref: marketing_campaign_segment0}
-
I check the segmentation is canceled.
-
!assert {model: marketing.campaign.segment, id: marketing_campaign_segment0}:
- state == 'cancelled'
-
I reopen the segmentation.
-
!workflow {model: marketing.campaign.segment, action: state_draft_set, ref: marketing_campaign_segment0}
-
!workflow {model: marketing.campaign.segment, action: state_running_set, ref: marketing_campaign_segment0}
-
I check the segment on Running mode after started.
-
!assert {model: marketing.campaign.segment, id: marketing_campaign_segment0}:
- state == 'running'
-
I close segmentation After completion of all activity.
-
!workflow {model: marketing.campaign.segment, action: state_done_set, ref: marketing_campaign_segment0}
-
I check the segmentation is done.
-
!assert {model: marketing.campaign.segment, id: marketing_campaign_segment0}:
- state == 'done'
-
I close this campaing.
-
!workflow {model: marketing.campaign.segment, action: state_done_set, ref: marketing_campaign_openerppartnerchannel}
-
I check the campaing is done.
-
!assert {model: marketing.campaign.segment, id: marketing_campaign_openerppartnerchannel}:
- state == 'done'
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import test_marketing_campaign_flow
@@ -0,0 +1,115 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.tests import common
class TestMarketingCampaignFlow(common.TransactionCase):
def test_00_marketing_campaign_flow(self):
Workitem = self.env['marketing.campaign.workitem']
# In order to test process of compaign, I start compaign.
partner_channel = self.env.ref('marketing_campaign.marketing_campaign_openerppartnerchannel')
partner_channel.signal_workflow('state_running_set')
# I check the campaign on Running mode after started.
self.assertEqual(partner_channel.state, 'running', 'The campaign should be on Running mode after having started.')
# I start this segment after assinged campaign.
segment0 = self.env.ref('marketing_campaign.marketing_campaign_segment0')
segment0.signal_workflow('state_running_set')
# I check the segment on Running mode after started.
self.assertEqual(segment0.state, 'running', 'The segment should be on Running mode after having started.')
# I synchronized segment manually to see all step of activity and process covered on this campaign.
self.assertTrue(segment0.date_next_sync, 'Next Synchronization date is not calculated.')
segment0.process_segment()
# I cancel Marketing Workitems.
workitems = Workitem.search([
('segment_id', '=', segment0.id),
('campaign_id', '=', partner_channel.id)
])
workitems.button_cancel()
self.assertTrue(workitems[0].state in ('cancelled', 'done'), 'Marketing Workitem shoud be in cancel state.')
# I set Marketing Workitems in draft state.
workitems.button_draft()
self.assertTrue(workitems[0].state in ('todo', 'done'), 'Marketing Workitem shoud be in draft state.')
# I process follow-up of first activity.
activity0_id = self.ref('marketing_campaign.marketing_campaign_activity_0')
workitems = Workitem.search([
('segment_id', '=', segment0.id),
('campaign_id', '=', partner_channel.id),
('activity_id', '=', activity0_id)
])
self.assertTrue(workitems, 'Follow-up item is not created for first activity.')
self.assertTrue(workitems[0].res_name, 'Resource Name is not defined.')
workitems.process()
self.assertEqual(workitems[0].state, 'done', "Follow-up item should be closed after process.")
# I check follow-up detail of second activity after process of first activity.
activity1_id = self.ref('marketing_campaign.marketing_campaign_activity_1')
workitems = Workitem.search([
('segment_id', '=', segment0.id),
('campaign_id', '=', partner_channel.id),
('activity_id', '=', activity1_id)
])
self.assertTrue(workitems, 'Follow-up item is not created for second activity.')
# Now I increase credit limit of customer
self.env.ref("base.res_partner_2").write({'credit_limit': 41000})
# I process follow-up of second activity after set draft.
workitems.button_draft()
workitems.process()
self.assertEqual(workitems[0].state, 'done', "Follow-up item should be closed after process.")
# I check follow-up detail of third activity after process of second activity.
activity2_id = self.ref('marketing_campaign.marketing_campaign_activity_2')
workitems = Workitem.search([
('segment_id', '=', segment0.id),
('campaign_id', '=', partner_channel.id),
('activity_id', '=', activity2_id)
])
self.assertTrue(workitems, 'Follow-up item is not created for third activity.')
# Now I increase credit limit of customer
self.env.ref("base.res_partner_2").write({'credit_limit': 151000})
# I process follow-up of third activity after set draft.
workitems.button_draft()
workitems.process()
self.assertEqual(workitems[0].state, 'done', "Follow-up item should be closed after process.")
# I print workitem report
workitems.preview()
# I cancel segmentation because of some activity.
segment0.signal_workflow('state_cancel_set')
# I check the segmentation is canceled.
self.assertEqual(segment0.state, 'cancelled', 'Segment should be in cancelled state.')
# I reopen the segmentation.
segment0.signal_workflow('state_draft_set')
segment0.signal_workflow('state_running_set')
# I check the segment on Running mode after started.
self.assertEqual(segment0.state, 'running', 'Segment should be in running state.')
# I close segmentation After completion of all activity.
segment0.signal_workflow('state_done_set')
# I check the segmentation is done.
self.assertEqual(segment0.state, 'done', 'Segment should be in done state.')
# I close this campaing.
partner_channel.signal_workflow('state_done_set')
# I check the campaing is done.
self.assertEqual(partner_channel.state, 'done', 'Campaign should be in done state.')
@@ -1,7 +1,5 @@
<?xml version="1.0"?>
<openerp>
<data>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Marketing Campaign -->
<record id="act_marketing_campaing_segment_opened" model="ir.actions.act_window">
@@ -113,7 +111,7 @@
<diagram string="Campaign Editor">
<node object="marketing.campaign.activity" shape="rectangle:type=='subcampaign'" bgcolor="gray:start==True">
<field name="name"/>
<field name="type"/>
<field name="action_type"/>
<field name="start" invisible="1"/>
<field name="condition" widget="char"/>
</node>
@@ -203,7 +201,7 @@
<label for="sync_last_date"/>
<div>
<field name="sync_last_date" class="oe_inline"/>
<button string="Synchronize Manually" states="running" name="synchroniz" type="object"/>
<button string="Synchronize Manually" states="running" name="process_segment" type="object"/>
</div>
<field name="date_next_sync"/>
</group>
@@ -297,11 +295,11 @@
<field name="keep_if_condition_not_met"/>
</group>
<group>
<field name="type"/>
<field name="email_template_id" attrs="{'required':[('type','=','email')], 'invisible':[('type','!=','email')]}"
<field name="action_type"/>
<field name="email_template_id" attrs="{'required':[('action_type','=','email')], 'invisible':[('action_type','!=','email')]}"
context="{'default_model_id':object_id}"/>
<field name="server_action_id" attrs="{'required':[('type','=','action')],'invisible':[('type','!=','action')]}" domain="[('model_id','=',object_id)]"/>
<field name="report_id" attrs="{'required':[('type','=','report')],'invisible':[('type','!=','report')]}" context="{'object_id':object_id}"/>
<field name="server_action_id" attrs="{'required':[('action_type','=','action')],'invisible':[('action_type','!=','action')]}" domain="[('model_id','=',object_id)]"/>
<field name="report_id" attrs="{'required':[('action_type','=','report')],'invisible':[('action_type','!=','report')]}" context="{'object_id':object_id}"/>
</group>
</group>
<separator string="Previous Activities"/>
@@ -349,7 +347,7 @@
<tree string="Activities">
<field name="name"/>
<field name="condition"/>
<field name="type"/>
<field name="action_type"/>
<field name="start"/>
</tree>
</field>
@@ -453,5 +451,4 @@
<menuitem id="menu_action_marketing_campaign_workitem" parent="report_marketing_campaign" action="action_marketing_campaign_workitem" sequence="3"/>
</data>
</openerp>
</odoo>