[UPDT] HORILLA AUTOMATIONS: Add also sent_to field in mail automation

This commit is contained in:
Horilla
2024-12-09 11:12:17 +05:30
parent dc1311cf70
commit 5d6a7c79c6
10 changed files with 128 additions and 37 deletions

View File

@@ -5,10 +5,15 @@ horilla_automations/forms.py
from typing import Any
from django import forms
from django.utils.translation import gettext_lazy as _
from base.forms import ModelForm
from employee.filters import EmployeeFilter
from employee.models import Employee
from horilla_automations.methods.methods import generate_choices
from horilla_automations.models import MODEL_CHOICES, MailAutomation
from horilla_widgets.widgets.horilla_multi_select_field import HorillaMultiSelectField
from horilla_widgets.widgets.select_widgets import HorillaMultiSelectWidget
class AutomationForm(ModelForm):
@@ -23,6 +28,19 @@ class AutomationForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["also_sent_to"] = HorillaMultiSelectField(
queryset=Employee.objects.all(),
required=False,
widget=HorillaMultiSelectWidget(
filter_route_name="employee-widget-filter",
filter_class=EmployeeFilter,
filter_instance_contex_name="f",
filter_template_path="employee_filters.html",
instance=self.instance,
),
label="Also Sent to",
help_text=_("The employees selected here will receive the email as Cc."),
)
if not self.data:
mail_to = []
@@ -57,6 +75,18 @@ class AutomationForm(ModelForm):
model = MailAutomation
fields = "__all__"
def clean(self):
cleaned_data = super().clean()
if isinstance(self.fields["also_sent_to"], HorillaMultiSelectField):
self.errors.pop("also_sent_to", None)
employee_data = self.fields["also_sent_to"].queryset.filter(
id__in=self.data.getlist("also_sent_to")
)
cleaned_data["also_sent_to"] = employee_data
return cleaned_data
def save(self, commit: bool = ...) -> Any:
self.instance: MailAutomation = self.instance
condition_querystring = self.cleaned_data["condition_querystring"]

View File

@@ -4,6 +4,7 @@ from django.utils.translation import gettext_lazy as _trans
from base.methods import eval_validate
from base.models import HorillaMailTemplate
from employee.models import Employee
from horilla.models import HorillaModel
from horilla_views.cbv_methods import render_template
@@ -36,13 +37,20 @@ class MailAutomation(HorillaModel):
mail_to = models.TextField(verbose_name="Mail to")
mail_details = models.CharField(
max_length=250,
help_text="Fill mail template details(reciever/instance, `self` will be the person who trigger the automation)",
help_text=_trans(
"Fill mail template details(reciever/instance, `self` will be the person who trigger the automation)"
),
)
mail_detail_choice = models.TextField(default="", editable=False)
trigger = models.CharField(max_length=10, choices=choices)
# udpate the on_update logic to if and only if when
# changes in the previous and current value
mail_template = models.ForeignKey(HorillaMailTemplate, on_delete=models.CASCADE)
also_sent_to = models.ManyToManyField(
Employee,
blank=True,
verbose_name=_trans("Also Send to"),
)
template_attachments = models.ManyToManyField(
HorillaMailTemplate,
related_name="template_attachment",
@@ -89,6 +97,12 @@ class MailAutomation(HorillaModel):
"horilla_automations/mail_to.html", {"instance": self, "mappings": mappings}
)
def get_mail_cc_display(self):
employees = self.also_sent_to.all()
return render_template(
"horilla_automations/mail_cc.html", {"employees": employees}
)
def detailed_url(self):
return reverse("automation-detailed-view", kwargs={"pk": self.pk})

View File

