[ADD] RECRUITMENT: Mail template setup
This commit is contained in:
@@ -450,8 +450,11 @@ def check_owner(employee, instance):
|
||||
return False
|
||||
|
||||
|
||||
def generate_pdf(template_path, context):
|
||||
html = render_to_string(template_path, context)
|
||||
def generate_pdf(template_path, context, path=True,title=None):
|
||||
html = template_path
|
||||
title = f"""{context.get("employee")}'s payslip for {context.get("range")}.pdf""" if not title else title
|
||||
if path:
|
||||
html = render_to_string(template_path, context)
|
||||
|
||||
result = io.BytesIO()
|
||||
pdf = pisa.pisaDocument(io.BytesIO(html.encode("utf-8")), result)
|
||||
@@ -460,6 +463,6 @@ def generate_pdf(template_path, context):
|
||||
response = HttpResponse(result.getvalue(), content_type="application/pdf")
|
||||
response[
|
||||
"Content-Disposition"
|
||||
] = f'''attachment;filename="{context.get("employee")}'s payslip for {context.get("range")}.pdf"'''
|
||||
] = f'''attachment;filename="{title}"'''
|
||||
return response
|
||||
return None
|
||||
|
||||
@@ -10,6 +10,7 @@ from recruitment.models import (
|
||||
Candidate,
|
||||
RecruitmentSurvey,
|
||||
RecruitmentSurveyAnswer,
|
||||
RecruitmentMailTemplate
|
||||
)
|
||||
|
||||
|
||||
@@ -21,3 +22,4 @@ admin.site.register(Recruitment)
|
||||
admin.site.register(Candidate)
|
||||
admin.site.register(RecruitmentSurveyAnswer)
|
||||
admin.site.register(RecruitmentSurvey)
|
||||
admin.site.register(RecruitmentMailTemplate)
|
||||
|
||||
@@ -33,10 +33,12 @@ from recruitment.models import (
|
||||
StageNote,
|
||||
JobPosition,
|
||||
RecruitmentSurvey,
|
||||
RecruitmentMailTemplate,
|
||||
)
|
||||
from recruitment import widgets
|
||||
from base.methods import reload_queryset
|
||||
|
||||
|
||||
class ModelForm(forms.ModelForm):
|
||||
"""
|
||||
Overriding django default model form to apply some styles
|
||||
@@ -575,3 +577,18 @@ class CandidateExportForm(forms.Form):
|
||||
"joining_date",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
class OfferLetterForm(ModelForm):
|
||||
"""
|
||||
OfferLetterForm
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = RecruitmentMailTemplate
|
||||
fields = "__all__"
|
||||
widgets = {
|
||||
"body": forms.Textarea(
|
||||
attrs={"data-summernote": "", "style": "display:none;"}
|
||||
),
|
||||
}
|
||||
|
||||
@@ -302,6 +302,16 @@ class Candidate(models.Model):
|
||||
],
|
||||
)
|
||||
sequence = models.IntegerField(null=True, default=0)
|
||||
offerletter_status = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
("not_sent", "Not sent"),
|
||||
("waiting", "Waiting Confirmation"),
|
||||
("accepted", "Accepted / Confirmed"),
|
||||
("rejected", "Rejected / Canceled"),
|
||||
],
|
||||
default="not_sent",
|
||||
)
|
||||
objects = HorillaCompanyManager(related_company_field="recruitment_id__company_id")
|
||||
|
||||
def __str__(self):
|
||||
@@ -328,6 +338,28 @@ class Candidate(models.Model):
|
||||
|
||||
return url
|
||||
|
||||
def get_company(self):
|
||||
"""
|
||||
This method is used to return the company
|
||||
"""
|
||||
return getattr(
|
||||
getattr(getattr(self, "recruitment_id", None), "company_id", None),
|
||||
"company",
|
||||
None,
|
||||
)
|
||||
|
||||
def get_job_position(self):
|
||||
"""
|
||||
This method is used to return the job position of the candidate
|
||||
"""
|
||||
return self.job_position_id.job_position
|
||||
|
||||
def get_email(self):
|
||||
"""
|
||||
Return email
|
||||
"""
|
||||
return self.email
|
||||
|
||||
def tracking(self):
|
||||
"""
|
||||
This method is used to return the tracked history of the instance
|
||||
@@ -374,7 +406,9 @@ class StageNote(models.Model):
|
||||
description = models.TextField(verbose_name=_("Description"))
|
||||
stage_id = models.ForeignKey(Stage, on_delete=models.CASCADE)
|
||||
updated_by = models.ForeignKey(Employee, on_delete=models.CASCADE)
|
||||
objects = HorillaCompanyManager(related_company_field="candidate_id__recruitment_id__company_id")
|
||||
objects = HorillaCompanyManager(
|
||||
related_company_field="candidate_id__recruitment_id__company_id"
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.description}"
|
||||
@@ -464,3 +498,14 @@ class RecruitmentSurveyAnswer(models.Model):
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.candidate_id.name}-{self.recruitment_id}"
|
||||
|
||||
|
||||
class RecruitmentMailTemplate(models.Model):
|
||||
title = models.CharField(max_length=25, unique=True)
|
||||
body = models.TextField()
|
||||
company_id = models.ForeignKey(
|
||||
Company, null=True, blank=True, on_delete=models.CASCADE,verbose_name="Company"
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.title
|
||||
|
||||
4
recruitment/templates/offerletter/create_letter.html
Normal file
4
recruitment/templates/offerletter/create_letter.html
Normal file
@@ -0,0 +1,4 @@
|
||||
{% extends "index.html" %}
|
||||
{% block content %}
|
||||
{{form}}
|
||||
{% endblock content %}
|
||||
32
recruitment/templates/offerletter/htmx/form.html
Normal file
32
recruitment/templates/offerletter/htmx/form.html
Normal file
@@ -0,0 +1,32 @@
|
||||
{% if form.instance.id %}
|
||||
<form id="editForm" hx-post="{% url "view-mail-template" form.instance.id %}" hx-target="#viewTemplateModalBody">
|
||||
{% else %}
|
||||
<form id="createForm" hx-post="{% url "create-mail-template" %}" hx-target="#viewTemplateModalBody">
|
||||
{% endif %}
|
||||
<section>
|
||||
<div id="viewTemplateModalContainer">
|
||||
<div class="my-3" id="keyResultCard">
|
||||
<div class="oh-card oh-card--no-shadow oh-card__body">
|
||||
<div class="oh-input__group">
|
||||
<label class="oh-input__label mt-0" for="keyTitle">Title</label>
|
||||
{{ form.title }}
|
||||
</div>
|
||||
<div class="oh-input__group">
|
||||
<label class="oh-input__label mt-2" for="keyTitle">Body</label>
|
||||
{{ form.body }}
|
||||
</div>
|
||||
<div class="oh-input__group">
|
||||
<label class="oh-input__label mt-2" for="keyTitle">Company</label>
|
||||
{{ form.company_id }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<button id="submitFormButton" type="submit" hidden ></button>
|
||||
</form>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
setModalLabel({{form.instance.title}}, "#viewTemplateModalLabel")
|
||||
});
|
||||
</script>
|
||||
4
recruitment/templates/offerletter/view_letter.html
Normal file
4
recruitment/templates/offerletter/view_letter.html
Normal file
@@ -0,0 +1,4 @@
|
||||
{% extends "index.html" %}
|
||||
{% block content %}
|
||||
{{instance.body}}
|
||||
{% endblock content %}
|
||||
75
recruitment/templates/offerletter/view_templates.html
Normal file
75
recruitment/templates/offerletter/view_templates.html
Normal file
@@ -0,0 +1,75 @@
|
||||
{% extends 'index.html' %}
|
||||
{% block content %}
|
||||
<style>
|
||||
.truncated-text {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.truncated-text::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1) 80%);
|
||||
pointer-events: none;
|
||||
/* Allow interaction with the text beneath the overlay */
|
||||
}
|
||||
</style>
|
||||
|
||||
<section class="oh-wrapper oh-main__topbar">
|
||||
<div class="oh-main__titlebar oh-main__titlebar--left oh-d-flex-column--resp oh-mb-3--small">
|
||||
<h1 class="oh-main__titlebar-title fw-bold">Mail Templates</h1>
|
||||
</div>
|
||||
<div class="oh-main__titlebar oh-main__titlebar--right oh-d-flex-column--resp oh-mb-3--small">
|
||||
<a href="#" data-toggle="oh-modal-toggle" data-target="#addTemplateModal" class="oh-btn oh-btn--secondary ml-2"><ion-icon name="add" class="mr-1 md hydrated" role="img" aria-label="add"></ion-icon>Add</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="oh-wrapper oh-faq-cards">
|
||||
{% for template in templates %}
|
||||
<div class="oh-faq-card">
|
||||
<h3 class="oh-faq-card__title d-flex justify-content-between"><span>{{ template.title }}</span> <a href="{% url "delete-mail-template" %}?ids={{template.id}}" class="text-danger" style="cursor: pointer;" onclick="return confirm('Do you want to delete this template?')"><ion-icon name="trash-outline"></ion-icon></a></h3>
|
||||
<p class="oh-faq-card__desc oh-card__footer--border-top">
|
||||
<div style="max-height: 350px;overflow: hidden;" class="truncated-text">{{ template.body|safe }}</div>
|
||||
</p>
|
||||
<a hx-get="{% url 'view-mail-template' template.id %}" hx-target="#viewTemplateModalBody" data-toggle="oh-modal-toggle" data-target="#viewTemplateModal" class="oh-btn oh-btn--secondary oh-btn--block">View Template</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="oh-modal" id="viewTemplateModal" role="dialog" aria-labelledby="viewTemplateModal" aria-hidden="true">
|
||||
<div class="oh-modal__dialog">
|
||||
<div class="oh-modal__dialog-header">
|
||||
<span class="oh-modal__dialog-title" id="viewTemplateModalLabel"></span>
|
||||
<button class="oh-modal__close" aria-label="Close"><ion-icon name="close-outline"></ion-icon></button>
|
||||
</div>
|
||||
<div class="oh-modal__dialog-body" id="viewTemplateModalBody"></div>
|
||||
<div class="oh-modal__dialog-footer">
|
||||
<button type="submit" onclick="$('#submitFormButton')[0].click()" class="oh-btn oh-btn--secondary oh-btn--shadow">Save Preferences</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oh-modal" id="addTemplateModal" role="dialog" aria-labelledby="addTemplateModal" aria-hidden="true">
|
||||
<div class="oh-modal__dialog">
|
||||
<div class="oh-modal__dialog-header">
|
||||
<span class="oh-modal__dialog-title" id="addTemplateModalLabel">Add Template</span>
|
||||
<button class="oh-modal__close" aria-label="Close"><ion-icon name="close-outline"></ion-icon></button>
|
||||
</div>
|
||||
<div class="oh-modal__dialog-body" id="addTemplateModalBody">
|
||||
{% include 'offerletter/htmx/form.html' %}
|
||||
</div>
|
||||
<div class="oh-modal__dialog-footer">
|
||||
<button type="submit" onclick="$('#submitFormButton')[0].click()" class="oh-btn oh-btn--secondary oh-btn--shadow">Save Preferences</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function setModalLabel(label, modalTarget) {
|
||||
$(modalTarget).html(label)
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -1,13 +1,4 @@
|
||||
{% load i18n %}
|
||||
{% if messages %}
|
||||
<div class="oh-alert-container">
|
||||
{% for message in messages %}
|
||||
<div class="oh-alert oh-alert--animated {{message.tags}}">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if recruitment_form.errors %}
|
||||
<!-- form errors -->
|
||||
<div class="oh-wrapper">
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
{% extends 'index.html' %} {% load i18n %} {% block content %} {% load static %} {% load recruitmentfilters %}
|
||||
<div id="messages" class="oh-alert-container">
|
||||
|
||||
</div>
|
||||
<style>
|
||||
.avatars {
|
||||
display: flex;
|
||||
|
||||
@@ -1,39 +1,120 @@
|
||||
{% load i18n %}
|
||||
<div id="ack-message-{{cand.id}}" >
|
||||
</div>
|
||||
<form hx-post='{% url "send-acknowledgement" %} ' class="oh-general__tab-target oh-profile-section" id = 'ack-form-{{cand.id}}' hx-target="#ack-message-{{cand.id}}" hx-replace-all>
|
||||
{% load i18n %}
|
||||
<div id="ack-message-{{cand.id}}">
|
||||
</div>
|
||||
<form hx-post='{% url "send-acknowledgement" %} ' class="oh-general__tab-target oh-profile-section"
|
||||
id='ack-form-{{cand.id}}' hx-target="#ack-message-{{cand.id}}" hx-encoding="multipart/form-data">
|
||||
<input type="hidden" value="{{cand.id}}" name="id">
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="to-email"><h6>{% trans "To" %}</h6></label>
|
||||
<input type="email" value="{{cand.email}}"name='to' required class="oh-input w-100" id="to-email" placeholder="Enter email">
|
||||
<div class="oh-timeoff-modal__profile-content">
|
||||
<div class="oh-profile mb-2">
|
||||
<div class="oh-profile__avatar">
|
||||
<img src="https://ui-avatars.com/api/?name={{cand.name}}&background=random"
|
||||
class="oh-profile__image me-2">
|
||||
</div>
|
||||
<div class="oh-timeoff-modal__profile-info">
|
||||
<span class="oh-timeoff-modal__user fw-bold">{{cand.name}}</span>
|
||||
<span class="oh-timeoff-modal__user m-0" style="font-size: 18px; color: #4d4a4a">
|
||||
{{cand.job_position_id.job_position}} /
|
||||
{{cand.recruitment_id}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="subject"><h6>{% trans "Subject" %}</h6></label>
|
||||
<input type="text" value="Congratulations..."name='subject' class="oh-input w-100" id="subject" placeholder="Subject">
|
||||
<div class="form-group mt-2">
|
||||
<label for="subject">
|
||||
<h6>{% trans "Subject" %}</h6>
|
||||
</label>
|
||||
<input required type="text" placeholder="Congrats..." name='subject' class="oh-input w-100" id="subject"
|
||||
placeholder="Subject">
|
||||
</div>
|
||||
<div class="form-group mt-2">
|
||||
<label for="template">
|
||||
<h6>{% trans "Template" %}</h6>
|
||||
</label>
|
||||
<select name="template" class="w-100 oh-select" id="template">
|
||||
<option value="">----</option>
|
||||
{% for template in templates %}
|
||||
<option value="{{template.id}}">{{template.title}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group mt-2">
|
||||
<label for="body">
|
||||
<h6>{% trans "Message Body" %}</h6>
|
||||
</label>
|
||||
<textarea hidden data-summernote name="body" required class="oh-input w-100" id="body" cols="30"
|
||||
rows="2"></textarea>
|
||||
</div>
|
||||
<div class="form-group mt-2">
|
||||
<label for="template_attachments">
|
||||
<h6>{% trans "Template as Attachment" %}</h6>
|
||||
</label>
|
||||
<select name="template_attachments" class="w-100 oh-select" id="template_attachments" multiple>
|
||||
{% for template in templates %}
|
||||
<option value="{{template.id}}">{{template.title}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group mt-2">
|
||||
<label for="other_attachments">
|
||||
<h6>{% trans "Other Attachments" %}</h6>
|
||||
</label>
|
||||
<input type="file" name="other_attachments" id="other_attachments" multiple style="display: block;">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="body"><h6>{% trans "Message Body" %}</h6></label>
|
||||
<textarea name="body" required class="oh-input w-100" id="body" cols="30" rows="2">
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="modal-footer d-flex flex-row-reverse mt-3">
|
||||
<input type="submit" class="oh-btn oh-btn--secondary submit-send" data-message-id="ack-message-{{cand.id}}" name="submit" id="submit" onclick="sendMail()" value="{% trans 'Send Mail' %}">
|
||||
<input type="submit" class="oh-btn oh-btn--secondary submit-send" data-message-id="ack-message-{{cand.id}}"
|
||||
name="submit" id="submit" onclick="sendMail()" value="{% trans 'Send Mail' %}">
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
button = document.querySelector('.submit-send')
|
||||
button.onclick = function(event) {
|
||||
button = document.querySelector('.submit-send')
|
||||
button.onclick = function (event) {
|
||||
var element = event.target;
|
||||
messageId = $(element).attr('data-message-id');
|
||||
$(`#${messageId}`).append(`
|
||||
<div class="alert alert-primary m-2" role="alert">
|
||||
Processing...
|
||||
var valid = true
|
||||
if (!$("#subject").val().length) {
|
||||
valid=false
|
||||
$(`#messages`).html($(`
|
||||
<div class="oh-alert oh-alert--animated oh-alert--danger" role="alert">
|
||||
The message subject is required
|
||||
</div>
|
||||
`);
|
||||
};
|
||||
</script>
|
||||
|
||||
`));
|
||||
}
|
||||
else if (!$("#body").val().length) {
|
||||
valid=false
|
||||
$(`#messages`).html($(`
|
||||
<div class="oh-alert oh-alert--animated oh-alert--danger" role="alert">
|
||||
The message body is required
|
||||
</div>
|
||||
`));
|
||||
}
|
||||
if (valid) {
|
||||
|
||||
$(`#messages`).html($(`
|
||||
<div class="oh-alert oh-alert--animated oh-alert--info" role="alert">
|
||||
Processing...
|
||||
</div>
|
||||
`));
|
||||
}
|
||||
};
|
||||
$(document).ready(function () {
|
||||
$("#template").change(function (e) {
|
||||
var id = $(this).val();
|
||||
if (id.length) {
|
||||
$.ajax({
|
||||
type: "get",
|
||||
url: `/recruitment/get-template/${id}/`,
|
||||
data: { "candidate_id": "{{cand.id}}" },
|
||||
dataType: "Json",
|
||||
success: function (response) {
|
||||
console.log(response.body);
|
||||
$('#ack-form-{{cand.id}} [name="body"]').html(response.body).change()
|
||||
$('#ack-form-{{cand.id}} [class="note-editable"]').html(response.body)
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,102 @@
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;text-align: justify;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Dear </span><span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">{{ instance.get_full_name }}</span><span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">,</span>
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;text-align: justify;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"> </span>
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;text-align: justify;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Good day!</span>
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;text-align: justify;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"> </span>
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;text-align: justify;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"><br /></span><span style="font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-variant-position: normal; font-size: 12pt; font-family: Calibri, sans-serif; background-color: transparent; vertical-align: baseline; white-space-collapse: preserve;">I am pleased to inform you that you have been chosen to work with our company, </span><span style="font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-variant-position: normal; font-size: 12pt; font-family: Calibri, sans-serif; background-color: transparent; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">{{ instance.get_company }}</span><span style="font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-variant-position: normal; font-size: 12pt; font-family: Calibri, sans-serif; background-color: transparent; vertical-align: baseline; white-space-collapse: preserve;">. For the position of </span><span style="font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-variant-position: normal; font-size: 12pt; font-family: Calibri, sans-serif; background-color: transparent; font-weight: 700; vertical-align: baseline; white-space-collapse: preserve;">{{ instance.get_job_position }}</span><span style="font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-variant-position: normal; font-size: 12pt; font-family: Calibri, sans-serif; background-color: transparent; vertical-align: baseline; white-space-collapse: preserve;">. As a part of our employee policy, we provide free accommodation to all our employees, and hence you as part of your organization are also eligible for the same.</span>
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;text-align: justify;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"> </span>
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;text-align: justify;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Attached hereto is the employment contract with all the required details, your offer letter, and accommodation details. Should you accept this offer, we expect you to revert back to this email within two days upon receipt.</span>
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;text-align: justify;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"> </span>
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;text-align: justify;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Welcome aboard! We look forward to having you on our team. </span>
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;text-align: justify;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"> </span>
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;text-align: justify;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Regards,</span>
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;text-align: justify;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"> </span>
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;text-align: justify;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Regards,</span>
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;text-align: justify;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Adam Luis</span>
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;text-align: justify;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">HR Manager</span><span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">, </span><span style="font-size:12pt;font-family:Calibri,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">{{ self.get_company }}</span>
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p dir="ltr" style="line-height:1.3800000000000001;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;">
|
||||
<br />
|
||||
</p>
|
||||
<p>
|
||||
<span id="docs-internal-guid-ac0edfad-7fff-5812-3bc2-3630dd38a672"><br /></span>
|
||||
</p>
|
||||
@@ -11,6 +11,13 @@ import recruitment.views.actions
|
||||
import recruitment.views.dashboard
|
||||
import recruitment.views.search
|
||||
import recruitment.views.surveys
|
||||
from recruitment.views.mail_templates import (
|
||||
view_letter,
|
||||
view_mail_templates,
|
||||
create_letter,
|
||||
delete_mail_templates,
|
||||
get_template
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path("recruitment-create", views.recruitment, name="recruitment-create"),
|
||||
@@ -132,13 +139,13 @@ urlpatterns = [
|
||||
"candidate-view/<int:cand_id>/",
|
||||
views.candidate_view_individual,
|
||||
name="candidate-view-individual",
|
||||
kwargs={'model':Candidate}
|
||||
kwargs={"model": Candidate},
|
||||
),
|
||||
path(
|
||||
"candidate-update/<int:cand_id>/",
|
||||
views.candidate_update,
|
||||
name="rec-candidate-update",
|
||||
kwargs={'model':Candidate}
|
||||
kwargs={"model": Candidate},
|
||||
),
|
||||
path(
|
||||
"delete-profile-image/<int:obj_id>/",
|
||||
@@ -251,7 +258,15 @@ urlpatterns = [
|
||||
recruitment.views.surveys.single_survey,
|
||||
name="single-survey-view",
|
||||
),
|
||||
path('candidate-select/', views.candidate_select, name='candidate-select'),
|
||||
path('candidate-select-filter/', views.candidate_select_filter, name='candidate-select-filter'),
|
||||
|
||||
path("candidate-select/", views.candidate_select, name="candidate-select"),
|
||||
path(
|
||||
"candidate-select-filter/",
|
||||
views.candidate_select_filter,
|
||||
name="candidate-select-filter",
|
||||
),
|
||||
path("view-mail-templates/", view_mail_templates, name="view-mail-templates"),
|
||||
path("view-mail-template/<int:obj_id>/", view_letter, name="view-mail-template"),
|
||||
path("create-mail-template/", create_letter, name="create-mail-template"),
|
||||
path("delete-mail-template/", delete_mail_templates, name="delete-mail-template"),
|
||||
path("get-template/<int:obj_id>/", get_template, name="get-template"),
|
||||
]
|
||||
|
||||
91
recruitment/views/mail_templates.py
Normal file
91
recruitment/views/mail_templates.py
Normal file
@@ -0,0 +1,91 @@
|
||||
"""
|
||||
offerletter.py
|
||||
|
||||
This module is related offerletter feature in Horilla
|
||||
"""
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
from django.shortcuts import redirect, render
|
||||
from django.contrib import messages
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from horilla.decorators import login_required, permission_required
|
||||
from recruitment.models import Candidate, RecruitmentMailTemplate
|
||||
from recruitment.forms import OfferLetterForm
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("recruitment.view_offerletterdelete")
|
||||
def view_mail_templates(request):
|
||||
"""
|
||||
This method will render template to disply the offerletter templates
|
||||
"""
|
||||
templates = RecruitmentMailTemplate.objects.all()
|
||||
form = OfferLetterForm()
|
||||
return render(
|
||||
request,
|
||||
"offerletter/view_templates.html",
|
||||
{"templates": templates, "form": form},
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("recruitment.change_offerletterdelete")
|
||||
def view_letter(request, obj_id):
|
||||
"""
|
||||
This method is used to display the template/form to edit
|
||||
"""
|
||||
template = RecruitmentMailTemplate.objects.get(id=obj_id)
|
||||
form = OfferLetterForm(instance=template)
|
||||
if request.method == "POST":
|
||||
form = OfferLetterForm(request.POST, instance=template)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
messages.success(request, "Template updated")
|
||||
return HttpResponse("<script>window.location.reload()</script>")
|
||||
|
||||
return render(request, "offerletter/htmx/form.html", {"form": form})
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
@permission_required("recruitment.add_offerletterdelete")
|
||||
def create_letter(request):
|
||||
"""
|
||||
This method is used to create offerletter template
|
||||
"""
|
||||
if request.method == "POST":
|
||||
form = OfferLetterForm(request.POST)
|
||||
if form.is_valid():
|
||||
instance = form.save()
|
||||
instance.save()
|
||||
messages.success(request, "Template created")
|
||||
return HttpResponse("<script>window.location.reload()</script>")
|
||||
return redirect(view_mail_templates)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("recruitment.delete_offerlettertemplate")
|
||||
def delete_mail_templates(request):
|
||||
ids = request.GET.getlist("ids")
|
||||
result = RecruitmentMailTemplate.objects.filter(id__in=ids).delete()
|
||||
return redirect(view_mail_templates)
|
||||
|
||||
|
||||
from django import template
|
||||
|
||||
|
||||
@login_required
|
||||
def get_template(request, obj_id):
|
||||
"""
|
||||
This method is used to return the mail template
|
||||
"""
|
||||
body = RecruitmentMailTemplate.objects.get(id=obj_id).body
|
||||
candidate_id = request.GET.get("candidate_id")
|
||||
if candidate_id:
|
||||
candidate_obj = Candidate.objects.get(id=candidate_id)
|
||||
template_bdy = template.Template(body)
|
||||
context = template.Context(
|
||||
{"instance": candidate_obj, "self": request.user.employee_get}
|
||||
)
|
||||
body = template_bdy.render(context)
|
||||
|
||||
return JsonResponse({"body": body})
|
||||
@@ -137,7 +137,18 @@
|
||||
>{% trans "Recruitment Survey" %}</a
|
||||
>
|
||||
</li>
|
||||
{% endif %} {% if perms.recruitment.view_candidate %}
|
||||
{% endif %}
|
||||
{% if perms.recruitment.view_offerlettertemplate %}
|
||||
<li class="oh-sidebar__submenu-item">
|
||||
<a
|
||||
class="oh-sidebar__submenu-link"
|
||||
href="{% url 'view-mail-templates' %}"
|
||||
class="oh-sidebar__submenu-link"
|
||||
>{% trans "Mail Templates" %}</a
|
||||
>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.recruitment.view_candidate %}
|
||||
<li class="oh-sidebar__submenu-item">
|
||||
<a
|
||||
class="oh-sidebar__submenu-link"
|
||||
|
||||
Reference in New Issue
Block a user