diff --git a/addons/marketing/marketing_view.xml b/addons/marketing/marketing_view.xml index 78f5a742bb6..7d8a46a8a92 100644 --- a/addons/marketing/marketing_view.xml +++ b/addons/marketing/marketing_view.xml @@ -31,6 +31,7 @@ + diff --git a/addons/marketing/marketing_wizard.py b/addons/marketing/marketing_wizard.py index aee302283b2..8eb1d1a01f4 100644 --- a/addons/marketing/marketing_wizard.py +++ b/addons/marketing/marketing_wizard.py @@ -28,6 +28,8 @@ class marketing_installer(osv.osv_memory): # Generic modules 'marketing_campaign':fields.boolean('Marketing Campaigns', help="Helps you to manage marketing campaigns and automate actions and communication steps."), + 'marketing_campaign_mailchimp':fields.boolean('Mailchimp Integration', + help="This modules integrate mailchimp.com's service with OpenERP to automate mass mailings."), 'crm_profiling':fields.boolean('Profiling Tools', help="Helps you to perform segmentation within partners and design questionaires.") } diff --git a/addons/marketing_campaign/marketing_campaign.py b/addons/marketing_campaign/marketing_campaign.py index 3f29e03c968..a23bb0e42d1 100755 --- a/addons/marketing_campaign/marketing_campaign.py +++ b/addons/marketing_campaign/marketing_campaign.py @@ -25,7 +25,7 @@ from dateutil.relativedelta import relativedelta from osv import fields, osv import netsvc -import tools +import tools _intervalTypes = { 'hours': lambda interval: relativedelta(hours=interval), @@ -37,10 +37,10 @@ _intervalTypes = { class marketing_campaign(osv.osv): #{{{ _name = "marketing.campaign" _description = "Marketing Campaign" - + _columns = { 'name': fields.char('Name', size=64, required=True), - 'object_id': fields.many2one('ir.model', 'Objects', required=True, + 'object_id': fields.many2one('ir.model', 'Object', required=True, help="Choose the Object on which you want \ this campaign to be run"), 'mode':fields.selection([('test', 'Test'), @@ -52,17 +52,15 @@ class marketing_campaign(osv.osv): #{{{ ('running', 'Running'), ('done', 'Done'), ('cancelled', 'Cancelled'),], - 'State',), - 'activity_ids': fields.one2many('marketing.campaign.activity', + 'State',), + 'activity_ids': fields.one2many('marketing.campaign.activity', 'campaign_id', 'Activities'), 'fixed_cost': fields.float('Fixed Cost', help="The fixed cost is cost\ - you required for the campaign"), - + you required for the campaign"), } - _defaults = { 'state': lambda *a: 'draft', - 'mode': lambda *a: 'test', + 'mode': lambda *a: 'test', } def state_running_set(self, cr, uid, ids, *args): @@ -79,16 +77,16 @@ class marketing_campaign(osv.osv): #{{{ ('state', '=', 'draft')]) self.write(cr, uid, ids, {'state': 'running'}) return True - + def state_done_set(self, cr, uid, ids, *args): segment_ids = self.pool.get('marketing.campaign.segment').search(cr, uid, [('campaign_id', 'in', ids), ('state', '=', 'running')]) if segment_ids : - raise osv.except_osv("Error", "Camapign cannot be done before all segments are done") + raise osv.except_osv("Error", "Camapign cannot be done before all segments are done") self.write(cr, uid, ids, {'state': 'done'}) return True - + def state_cancel_set(self, cr, uid, ids, *args): self.write(cr, uid, ids, {'state': 'cancelled'}) return True @@ -100,7 +98,7 @@ class marketing_campaign_segment(osv.osv): #{{{ _columns = { 'name': fields.char('Name', size=64,required=True), - 'campaign_id': fields.many2one('marketing.campaign', 'Campaign', + 'campaign_id': fields.many2one('marketing.campaign', 'Campaign', required=True), 'object_id': fields.related('campaign_id','object_id', type='many2one', relation='ir.model', @@ -114,22 +112,22 @@ class marketing_campaign_segment(osv.osv): #{{{ ('running', 'Running'), ('done', 'Done'), ('cancelled', 'Cancelled')], - 'State',), + 'State',), 'date_run': fields.datetime('Running'), 'date_done': fields.datetime('Done'), } - + _defaults = { 'state': lambda *a: 'draft', 'sync_mode': lambda *a: 'create_date', } - + def state_running_set(self, cr, uid, ids, *args): segment = self.browse(cr, uid, ids[0]) curr_date = time.strftime('%Y-%m-%d %H:%M:%S') vals = {'state': 'running'} if not segment.date_run: - vals['date_run'] = time.strftime('%Y-%m-%d %H:%M:%S') + vals['date_run'] = time.strftime('%Y-%m-%d %H:%M:%S') if not segment.sync_last_date: vals['sync_last_date']=curr_date if not segment.date_done: @@ -137,9 +135,9 @@ class marketing_campaign_segment(osv.osv): #{{{ ).strftime('%Y-%m-%d %H:%M:%S') self.write(cr, uid, ids, vals) return True - + def state_done_set(self, cr, uid, ids, *args): - date_done = self.browse(cr, uid, ids[0]).date_done + date_done = self.browse(cr, uid, ids[0]).date_done if (date_done > time.strftime('%Y-%m-%d %H:%M:%S')): raise osv.except_osv("Error", "Segment cannot be closed before end date") @@ -147,21 +145,21 @@ class marketing_campaign_segment(osv.osv): #{{{ [('state', 'in', ['inprogress', 'todo']), ('segment_id', '=', ids[0])]) if wi_ids : - raise osv.except_osv("Error", "Segment cannot be done before all workitems are processed") + raise osv.except_osv("Error", "Segment cannot be done before all workitems are processed") self.write(cr, uid, ids, {'state': 'done'}) return True - + def state_cancel_set(self, cr, uid, ids, *args): self.write(cr, uid, ids, {'state': 'cancelled'}) return True - + def process_segment(self, cr, uid, context={}): segment_ids = self.search(cr, uid, [('state', '=', 'running')]) action_date = time.strftime('%Y-%m-%d %H:%M:%S') last_action_date = (datetime.now() + _intervalTypes['days'](-1) \ ).strftime('%Y-%m-%d %H:%M:%S') for segment in self.browse(cr, uid, segment_ids): - act_ids = self.pool.get('marketing.campaign.activity').search(cr, + act_ids = self.pool.get('marketing.campaign.activity').search(cr, uid, [('start', '=', True), ('campaign_id', '=', segment.campaign_id.id)]) if (segment.sync_last_date and \ @@ -195,23 +193,23 @@ class marketing_campaign_activity(osv.osv): #{{{ _columns = { 'name': fields.char('Name', size=128, required=True), - 'campaign_id': fields.many2one('marketing.campaign', 'Campaign', + 'campaign_id': fields.many2one('marketing.campaign', 'Campaign', required = True, ondelete='cascade'), 'object_id': fields.related('campaign_id','object_id', type='many2one', relation='ir.model', string='Object'), 'start': fields.boolean('Start',help= "Its necessary to start activity \ before running campaign"), - 'condition': fields.char('Condition', size=256, required=True, + 'condition': fields.char('Condition', size=256, required=True, help="Condition that is to be tested before \ - action is executed"), + action is executed"), 'type': fields.selection([('email', 'E-mail'), ('paper', 'Paper'), ('action', 'Action'), ('subcampaign', 'Sub-Campaign')], 'Type', required=True), 'email_template_id': fields.many2one('email.template','Email Template'), - 'report_id': fields.many2one('ir.actions.report.xml', 'Reports', ), + 'report_id': fields.many2one('ir.actions.report.xml', 'Reports', ), 'report_directory_id': fields.many2one('document.directory','Directory', help="Folder is used to store the generated reports"), 'server_action_id': fields.many2one('ir.actions.server', string='Action', @@ -222,7 +220,7 @@ class marketing_campaign_activity(osv.osv): #{{{ 'Next Activities'), 'from_ids': fields.one2many('marketing.campaign.transition', 'activity_to_id', - 'Previous Activities'), + 'Previous Activities'), 'subcampaign_id': fields.many2one('marketing.campaign', 'Sub-Campaign'), 'subcampaign_segment_id': fields.many2one('marketing.campaign.segment', 'Sub Campaign Segment'), @@ -236,7 +234,7 @@ class marketing_campaign_activity(osv.osv): #{{{ 'object_id' : lambda obj, cr, uid, context : context.get('object_id',False), } - def search(self, cr, uid, args, offset=0, limit=None, order=None, + def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False): if context == None: context = {} @@ -247,14 +245,14 @@ class marketing_campaign_activity(osv.osv): #{{{ for activity in segment_obj.campaign_id.activity_ids: act_ids.append(activity.id) return act_ids - return super(marketing_campaign_activity, self).search(cr, uid, args, + return super(marketing_campaign_activity, self).search(cr, uid, args, offset, limit, order, context, count) def process(self, cr, uid, act_id, wi_id, context={}): activity = self.browse(cr, uid, act_id) workitem_obj = self.pool.get('marketing.campaign.workitem') workitem = workitem_obj.browse(cr, uid, wi_id) - + if activity.type == 'paper' : service = netsvc.LocalService('report.%s'%activity.report_id.report_name) (report_data, format) = service.create(cr, uid, [], {}, {}) @@ -298,7 +296,7 @@ class marketing_campaign_activity(osv.osv): #{{{ server_obj = self.pool.get('ir.actions.server') server_obj.run(cr, uid, [activity.server_action_id.id], context) #??? - return True + return True marketing_campaign_activity()#}}} class marketing_campaign_transition(osv.osv): #{{{ @@ -309,10 +307,10 @@ class marketing_campaign_transition(osv.osv): #{{{ _columns = { 'activity_from_id': fields.many2one('marketing.campaign.activity', 'Source Activity'), - 'activity_to_id': fields.many2one('marketing.campaign.activity', + 'activity_to_id': fields.many2one('marketing.campaign.activity', 'Destination Activity'), 'interval_nbr': fields.integer('Interval No.'), - 'interval_type': fields.selection([('hours', 'Hours'), ('days', 'Days'), + 'interval_type': fields.selection([('hours', 'Hours'), ('days', 'Days'), ('months', 'Months'), ('years','Years')],'Interval Type') } @@ -323,32 +321,46 @@ class marketing_campaign_transition(osv.osv): #{{{ if context.has_key('type_id'): value[context['type_id']] = context['activity_id'] return value - + marketing_campaign_transition() #}}} class marketing_campaign_workitem(osv.osv): #{{{ _name = "marketing.campaign.workitem" _description = "Campaign Workitem" + def _res_name_get(self, cr, uid, ids, field_name, arg, context=None): + res = {} + for obj in self.browse(cr, uid, ids, context=context): + if obj.res_id: + try: + res[obj.id] = self.pool.get(obj.object_id.model).name_get(cr, uid, [obj.res_id], context=context)[0][1] + except: + res[obj.id] = '/' + else: + res[obj.id] = '/' + return res + _columns = { 'segment_id': fields.many2one('marketing.campaign.segment', 'Segment', - required=True), + required=True), 'activity_id': fields.many2one('marketing.campaign.activity','Activity', - required=True), - 'object_id': fields.related('segment_id', 'campaign_id', 'object_id', - type='many2one', relation='ir.model', - string='Object'), - 'res_id': fields.integer('Results'), + required=True), + 'campaign_id': fields.related('segment_id', 'campaign_id', + type='many2one', relation='marketing.campaign', string='Campaign', readonly=True), + 'object_id': fields.related('segment_id', 'campaign_id', 'object_id', + type='many2one', relation='ir.model', string='Object'), + 'res_id': fields.integer('Resource ID'), + 'res_name': fields.function(_res_name_get, method=True, string='Resource Name', type="char", size=64), 'date': fields.datetime('Execution Date'), 'partner_id': fields.many2one('res.partner', 'Partner',required=True), - 'state': fields.selection([('todo', 'ToDo'), ('inprogress', 'In Progress'), + 'state': fields.selection([('todo', 'ToDo'), ('inprogress', 'In Progress'), ('exception', 'Exception'), ('done', 'Done'), ('cancelled', 'Cancelled')], 'State'), 'error_msg' : fields.text('Error Message') - } + } _defaults = { - 'state': lambda *a: 'draft', + 'state': lambda *a: 'todo', } def process_chain(self, cr, uid, workitem_id, context={}): @@ -369,11 +381,17 @@ class marketing_campaign_workitem(osv.osv): #{{{ 'state': 'todo', } self.create(cr, uid, workitem_vals) - + + def button_cancel(self, cr, uid, workitem_ids, context={}): + for wi in self.browse(cr, uid, workitem_ids): + if wi.state in ('todo','exception'): + self.write(cr, uid, [wi.id], {'state':'cancelled'}, context=context) + return True + def process(self, cr, uid, workitem_ids, context={}): for wi in self.browse(cr, uid, workitem_ids): - if wi.state == 'todo':# we searched the wi which are in todo state - #then y v keep this filter again + if wi.state == 'todo':# we searched the wi which are in todo state + #then y v keep this filter again eval_context = { 'pool': self.pool, 'cr': cr, @@ -390,45 +408,44 @@ class marketing_campaign_workitem(osv.osv): #{{{ if res : self.write(cr, uid, wi.id, {'state': 'done'}) self.process_chain(cr, uid, wi.id, context) - else : + else : self.write(cr, uid, wi.id, {'state': 'exception', 'error_msg': res['error_msg']}) except Exception,e: self.write(cr, uid, wi.id, {'state': 'exception'}) else : self.write(cr, uid, wi.id, {'state': 'cancelled'}) - + return True - + def process_all(self, cr, uid, context={}): workitem_ids = self.search(cr, uid, [('state', '=', 'todo'), ('date','<=', time.strftime('%Y-%m-%d %H:%M:%S'))]) if workitem_ids: self.process(cr, uid, workitem_ids, context) - -marketing_campaign_workitem() #}}} + +marketing_campaign_workitem() #}}} class email_template(osv.osv): _inherit = "email.template" - _defaults = { 'object_name': lambda obj, cr, uid, context: context.get('object_id',False), } -email_template() +email_template() class report_xml(osv.osv): _inherit = 'ir.actions.report.xml' - + def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False): if not context: context = {} if context and 'object_id' in context and context['object_id']: - model = self.pool.get('ir.model').browse(cr, uid, + model = self.pool.get('ir.model').browse(cr, uid, context['object_id']).model args.append(('model', '=', model)) return super(report_xml, self).search(cr, uid, args, offset, limit, order, context, count) - -report_xml() + +report_xml() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/marketing_campaign/marketing_campaign_view.xml b/addons/marketing_campaign/marketing_campaign_view.xml index 3e2d7330291..8f0e6dcfcd6 100755 --- a/addons/marketing_campaign/marketing_campaign_view.xml +++ b/addons/marketing_campaign/marketing_campaign_view.xml @@ -14,75 +14,67 @@ form
- + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - + - - - - - @@ -105,7 +97,6 @@ - @@ -122,13 +113,13 @@ - - + + - - + + @@ -183,21 +174,23 @@ form
- - - + + - - - + + + + + + + + + - - - - +