@@ -40,12 +40,22 @@ def start_automation():
"""
Automation signals
"""
from base.models import HorillaMailTemplate
from horilla_automations.methods.methods import get_model_class, split_query_string
from horilla_automations.models import MailAutomation
@receiver(post_delete, sender=MailAutomation)
@receiver(post_save, sender=MailAutomation)
def automation_pre_create(sender, instance, **kwargs):
def automation_signal(sender, instance, **kwargs):
"""
signal method to handle automation post save
"""
start_connection()
track_previous_instance()
@receiver(post_delete, sender=HorillaMailTemplate)
@receiver(post_save, sender=HorillaMailTemplate)
def template_signal(sender, instance, **kwargs):
"""
signal method to handle automation post save
"""
@@ -81,10 +91,10 @@ def start_automation():
)
previous_bulk_record = getattr(_thread_locals, "previous_bulk_record", None)
previous_queryset = None
previous_queryset_copy = []
if previous_bulk_record:
previous_queryset = previous_bulk_record["queryset"]
previous_queryset_copy = previous_bulk_record["queryset_copy"]
previous_queryset = previous_bulk_record.get("queryset", None)
previous_queryset_copy = previous_bulk_record.get("queryset_copy", [])
bulk_thread = threading.Thread(
target=_bulk_update_thread_handler,
@@ -355,6 +365,19 @@ def send_mail(request, automation, instance):
tos = list(filter(None, tos))
to = tos[:1]
cc = tos[1:]
try:
also_sent_to = automation.also_sent_to.select_related(
"employee_work_info"
).all()
if also_sent_to.exists():
cc.extend(
str(employee.get_mail())
for employee in also_sent_to
if employee.get_mail()
)
except Exception as e:
logger.error(e)
email_backend = ConfiguredEmailBackend()
display_email_name = email_backend.dynamic_from_email_with_display_name
if request:
@@ -384,7 +407,9 @@ def send_mail(request, automation, instance):
)
template_bdy = template.Template(mail_template.body)
context = template.Context({"instance": mail_to_instance, "self": sender})
context = template.Context(
{"instance": mail_to_instance, "self": sender, "model_instance": instance}
)
render_bdy = template_bdy.render(context)
title_template = template.Template(automation.title)

View File

@@ -1,32 +1,39 @@
<div id="formContainer">
{% include "generic/horilla_form.html" %}
{% include "generic/horilla_form.html" %}
</div>
<script>
$("#{{view_id}} form button").click(function (e) {
const form = document.getElementById('multipleConditionForm');
const elements = form.elements;
const queryString = Array.from(elements)
.filter(element => element.name && !element.disabled)
.map(element => encodeURIComponent(element.name) + '=' + encodeURIComponent(element.value))
.join('&');
$("#{{view_id}} form [name=condition_querystring]").val(queryString);
html = $(".note-editable #multipleConditionForm").html()
$("#{{view_id}} form [name=condition_html]").val(html);
});
$("#dynamic_field_condition").parent().removeClass("col-md-6");
// summernote
$("#dynamic_field_condition textarea")
.summernote({
height: 100,
toolbar: false,
})
.summernote("code", getHtml());
{% if form.instance.pk %}
$("#id_mail_to").val({{form.instance.mail_to|safe}}).change()
$("#dynamic_field_condition .note-editable").html($("<form id='multipleConditionForm'></form>"));
$("#dynamic_field_condition .note-editable #multipleConditionForm").html($(`{{form.instance.condition_html|safe}}`))
$(".note-editable select").parent().find("span.select2").remove()
$(".note-editable select").parent().find(".select2-hidden-accessible").removeClass("select2-hidden-accessible")
$(".note-editable select").parent().find("select").select2();
{% endif %}
$(document).on('click', '.oh-accordion-header', function (event) {
$(this).closest('.oh-accordion').toggleClass('oh-accordion--show');
});
$("#{{view_id}} form button").click(function (e) {
const form = document.getElementById('multipleConditionForm');
const elements = form.elements;
const queryString = Array.from(elements)
.filter(element => element.name && !element.disabled)
.map(element => encodeURIComponent(element.name) + '=' + encodeURIComponent(element.value))
.join('&');
$("#{{view_id}} form [name=condition_querystring]").val(queryString);
html = $(".note-editable #multipleConditionForm").html()
$("#{{view_id}} form [name=condition_html]").val(html);
});
$("#dynamic_field_condition").parent().removeClass("col-md-6");
// summernote
$("#dynamic_field_condition textarea")
.summernote({
height: 100,
toolbar: false,
})
.summernote("code", getHtml());
{% if form.instance.pk %}
$("#id_mail_to").val({{ form.instance.mail_to | safe }}).change()
$("#dynamic_field_condition .note-editable").html($("<form id='multipleConditionForm'></form>"));
$("#dynamic_field_condition .note-editable #multipleConditionForm").html($(`{{form.instance.condition_html|safe}}`))
$(".note-editable select").parent().find("span.select2").remove()
$(".note-editable select").parent().find(".select2-hidden-accessible").removeClass("select2-hidden-accessible")
$(".note-editable select").parent().find("select").select2();
{% endif %}
</script>

