- create new module hr_promote (depend: hr,approvals) \n - customize approval_type (WIP)
125 lines
5.4 KiB
Python
125 lines
5.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
from odoo import api, fields, models, _
|
|
from odoo.fields import Datetime
|
|
|
|
|
|
class MarketingTrace(models.Model):
|
|
_name = 'marketing.trace'
|
|
_description = 'Marketing Trace'
|
|
_order = 'schedule_date DESC, id ASC'
|
|
_rec_name = 'participant_id'
|
|
|
|
participant_id = fields.Many2one(
|
|
'marketing.participant', string='Participant',
|
|
index=True, ondelete='cascade', required=True)
|
|
res_id = fields.Integer(string='Document ID', related='participant_id.res_id', index=True, store=True, readonly=False)
|
|
is_test = fields.Boolean(string='Test Trace', related='participant_id.is_test', index=True, store=True, readonly=True)
|
|
activity_id = fields.Many2one(
|
|
'marketing.activity', string='Activity',
|
|
index=True, ondelete='cascade', required=True)
|
|
activity_type = fields.Selection(related='activity_id.activity_type', readonly=True)
|
|
trigger_type = fields.Selection(related='activity_id.trigger_type', readonly=True)
|
|
|
|
state = fields.Selection([
|
|
('scheduled', 'Scheduled'),
|
|
('processed', 'Processed'),
|
|
('rejected', 'Rejected'),
|
|
('canceled', 'Cancelled'),
|
|
('error', 'Error')
|
|
], default='scheduled', index=True, required=True)
|
|
schedule_date = fields.Datetime()
|
|
state_msg = fields.Char(string='Error message')
|
|
# hierarchy
|
|
parent_id = fields.Many2one('marketing.trace', string='Parent', index=True, ondelete='cascade')
|
|
child_ids = fields.One2many('marketing.trace', 'parent_id', string='Direct child traces')
|
|
# mailing traces
|
|
mailing_trace_ids = fields.One2many('mailing.trace', 'marketing_trace_id', string='Mass mailing statistics')
|
|
mailing_trace_status = fields.Selection(related='mailing_trace_ids.trace_status', readonly=True)
|
|
links_click_datetime = fields.Datetime(compute='_compute_links_click_datetime')
|
|
|
|
@api.depends('mailing_trace_ids')
|
|
def _compute_links_click_datetime(self):
|
|
# necessary, because sometimes mailing_trace_ids aren't available
|
|
# due to failed messages, which prevents `links_click_datetime` to get assigned
|
|
self.links_click_datetime = False
|
|
mailing_trace = self.filtered(lambda x: x.mailing_trace_ids)
|
|
for trace in mailing_trace:
|
|
trace.links_click_datetime = trace.mailing_trace_ids[0].links_click_datetime
|
|
|
|
def participant_action_cancel(self):
|
|
self.action_cancel(message=_('Manually'))
|
|
|
|
def action_cancel(self, message=None):
|
|
values = {'state': 'canceled', 'schedule_date': Datetime.now()}
|
|
if message:
|
|
values['state_msg'] = message
|
|
self.write(values)
|
|
self.mapped('participant_id').check_completed()
|
|
|
|
def action_execute(self):
|
|
self.activity_id.execute_on_traces(self)
|
|
|
|
# DANE: try to make this function to work on batches later
|
|
def process_event(self, action):
|
|
"""Process event coming from customers currently centered on email actions.
|
|
It updates child traces :
|
|
|
|
* opposite actions are canceled, for example mail_not_open when mail_open is triggered;
|
|
* bounced mail cancel all child actions not being mail_bounced;
|
|
|
|
:param string action: see trigger_type field of activity
|
|
"""
|
|
self.ensure_one()
|
|
if self.participant_id.campaign_id.state not in ['draft', 'running']:
|
|
return
|
|
|
|
now = Datetime.from_string(Datetime.now())
|
|
msg = {
|
|
'mail_not_reply': _('Parent activity mail replied'),
|
|
'mail_not_click': _('Parent activity mail clicked'),
|
|
'mail_not_open': _('Parent activity mail opened'),
|
|
'mail_bounce': _('Parent activity mail bounced'),
|
|
}
|
|
|
|
opened_child = self.child_ids.filtered(lambda trace: trace.state == 'scheduled')
|
|
|
|
cron_trigger_dates = set()
|
|
for next_trace in opened_child.filtered(lambda trace: trace.activity_id.trigger_type == action):
|
|
if next_trace.activity_id.interval_number == 0:
|
|
next_trace.write({
|
|
'schedule_date': now,
|
|
})
|
|
next_trace.activity_id.execute_on_traces(next_trace)
|
|
else:
|
|
schedule_date = now + relativedelta(**{
|
|
next_trace.activity_id.interval_type: next_trace.activity_id.interval_number
|
|
})
|
|
next_trace.write({
|
|
'schedule_date': schedule_date,
|
|
})
|
|
cron_trigger_dates.add(schedule_date)
|
|
|
|
if cron_trigger_dates:
|
|
# based on updated activities, we schedule CRON triggers that match the scheduled_dates
|
|
# we use a set to only trigger the CRON once per timeslot event if there are multiple
|
|
# marketing.participants
|
|
cron = self.env.ref('marketing_automation.ir_cron_campaign_execute_activities')
|
|
cron._trigger(cron_trigger_dates)
|
|
|
|
if action in ['mail_reply', 'mail_click', 'mail_open']:
|
|
opposite_trigger = action.replace('_', '_not_')
|
|
opened_child.filtered(
|
|
lambda trace: trace.activity_id.trigger_type == opposite_trigger
|
|
).action_cancel(message=msg[opposite_trigger])
|
|
|
|
elif action == 'mail_bounce':
|
|
opened_child.filtered(
|
|
lambda trace: trace.activity_id.trigger_type != 'mail_bounce'
|
|
).action_cancel(message=msg[action])
|
|
|
|
return True
|