1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/sale_subscription/models/sale_order_alert.py
2024-12-10 09:04:09 +07:00

312 lines
15 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from ast import literal_eval
from odoo import api, Command, fields, models, _
from odoo.addons.sale.models.sale_order import SALE_ORDER_STATE
class BaseAutomation(models.Model):
_inherit = 'base.automation'
is_sale_order_alert = fields.Boolean(readonly=True, default=False, string='Is Sale Order Alert')
class SaleOrderAlert(models.Model):
_name = 'sale.order.alert'
_description = 'Sale Order Alert'
_inherits = {'base.automation': 'automation_id'}
_check_company_auto = True
@api.model
def default_get(self, default_fields):
res = super().default_get(default_fields)
if 'model_id' in default_fields:
# model_id default cannot be specified at field level
# because model_id is an inherited field from base.automation
res['model_id'] = self.env['ir.model']._get_id('sale.order')
return res
automation_id = fields.Many2one('base.automation', 'Automation Rule', required=True, ondelete='restrict')
action_id = fields.Many2one('ir.actions.server', string='Server Action', ondelete='restrict')
template_id = fields.Many2one(related='action_id.template_id', readonly=False)
sms_template_id = fields.Many2one(related='action_id.sms_template_id', readonly=False)
activity_type_id = fields.Many2one(related='action_id.activity_type_id', readonly=False)
activity_summary = fields.Char(related='action_id.activity_summary', readonly=False)
activity_note = fields.Html(related='action_id.activity_note', readonly=False)
activity_date_deadline_range = fields.Integer(related='action_id.activity_date_deadline_range', readonly=False)
activity_date_deadline_range_type = fields.Selection(related='action_id.activity_date_deadline_range_type', readonly=False)
activity_user_id = fields.Many2one(related='action_id.activity_user_id', readonly=False)
action = fields.Selection([
('next_activity', 'Create next activity'),
('mail_post', 'Send an email to the customer'),
('sms', 'Send an SMS Text Message to the customer'),
('set_health_value', 'Set Contract Health value')
], string='Action To Do', required=True, default=None)
trigger_condition = fields.Selection([
('on_create_or_write', 'Modification'), ('on_time', 'Timed Condition')], string='Trigger On', required=True, default='on_create_or_write')
currency_id = fields.Many2one('res.currency', string='Currency', default=lambda self: self.env.company.currency_id)
subscription_plan_ids = fields.Many2many('sale.subscription.plan', string='Subscription Plans', check_company=True)
customer_ids = fields.Many2many('res.partner', string='Customers')
company_id = fields.Many2one('res.company', string='Company')
mrr_min = fields.Monetary('MRR Range Min', currency_field='currency_id')
team_ids = fields.Many2many('crm.team', string='Sales Team')
mrr_max = fields.Monetary('MRR Range Max', currency_field='currency_id')
product_ids = fields.Many2many(
'product.product', string='Specific Products',
check_company=True,
domain="[('product_tmpl_id.recurring_invoice', '=', True)]")
mrr_change_amount = fields.Float('MRR Change Amount')
mrr_change_unit = fields.Selection(selection='_get_selection_mrr_change_unit', string='MRR Change Unit', default='percentage')
mrr_change_period = fields.Selection([('1month', '1 Month'), ('3months', '3 Months')], string='MRR Change Period',
default='1month', help="Period over which the KPI is calculated")
rating_percentage = fields.Integer('Rating Percentage', help="Rating Satisfaction is the ratio of positive rating to total number of rating.")
rating_operator = fields.Selection([('>', 'greater than'), ('<', 'less than')], string='Rating Operator', default='>')
subscription_state_from = fields.Selection(
string='Stage from',
selection=[
('1_draft', 'Quotation'), # Quotation for a new subscription
('3_progress', 'In Progress'), # Active Subscription or confirmed renewal for active subscription
('6_churn', 'Churned'), # Closed or ended subscription
('2_renewal', 'Renewal Quotation'), # Renewal Quotation for existing subscription
('5_renewed', 'Renewed'), # Active or ended subscription that has been renewd
('4_paused', 'Paused'), # Active subcription with paused invoicing
('7_upsell', 'Upsell'), # Quotation or SO upselling a subscription
]
)
subscription_state = fields.Selection(
string='Stage',
selection=[
('1_draft', 'Quotation'), # Quotation for a new subscription
('3_progress', 'In Progress'), # Active Subscription or confirmed renewal for active subscription
('6_churn', 'Churned'), # Closed or ended subscription
('2_renewal', 'Renewal Quotation'), # Renewal Quotation for existing subscription
('5_renewed', 'Renewed'), # Active or ended subscription that has been renewd
('4_paused', 'Paused'), # Active subcription with paused invoicing
('7_upsell', 'Upsell'), # Quotation or SO upselling a subscription
]
)
order_state = fields.Selection(selection=SALE_ORDER_STATE, string="Status")
activity_user = fields.Selection([
('contract', 'Subscription Salesperson'),
('channel_leader', 'Sales Team Leader'),
('users', 'Specific Users'),
], string='Assign To')
activity_user_ids = fields.Many2many('res.users', string='Specific Users')
subscription_count = fields.Integer(compute='_compute_subscription_count')
cron_nextcall = fields.Datetime(compute='_compute_nextcall', store=False)
health = fields.Selection([('normal', 'Neutral'), ('done', 'Good'), ('bad', 'Bad')], string="Health", help="Show the health status")
@api.onchange('trigger_condition', 'automation_id')
def _onchange_automation_trigger(self):
# This method is needed to force saving the automation_id.trigger according to trigger_condition value
# Overriding create/write does is not sufficient anymore as the automation_id.trg_date_range_type is
# evaluated before calling super/write.
for alert in self:
alert.automation_id.trigger = alert.trigger_condition
def _get_action_activity_values(self):
if self.activity_user == 'users':
action_commands = [Command.create({
'name': '%s-%s' % (self.name, seq),
'sequence': seq,
'state': 'next_activity',
'model_id': self.model_id.id,
'activity_summary': self.activity_summary,
'activity_type_id': self.activity_type_id.id,
'activity_note': self.activity_note,
'activity_date_deadline_range': self.activity_date_deadline_range,
'activity_date_deadline_range_type': self.activity_date_deadline_range_type,
'activity_user_type': 'specific',
'activity_user_id': user.id,
'usage': 'base_automation',
}) for seq, user in enumerate(self.activity_user_ids, 1)]
return {
'state': 'multi',
'child_ids': action_commands
}
elif self.activity_user == 'contract':
return {
'state': 'next_activity',
'activity_user_type': 'generic',
'activity_user_field_name': 'user_id',
}
elif self.activity_user == 'channel_leader':
return {
'state': 'next_activity',
'activity_user_type': 'generic',
'activity_user_field_name': 'team_user_id',
}
def _get_alert_domain(self):
domain = [('is_subscription', '=', True)]
if self.subscription_plan_ids:
domain += [('plan_id', 'in', self.subscription_plan_ids.ids)]
if self.customer_ids:
domain += [('partner_id', 'in', self.customer_ids.ids)]
if self.team_ids:
domain += [('team_id', 'in', self.team_ids.ids)]
if self.company_id:
domain += [('company_id', '=', self.company_id.id)]
if self.mrr_min:
domain += [('recurring_monthly', '>=', self.mrr_min)]
if self.mrr_max:
domain += [('recurring_monthly', '<=', self.mrr_max)]
if self.product_ids:
domain += [('order_line.product_id', 'in', self.product_ids.ids)]
if self.mrr_change_amount:
if self.mrr_change_unit == 'percentage':
domain += [('kpi_%s_mrr_percentage' % self.mrr_change_period, '>', self.mrr_change_amount / 100)]
else:
domain += [('kpi_%s_mrr_delta' % self.mrr_change_period, '>', self.mrr_change_amount)]
if self.rating_percentage:
domain += [('percentage_satisfaction', self.rating_operator, self.rating_percentage)]
if self.subscription_state:
domain += [('subscription_state', '=', self.subscription_state)]
elif self.subscription_state_from:
domain += [('subscription_state', '!=', self.subscription_state_from)]
if self.order_state:
domain += [('state', '=', self.order_state)]
return domain
def _get_selection_mrr_change_unit(self):
return [('percentage', '%'), ('currency', self.env.company.currency_id.symbol)]
def _compute_subscription_count(self):
for alert in self:
domain = literal_eval(alert.filter_domain) if alert.filter_domain else []
alert.subscription_count = self.env['sale.order'].search_count(domain)
def _get_action_template_values(self):
self.ensure_one()
if self.action == 'mail_post':
return {'template_id': self.template_id.id}
elif self.action == 'sms':
return {'sms_template_id': self.sms_template_id.id}
elif self.action == 'next_activity':
return {
'activity_type_id': self.activity_type_id and self.activity_type_id.id,
'activity_summary': self.activity_summary,
'activity_note': self.activity_note,
'activity_date_deadline_range': self.activity_date_deadline_range,
'activity_date_deadline_range_type': self.activity_date_deadline_range_type,
'activity_user_id': self.activity_user_id and self.activity_user_id.id,
}
return {}
def _create_actions(self):
action_values = [{
'name': alert.name,
'usage': 'base_automation',
'model_id': alert.model_id.id,
'base_automation_id': alert.automation_id.id,
**alert._get_action_template_values()
} for alert in self]
actions = self.env['ir.actions.server'].create(action_values)
for alert, action in zip(self, actions):
alert.action_id = action
alert.action_server_ids = [action.id]
def _configure_alerts(self, vals_list):
# Unlink the children server actions if not needed anymore
self.filtered(lambda alert: alert.action != 'next_activity' and alert.action_id.child_ids).action_id.child_ids.unlink()
field_names = ['subscription_state', 'health']
tag_fields = self.env['ir.model.fields'].search([('model', 'in', self.mapped('model_name')), ('name', 'in', field_names)])
for alert, vals in zip(self, vals_list):
# Configure alert
alert_values = {}
if not vals.get('filter_domain'):
alert_values['filter_domain'] = alert._get_alert_domain()
if not vals.get('filter_pre_domain'):
if alert.subscription_state_from:
alert_values['filter_pre_domain'] = [('subscription_state', '=', alert.subscription_state_from)]
elif alert.subscription_state:
alert_values['filter_pre_domain'] = [('subscription_state', '!=', alert.subscription_state)]
else:
alert_values['filter_pre_domain'] = []
if alert_values:
alert.with_context(skip_configure_alerts=True).write(alert_values)
# Configure action
action_values = {}
field_name = None
if alert.action == 'subscription_state' and alert.subscription_state:
field_name = 'subscription_state'
action_values['selection_value'] = alert.subscription_state
elif alert.action == 'set_health_value' and alert.health:
field_name = 'health'
action_values['value'] = alert.health
if field_name:
tag_field = tag_fields.filtered(lambda t: t.name == field_name)
action_values['state'] = 'object_write'
action_values['update_path'] = tag_field.name
action_values['evaluation_type'] = 'value'
elif vals.get('action') in ('mail_post', 'sms'):
action_values['state'] = vals['action']
elif vals.get('action') == 'next_activity' or vals.get('activity_user_ids') or vals.get('activity_user'):
self.action_id.child_ids.unlink()
action_values = alert._get_action_activity_values()
if action_values:
alert.action_id.write(action_values)
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
vals['is_sale_order_alert'] = True
if vals.get('trigger_condition'):
vals['trigger'] = vals['trigger_condition']
alerts = super().create(vals_list)
alerts._create_actions()
alerts._configure_alerts(vals_list)
return alerts
def write(self, vals):
if vals.get('trigger_condition'):
vals['trigger'] = vals['trigger_condition']
res = super().write(vals)
if not self._context.get('skip_configure_alerts'):
self._configure_alerts([vals])
return res
def unlink(self):
self.automation_id.active = False
return super().unlink()
def action_view_subscriptions(self):
self.ensure_one()
domain = literal_eval(self.filter_domain) if self.filter_domain else [('is_subscription', '=', True)]
return {
'type': 'ir.actions.act_window',
'name': _('Subscriptions'),
'res_model': 'sale.order',
'view_mode': 'kanban,tree,form,pivot,graph,cohort,activity',
'domain': domain,
'context': {'create': False},
}
def run_cron_manually(self):
self.ensure_one()
domain = literal_eval(self.filter_domain)
subs = self.env['sale.order'].search(domain)
ctx = {
'active_model': 'sale.order',
'active_ids': subs.ids,
'domain_post': domain,
}
for action_server in self.action_server_ids.with_context(**ctx):
action_server.run()
def _compute_nextcall(self):
cron = self.env.ref('sale_subscription.ir_cron_sale_subscription_update_kpi', raise_if_not_found=False)
self.cron_nextcall = cron.nextcall if cron else False