View File

@@ -0,0 +1,10 @@
{% load i18n %}
{% if employees %}
<ol>
{% for employee in employees %}
<li>{{employee}}</li>
{% endfor %}
</ol>
{% else %}
{% trans "Not Added" %}
{% endif %}

View File

@@ -162,6 +162,7 @@ class AutomationDetailedView(views.HorillaDetailedView):
("Model", "model"),
("Mail Templates", "mail_template"),
("Mail To", "get_mail_to_display"),
("Mail Cc", "get_mail_cc_display"),
("Trigger", "trigger_display"),
]
actions = [

View File

@@ -10,6 +10,7 @@ from django import forms
from horilla_widgets.widgets.horilla_multi_select_field import HorillaMultiSelectField
orginal_template_name = forms.Select.option_template_name
forms.Select.option_template_name = "horilla_widgets/horilla_select_option.html"

View File

@@ -118,7 +118,7 @@
.select2{
width: 100% !important;
}
#slectContainer{{self.attrs.id}} .select2-container .select2-selection{
#selectContainer{{self.attrs.id}} .select2-container .select2-selection{
padding: 5px !important;
max-height: 70px !important;
overflow: hidden;
@@ -138,7 +138,7 @@
border-radius: 6px;
}
</style>
<div id="slectContainer{{self.attrs.id}}">
<div id="selectContainer{{self.attrs.id}}">
<select name="{{field_name}}" id="{{self.attrs.id}}" {% if required %}required{% endif %} class="w-100 oh-select oh-select2" multiple>
{% for instance in queryset %}
<option value="{{instance.id}}">{{instance}}</option>

View File

@@ -200,7 +200,7 @@
});
});
{% if initial %}
$('#{{section_id}} #slectContainer{{self.attrs.id}}').find("[name={{field_name}}]").val({{initial|safe}}).change()
$('#{{section_id}} #selectContainer{{self.attrs.id}}').find("[name={{field_name}}]").val({{initial|safe}}).change()
{% endif %}
});
$("#{{section_id}} #choose-all-user").click(function (e) {

View File

@@ -36,6 +36,7 @@ class HorillaMultiSelectWidget(forms.Widget):
instance=None,
required=False,
form=None,
help_text=None,
**kwargs
) -> None:
self.filter_route_name = filter_route_name
@@ -45,6 +46,7 @@ class HorillaMultiSelectWidget(forms.Widget):
self.filter_template_path = filter_template_path
self.instance = instance
self.form = form
self.help_text = help_text
super().__init__()
template_name = "horilla_widgets/horilla_multiselect_widget.html"
@@ -73,6 +75,7 @@ class HorillaMultiSelectWidget(forms.Widget):
context["filter_template_path"] = self.filter_template_path
context["filter_route_name"] = self.filter_route_name
context["required"] = self.required
context["help_text"] = self.help_text
self.attrs["id"] = (
("id_" + name) if self.attrs.get("id") is None else self.attrs.get("id")
)