Odoo18-Base/extra-addons/marketing_automation/models/marketing_participant.py
hoangvv b8024171a2 - add modules(marketing automation + approvals + webstudio) \n
- create new module hr_promote (depend: hr,approvals) \n
- customize approval_type (WIP)
2025-01-17 07:32:51 +07:00

141 lines
5.9 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
from odoo.osv.expression import NEGATIVE_TERM_OPERATORS
class MarketingParticipant(models.Model):
_name = 'marketing.participant'
_description = 'Marketing Participant'
_order = 'id ASC'
_rec_name = 'resource_ref'
@api.model
def default_get(self, default_fields):
defaults = super(MarketingParticipant, self).default_get(default_fields)
if 'res_id' in default_fields and not defaults.get('res_id'):
model_name = defaults.get('model_name')
if not model_name and defaults.get('campaign_id'):
model_name = self.env['marketing.campaign'].browse(defaults['campaign_id']).model_name
if model_name and model_name in self.env:
resource = self.env[model_name].search([], limit=1)
defaults['res_id'] = resource.id
return defaults
@api.model
def _selection_target_model(self):
models = self.env['ir.model'].sudo().search([('is_mail_thread', '=', True)])
return [(model.model, model.name) for model in models]
def _search_resource_ref(self, operator, value):
ir_models = set([model['model_name'] for model in self.env['marketing.campaign'].search([]).read(['model_name'])])
ir_model_ids = []
for model in ir_models:
if model in self.env:
ir_model_ids += self.env['marketing.participant'].search(['&', ('model_name', '=', model), ('res_id', 'in', [name[0] for name in self.env[model].name_search(name=value)])]).ids
operator = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in'
return [('id', operator, ir_model_ids)]
campaign_id = fields.Many2one(
'marketing.campaign', string='Campaign',
index=True, ondelete='cascade', required=True)
model_id = fields.Many2one(
'ir.model', string='Model', related='campaign_id.model_id',
index=True, readonly=True, store=True)
model_name = fields.Char(
string='Record model', related='campaign_id.model_id.model',
readonly=True, store=True)
res_id = fields.Integer(string='Record ID', index=True)
resource_ref = fields.Reference(
string='Record', selection='_selection_target_model',
compute='_compute_resource_ref', inverse='_set_resource_ref', search='_search_resource_ref')
trace_ids = fields.One2many('marketing.trace', 'participant_id', string='Actions')
state = fields.Selection([
('running', 'Running'),
('completed', 'Completed'),
('unlinked', 'Removed'),
], default='running', index=True, required=True,
help='Removed means the related record does not exist anymore.')
is_test = fields.Boolean('Test Record', default=False)
@api.depends('model_name', 'res_id')
def _compute_resource_ref(self):
for participant in self:
if participant.model_name and participant.model_name in self.env:
participant.resource_ref = '%s,%s' % (participant.model_name, participant.res_id or 0)
else:
participant.resource_ref = None
def _set_resource_ref(self):
for participant in self:
if participant.resource_ref:
participant.res_id = participant.resource_ref.id
def check_completed(self):
existing_traces = self.env['marketing.trace'].search([
('participant_id', 'in', self.ids),
('state', '=', 'scheduled'),
])
(self - existing_traces.mapped('participant_id')).write({'state': 'completed'})
@api.model_create_multi
def create(self, vals_list):
participants = super().create(vals_list)
now = Datetime.now()
cron_trigger_dates = set()
for res in participants:
# prepare first traces related to begin activities
primary_activities = res.campaign_id.marketing_activity_ids.filtered(lambda act: act.trigger_type == 'begin')
trace_ids = [
(0, 0, {
'activity_id': activity.id,
'schedule_date': now + relativedelta(**{activity.interval_type: activity.interval_number}),
}) for activity in primary_activities]
res.write({'trace_ids': trace_ids})
cron_trigger_dates |= set([
now + relativedelta(**{activity.interval_type: activity.interval_number})
for activity in primary_activities
])
if cron_trigger_dates:
# based on activities with 'begin' trigger_type, we schedule CRON triggers
# that match the scheduled_dates of created marketing.traces
# 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)
return participants
def action_set_completed(self):
''' Manually mark as a completed and cancel every scheduled trace '''
# TDE TODO: delegate set Canceled to trace record
self.write({'state': 'completed'})
self.env['marketing.trace'].search([
('participant_id', 'in', self.ids),
('state', '=', 'scheduled')
]).write({
'state': 'canceled',
'schedule_date': Datetime.now(),
'state_msg': _('Marked as completed')
})
def action_set_running(self):
self.write({'state': 'running'})
def action_set_unlink(self):
self.write({'state': 'unlinked'})
self.env['marketing.trace'].search([
('participant_id', 'in', self.ids),
('state', '=', 'scheduled')
]).write({
'state': 'canceled',
'state_msg': _('Record deleted'),
})
return True