JOD-CHANGES
This commit is contained in:
parent
3bccb97ca3
commit
506e3fd2f4
|
|
@ -1 +1 @@
|
||||||
from . import models
|
from . import models, controllers
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
from odoo import http, _
|
||||||
|
from odoo.http import request
|
||||||
|
from odoo.addons.hr_recruitment_extended.controllers.controllers import website_hr_recruitment_applications
|
||||||
|
from odoo.http import content_disposition
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from odoo18.odoo.tools import misc
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class website_hr_recruitment_applications_extended(website_hr_recruitment_applications):
|
||||||
|
|
||||||
|
@http.route(['/FTPROTECH/JoiningForm/<int:applicant_id>'], type='http', auth="public",
|
||||||
|
website=True)
|
||||||
|
def post_onboarding_form(self, applicant_id, **kwargs):
|
||||||
|
"""Renders the website form for applicants to submit additional details."""
|
||||||
|
applicant = request.env['hr.applicant'].sudo().browse(applicant_id)
|
||||||
|
if not applicant.exists():
|
||||||
|
return request.not_found()
|
||||||
|
if applicant and applicant.send_post_onboarding_form:
|
||||||
|
if applicant.post_onboarding_form_status == 'done':
|
||||||
|
return request.render("hr_recruitment_extended.thank_you_template", {
|
||||||
|
'applicant': applicant
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return request.render("hr_recruitment_extended.post_onboarding_form_template", {
|
||||||
|
'applicant': applicant
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return request.not_found()
|
||||||
|
|
||||||
|
@http.route(['/download/jod/<int:applicant_id>'], type='http', auth="public", cors='*', website=True)
|
||||||
|
def download_jod_form(self, applicant_id, **kwargs):
|
||||||
|
# Get the applicant record
|
||||||
|
applicant = request.env['hr.applicant'].sudo().browse(applicant_id)
|
||||||
|
if not applicant.exists():
|
||||||
|
return f"Error: Applicant with ID {applicant_id} not found"
|
||||||
|
|
||||||
|
# Business logic check
|
||||||
|
if not applicant.send_post_onboarding_form or applicant.post_onboarding_form_status != 'done':
|
||||||
|
return f"Error: Applicant {applicant_id} does not meet the criteria for download"
|
||||||
|
|
||||||
|
# Get the template
|
||||||
|
template = request.env.ref('hr_recruitment_extended.employee_joining_form_template')
|
||||||
|
if not template:
|
||||||
|
return "Error: Template not found"
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Render the template to HTML for debugging
|
||||||
|
html = request.env['ir.qweb']._render(
|
||||||
|
template.id,
|
||||||
|
{
|
||||||
|
'docs': applicant,
|
||||||
|
'doc': applicant,
|
||||||
|
'time': misc.datetime,
|
||||||
|
'user': request.env.user,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Return HTML for debugging
|
||||||
|
return html
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return f"Error rendering template: {str(e)}"
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<template id="emp_joining_form_template">
|
<template id="emp_joining_form_template">
|
||||||
<t t-call="web.external_layout">
|
<t t-call="web.basic_layout">
|
||||||
<main class="page"
|
<main class="page"
|
||||||
style="margin: 0px; padding: 0px; font-size: 16px; font-family: 'Arial', sans-serif;">
|
style="margin: 0px; padding: 0px; font-size: 16px; font-family: 'Arial', sans-serif;">
|
||||||
<t t-foreach="docs" t-as="doc">
|
<t t-foreach="docs" t-as="doc">
|
||||||
|
|
@ -313,4 +313,13 @@
|
||||||
</t>
|
</t>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- <template id="thank_you_template_inherit" inherit_id="hr_recruitment_exteded.thank_you_template" name="Thank You Template Extended">-->
|
||||||
|
<!-- <xpath expr="//div[@class='container mt-5 text-center']" position="inside">-->
|
||||||
|
<!-- <div t-if="applicant.post_onboarding_form_status == 'done'" style="margin-top: 20px;">-->
|
||||||
|
<!-- <a t-att-href="'/download/jod/%s' % applicant.id" class="btn btn-primary">Download JOD</a>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </xpath>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
@ -170,7 +170,9 @@ class website_hr_recruitment_applications(http.Controller):
|
||||||
return request.not_found() # Return 404 if applicant doesn't exist
|
return request.not_found() # Return 404 if applicant doesn't exist
|
||||||
|
|
||||||
if applicant.post_onboarding_form_status == 'done':
|
if applicant.post_onboarding_form_status == 'done':
|
||||||
return request.render("hr_recruitment_extended.thank_you_template")
|
return request.render("hr_recruitment_extended.thank_you_template",{
|
||||||
|
'applicant': applicant
|
||||||
|
})
|
||||||
private_state_id = request.env['res.country.state'].sudo().browse(int(post.get('present_state', 0)))
|
private_state_id = request.env['res.country.state'].sudo().browse(int(post.get('present_state', 0)))
|
||||||
|
|
||||||
permanent_state_id = request.env['res.country.state'].sudo().browse(int(post.get('permanent_state', 0)))
|
permanent_state_id = request.env['res.country.state'].sudo().browse(int(post.get('permanent_state', 0)))
|
||||||
|
|
@ -281,6 +283,19 @@ class website_hr_recruitment_applications(http.Controller):
|
||||||
|
|
||||||
applicant.write(applicant_data)
|
applicant.write(applicant_data)
|
||||||
|
|
||||||
|
template = request.env.ref('hr_recruitment_extended.email_template_post_onboarding_form_user_submit',
|
||||||
|
raise_if_not_found=False)
|
||||||
|
group = request.env.ref('hr.group_hr_manager')
|
||||||
|
users = request.env['res.users'].sudo().search([('groups_id', 'in', group.ids)], order='id')
|
||||||
|
email_values = {
|
||||||
|
'email_from': applicant.email_from,
|
||||||
|
'email_to': 'hr@ftprotech.com',
|
||||||
|
'email_cc': [user.email for user in users if user.email != 'hr@ftprotech.com']
|
||||||
|
}
|
||||||
|
# Use 'with_context' to override the email template fields dynamically
|
||||||
|
template.sudo().send_mail(applicant.id, email_values=email_values,
|
||||||
|
force_send=True)
|
||||||
|
|
||||||
return request.render("hr_recruitment_extended.thank_you_template")
|
return request.render("hr_recruitment_extended.thank_you_template")
|
||||||
|
|
||||||
def safe_date_parse(self,date_str):
|
def safe_date_parse(self,date_str):
|
||||||
|
|
|
||||||
|
|
@ -290,6 +290,58 @@
|
||||||
<field name="auto_delete" eval="True"/>
|
<field name="auto_delete" eval="True"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="email_template_post_onboarding_form_user_submit" model="mail.template">
|
||||||
|
<field name="name">Joining Formalities Submission Notification</field>
|
||||||
|
<field name="model_id" ref="hr_recruitment.model_hr_applicant"/>
|
||||||
|
<field name="email_from">{{ object.email_from }}</field>
|
||||||
|
<field name="email_to">hr@ftprotech.com</field>
|
||||||
|
<field name="subject">{{ object.candidate_id.partner_name or 'Applicant' }} JOD Submission</field>
|
||||||
|
<field name="description">
|
||||||
|
Notification sent by the applicants with joining formalities details.
|
||||||
|
</field>
|
||||||
|
<field name="body_html" type="html">
|
||||||
|
<div style="font-family: Arial, sans-serif; font-size: 14px; color: #333; padding: 20px; line-height: 1.6;">
|
||||||
|
<p>Dear
|
||||||
|
<strong>
|
||||||
|
<t>HR</t>
|
||||||
|
</strong>
|
||||||
|
,
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<t t-set="applicant_name" t-value="object.candidate_id.partner_name or 'Applicant'"/>
|
||||||
|
<t t-if="object.employee_code">
|
||||||
|
<t t-set="employee_code" t-value="object.employee_code"/>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<t t-esc="applicant_name"/> has submitted the Joining Formalities (JOD) Form. Please click the link below to review the details.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<t t-set="base_url"
|
||||||
|
t-value="object.env['ir.config_parameter'].sudo().get_param('web.base.url')"/>
|
||||||
|
<t t-set="form_url"
|
||||||
|
t-value="base_url + '/odoo/hr.applicant/' % object.id"/>
|
||||||
|
|
||||||
|
<p style="text-align: center; margin-top: 20px;">
|
||||||
|
<a t-att-href="form_url" target="_blank"
|
||||||
|
style="background-color: #007bff; color: #fff; padding: 10px 20px; text-decoration: none;
|
||||||
|
font-weight: bold; border-radius: 5px; display: inline-block;">
|
||||||
|
Open Application
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>Best Regards,
|
||||||
|
<br/>
|
||||||
|
<strong>
|
||||||
|
<t t-esc="object.company_id.name or 'HR Team'">HR Team</t>
|
||||||
|
</strong>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
<record id="email_template_post_onboarding_form" model="mail.template">
|
<record id="email_template_post_onboarding_form" model="mail.template">
|
||||||
<field name="name">Joining Formalities Notification</field>
|
<field name="name">Joining Formalities Notification</field>
|
||||||
<field name="model_id" ref="hr_recruitment.model_hr_applicant"/>
|
<field name="model_id" ref="hr_recruitment.model_hr_applicant"/>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<odoo>
|
<odoo>
|
||||||
<odoo>
|
<odoo>
|
||||||
<template id="employee_joining_form_template">
|
<template id="employee_joining_form_template">
|
||||||
<t t-call="web.external_layout">
|
<t t-call="web.basic_layout">
|
||||||
<main class="page"
|
<main class="page"
|
||||||
style="margin: 0px; padding: 0px; font-size: 16px; font-family: 'Arial', sans-serif;">
|
style="margin: 0px; padding: 0px; font-size: 16px; font-family: 'Arial', sans-serif;">
|
||||||
<t t-foreach="docs" t-as="doc">
|
<t t-foreach="docs" t-as="doc">
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ access_hr_job_recruitment_user,access.hr.job.recruitment.user,model_hr_job_recru
|
||||||
access_hr_job_recruitment_manager,access.hr.job.recruitment.manager,model_hr_job_recruitment,hr_recruitment.group_hr_recruitment_user,1,1,1,1
|
access_hr_job_recruitment_manager,access.hr.job.recruitment.manager,model_hr_job_recruitment,hr_recruitment.group_hr_recruitment_user,1,1,1,1
|
||||||
|
|
||||||
hr_recruitment.access_hr_candidate_interviewer,hr.candidate.interviewer,hr_recruitment.model_hr_candidate,hr_recruitment.group_hr_recruitment_interviewer,1,1,1,0
|
hr_recruitment.access_hr_candidate_interviewer,hr.candidate.interviewer,hr_recruitment.model_hr_candidate,hr_recruitment.group_hr_recruitment_interviewer,1,1,1,0
|
||||||
|
access_hr_candidate_hr,hr.candidate.hr,hr_recruitment.model_hr_candidate,hr.group_hr_manager,1,0,0,0
|
||||||
access_candidate_experience,access.candidate.experience.manager,model_candidate_experience,hr_recruitment.group_hr_recruitment_user,1,1,1,1
|
access_candidate_experience,access.candidate.experience.manager,model_candidate_experience,hr_recruitment.group_hr_recruitment_user,1,1,1,1
|
||||||
access_candidate_experience_user,access.candidate.experience.user,model_candidate_experience,base.group_user,1,0,0,0
|
access_candidate_experience_user,access.candidate.experience.user,model_candidate_experience,base.group_user,1,0,0,0
|
||||||
|
|
||||||
|
|
@ -23,10 +23,13 @@ access_employee_recruitment_attachments,employee.recruitment.attachments,model_e
|
||||||
|
|
||||||
hr_recruitment.access_hr_applicant_interviewer,hr.applicant.interviewer,hr_recruitment.model_hr_applicant,hr_recruitment.group_hr_recruitment_interviewer,1,1,1,0
|
hr_recruitment.access_hr_applicant_interviewer,hr.applicant.interviewer,hr_recruitment.model_hr_applicant,hr_recruitment.group_hr_recruitment_interviewer,1,1,1,0
|
||||||
hr_recruitment.access_hr_recruitment_stage_user,hr.recruitment.stage.user,hr_recruitment.model_hr_recruitment_stage,hr_recruitment.group_hr_recruitment_user,1,1,1,0
|
hr_recruitment.access_hr_recruitment_stage_user,hr.recruitment.stage.user,hr_recruitment.model_hr_recruitment_stage,hr_recruitment.group_hr_recruitment_user,1,1,1,0
|
||||||
|
access_hr_recruitment_stage_hr,hr.recruitment.stage.hr,hr_recruitment.model_hr_recruitment_stage,hr.group_hr_manager,1,0,0,0
|
||||||
|
|
||||||
access_application_stage_status,application.stage.status,model_application_stage_status,base.group_user,1,1,1,1
|
access_application_stage_status,application.stage.status,model_application_stage_status,base.group_user,1,1,1,1
|
||||||
|
|
||||||
|
|
||||||
access_ats_invite_mail_template_wizard,ats.invite.mail.template.wizard.user,hr_recruitment_extended.model_ats_invite_mail_template_wizard,,1,1,1,1
|
access_ats_invite_mail_template_wizard,ats.invite.mail.template.wizard.user,hr_recruitment_extended.model_ats_invite_mail_template_wizard,,1,1,1,1
|
||||||
access_client_submission_mails_template_wizard,client.submission.mails.template.wizard.user,hr_recruitment_extended.model_client_submission_mails_template_wizard,,1,1,1,1
|
access_client_submission_mails_template_wizard,client.submission.mails.template.wizard.user,hr_recruitment_extended.model_client_submission_mails_template_wizard,,1,1,1,1
|
||||||
|
access_hr_application_public,hr.applicant.public.access,hr_recruitment.model_hr_applicant,base.group_public,1,0,0,0
|
||||||
|
access_hr_application_group_hr,hr.applicant.hr.access,hr_recruitment.model_hr_applicant,hr.group_hr_manager,1,1,0,0
|
||||||
|
,,,,,,,
|
||||||
|
|
|
@ -465,6 +465,13 @@
|
||||||
<label>Permanent Address
|
<label>Permanent Address
|
||||||
<span class="text-danger">*</span>
|
<span class="text-danger">*</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<!-- Checkbox to toggle same address -->
|
||||||
|
<div class="form-check d-inline-block ml-3">
|
||||||
|
<input type="checkbox" class="form-check-input permanent-address-checkbox" id="same_as_present"/>
|
||||||
|
<label class="form-check-label" for="same_as_present">Same as Present Address</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
</h5>
|
</h5>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<input type="text" class="form-control mb-2" name="permanent_street"
|
<input type="text" class="form-control mb-2" name="permanent_street"
|
||||||
|
|
@ -1716,6 +1723,80 @@
|
||||||
let prevButtons = document.querySelectorAll(".prev-step");
|
let prevButtons = document.querySelectorAll(".prev-step");
|
||||||
let currentStep = 0;
|
let currentStep = 0;
|
||||||
|
|
||||||
|
// Same as Present Address functionality
|
||||||
|
const sameAsPresentCheckbox = document.getElementById('same_as_present');
|
||||||
|
const form = document.getElementById('post_onboarding_form');
|
||||||
|
|
||||||
|
// Add event listener to the checkbox
|
||||||
|
sameAsPresentCheckbox.addEventListener('change', function() {
|
||||||
|
if (this.checked) {
|
||||||
|
// Get present address elements by name (since they don't have IDs)
|
||||||
|
const presentStreet = form.querySelector('input[name="present_street"]');
|
||||||
|
const presentStreet2 = form.querySelector('input[name="present_street2"]');
|
||||||
|
const presentCity = form.querySelector('input[name="present_city"]');
|
||||||
|
const presentZip = form.querySelector('input[name="present_zip"]');
|
||||||
|
|
||||||
|
// Get permanent address elements by name
|
||||||
|
const permanentStreet = form.querySelector('input[name="permanent_street"]');
|
||||||
|
const permanentStreet2 = form.querySelector('input[name="permanent_street2"]');
|
||||||
|
const permanentCity = form.querySelector('input[name="permanent_city"]');
|
||||||
|
const permanentZip = form.querySelector('input[name="permanent_zip"]');
|
||||||
|
|
||||||
|
// Copy values only if both elements exist
|
||||||
|
if (permanentStreet && presentStreet) permanentStreet.value = presentStreet.value;
|
||||||
|
if (permanentStreet2 && presentStreet2) permanentStreet2.value = presentStreet2.value;
|
||||||
|
if (permanentCity && presentCity) permanentCity.value = presentCity.value;
|
||||||
|
if (permanentZip && presentZip) permanentZip.value = presentZip.value;
|
||||||
|
|
||||||
|
// Handle state field
|
||||||
|
const presentStateContainer = document.getElementById('present_state_ids_container');
|
||||||
|
const permanentStateContainer = document.getElementById('permanent_state_ids_container');
|
||||||
|
|
||||||
|
// If state dropdowns exist, copy the selected value
|
||||||
|
const presentStateSelect = presentStateContainer ? presentStateContainer.querySelector('select') : null;
|
||||||
|
const permanentStateSelect = permanentStateContainer ? permanentStateContainer.querySelector('select') : null;
|
||||||
|
|
||||||
|
if (presentStateSelect && permanentStateSelect) {
|
||||||
|
permanentStateSelect.value = presentStateSelect.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make permanent address fields readonly
|
||||||
|
[permanentStreet, permanentStreet2, permanentCity, permanentZip].forEach(field => {
|
||||||
|
if (field) {
|
||||||
|
field.readOnly = true;
|
||||||
|
field.classList.add('bg-light');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (permanentStateSelect) {
|
||||||
|
permanentStateSelect.disabled = true;
|
||||||
|
permanentStateSelect.classList.add('bg-light');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Make permanent address fields editable but keep the values
|
||||||
|
const permanentStreet = form.querySelector('input[name="permanent_street"]');
|
||||||
|
const permanentStreet2 = form.querySelector('input[name="permanent_street2"]');
|
||||||
|
const permanentCity = form.querySelector('input[name="permanent_city"]');
|
||||||
|
const permanentZip = form.querySelector('input[name="permanent_zip"]');
|
||||||
|
|
||||||
|
[permanentStreet, permanentStreet2, permanentCity, permanentZip].forEach(field => {
|
||||||
|
if (field) {
|
||||||
|
field.readOnly = false;
|
||||||
|
field.classList.remove('bg-light');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const permanentStateContainer = document.getElementById('permanent_state_ids_container');
|
||||||
|
const permanentStateSelect = permanentStateContainer ? permanentStateContainer.querySelector('select') : null;
|
||||||
|
if (permanentStateSelect) {
|
||||||
|
permanentStateSelect.disabled = false;
|
||||||
|
permanentStateSelect.classList.remove('bg-light');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function updateSteps() {
|
function updateSteps() {
|
||||||
// Show/hide step content
|
// Show/hide step content
|
||||||
steps.forEach((step, index) => {
|
steps.forEach((step, index) => {
|
||||||
|
|
@ -2149,6 +2230,7 @@
|
||||||
<h2>Thank You for Your Submission</h2>
|
<h2>Thank You for Your Submission</h2>
|
||||||
<p>Your form has been successfully submitted.</p>
|
<p>Your form has been successfully submitted.</p>
|
||||||
<a href="/" class="btn btn-primary">Go Back to Home</a>
|
<a href="/" class="btn btn-primary">Go Back to Home</a>
|
||||||
|
<a t-if="applicant and applicant.post_onboarding_form_status == 'done'" t-att-href="'/download/jod/%s' % applicant.id" class="btn btn-primary">Download JOD</a>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue