import warnings from datetime import datetime from dateutil.relativedelta import relativedelta from operator import itemgetter from werkzeug.urls import url_encode from odoo import http, _ from odoo.addons.website_hr_recruitment.controllers.main import WebsiteHrRecruitment from odoo.osv.expression import AND from odoo.http import request from odoo.tools import email_normalize from odoo.tools.misc import groupby import base64 from odoo.exceptions import UserError from PIL import Image from io import BytesIO import re import json class website_hr_recruitment_applications(http.Controller): @http.route(['/hr_recruitment/second_application_form/'], type='http', auth="public", website=True) def second_application_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_second_application_form: if applicant.second_application_form_status == 'done': return request.render("hr_recruitment_extended.thank_you_template") else: return request.render("hr_recruitment_extended.applicant_form_template", { 'applicant': applicant }) else: return request.not_found() @http.route(['/hr_recruitment/submit_second_application//submit'], type='http', auth="public", methods=['POST'], website=True, csrf=False) def process_application_form(self, applicant_id, **kwargs): # Get the applicant candidate_image_base64 = kwargs.pop('candidate_image_base64') candidate_image = kwargs.pop('candidate_image') experience_years = kwargs.pop('experience_years', 0) experience_months = kwargs.pop('experience_months', 0) if not len(str(experience_months)) > 0: experience_months = 0 if not len(str(experience_years)) > 0: experience_years = 0 # If there are months, convert everything to months if int(experience_months) > 0: kwargs['total_exp'] = (int(experience_years) * 12) + int(experience_months) kwargs['total_exp_type'] = 'month' else: kwargs['total_exp'] = int(experience_years) kwargs['total_exp_type'] = 'year' applicant = request.env['hr.applicant'].sudo().browse(applicant_id) if not applicant.exists(): return request.not_found() # Return 404 if applicant doesn't exist if applicant.second_application_form_status == 'done': return request.render("hr_recruitment_extended.thank_you_template") kwargs['candidate_image'] = candidate_image_base64 applicant.write(kwargs) applicant.candidate_id.candidate_image = candidate_image_base64 template = request.env.ref('hr_recruitment_extended.email_template_second_application_submitted', raise_if_not_found=False) if template and applicant.user_id.email: template.sudo().send_mail(applicant.id, force_send=True) applicant.second_application_form_status = 'done' # Redirect to a Thank You page return request.render("hr_recruitment_extended.thank_you_template") @http.route(['/FTPROTECH/DocRequests/'], type='http', auth="public", website=True) def doc_request_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: if applicant.doc_requests_form_status == 'done': return request.render("hr_recruitment_extended.thank_you_template") else: return request.render("hr_recruitment_extended.doc_request_form_template", { 'applicant': applicant }) else: return request.not_found() @http.route(['/FTPROTECH/submit//docRequest'], type='http', auth="public", methods=['POST'], website=True, csrf=False) def process_applicant_doc_submission_form(self, applicant_id, **post): applicant = request.env['hr.applicant'].sudo().browse(applicant_id) if not applicant.exists(): return request.not_found() # Return 404 if applicant doesn't exist if applicant.doc_requests_form_status == 'done': return request.render("hr_recruitment_extended.thank_you_template") applicant_data = { 'applicant_id': int(post.get('applicant_id', 0)), 'candidate_image': post.get('candidate_image_base64', ''), 'doc_requests_form_status': 'done' } applicant_data = {k: v for k, v in applicant_data.items() if v != '' and v != 0} # attachments attachments_data_json = post.get('attachments_data_json', '[]') attachments_data = json.loads(attachments_data_json) if attachments_data_json else [] if attachments_data: applicant_data['joining_attachment_ids'] = [ (4, existing_id) for existing_id in (applicant.joining_attachment_ids).ids ] + [ (0, 0, { 'name': attachment.get('file_name', ''), 'recruitment_attachment_id': attachment.get( 'attachment_rec_id', ''), 'file': attachment.get('file_content', '') }) for attachment in attachments_data if attachment.get('attachment_rec_id') ] applicant.write(applicant_data) return request.render("hr_recruitment_extended.thank_you_template") @http.route(['/FTPROTECH/JoiningForm/'], 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") else: return request.render("hr_recruitment_extended.post_onboarding_form_template", { 'applicant': applicant }) else: return request.not_found() @http.route(['/FTPROTECH/submit//JoinForm'], type='http', auth="public", methods=['POST'], website=True, csrf=False) def process_employee_joining_form(self,applicant_id,**post): applicant = request.env['hr.applicant'].sudo().browse(applicant_id) if not applicant.exists(): return request.not_found() # Return 404 if applicant doesn't exist if applicant.post_onboarding_form_status == 'done': 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))) permanent_state_id = request.env['res.country.state'].sudo().browse(int(post.get('permanent_state', 0))) applicant_data = { 'applicant_id': int(post.get('applicant_id', 0)), 'employee_id': int(post.get('employee_id', 0)), 'candidate_image': post.get('candidate_image_base64', ''), 'doj': datetime.strptime(post.get('doj'), '%Y-%m-%d').date() if post.get('doj', None) else '', 'email_from': post.get('email_from', ''), 'gender': post.get('gender', ''), 'partner_phone': post.get('partner_phone', ''), 'alternate_phone': post.get('alternate_phone', ''), 'birthday': post.get('birthday', ''), 'blood_group': post.get('blood_group', ''), 'private_street': post.get('present_street', ''), 'private_street2': post.get('present_street2', ''), 'private_city': post.get('present_city', ''), 'private_state_id': private_state_id.id if private_state_id else '', 'private_country_id': private_state_id.country_id.id if private_state_id else '', 'private_zip': post.get('present_zip', ''), 'permanent_street': post.get('permanent_street', ''), 'permanent_street2': post.get('permanent_street2', ''), 'permanent_city': post.get('permanent_city', ''), 'permanent_state_id': permanent_state_id.id if permanent_state_id else '', 'permanent_country_id': permanent_state_id.country_id.id if permanent_state_id else '', 'permanent_zip': post.get('permanent_zip', ''), 'marital': post.get('marital', ''), 'marriage_anniversary_date': post.get('marriage_anniversary_date', ''), 'full_name_as_in_bank': post.get('full_name_as_in_bank', ''), 'bank_name': post.get('bank_name', ''), 'bank_branch': post.get('bank_branch', ''), 'bank_account_no': post.get('bank_account_no', ''), 'bank_ifsc_code': post.get('bank_ifsc_code', ''), 'passport_no': post.get('passport_no', ''), 'passport_start_date': datetime.strptime(post.get('passport_start_date'), '%Y-%m-%d').date() if post.get('passport_start_date', None) else '', 'passport_end_date': datetime.strptime(post.get('passport_end_date'), '%Y-%m-%d').date() if post.get('passport_end_date', None) else '', 'passport_issued_location': post.get('passport_issued_location', ''), 'pan_no': post.get('pan_no', ''), 'identification_id': post.get('identification_id', ''), 'previous_company_pf_no': post.get('previous_company_pf_no', ''), 'previous_company_uan_no': post.get('previous_company_uan_no', ''), 'post_onboarding_form_status': 'done' } applicant_data = {k: v for k, v in applicant_data.items() if v != '' and v != 0} # Get family details data from JSON family_data_json = post.get('family_data_json', '[]') family_data = json.loads(family_data_json) if family_data_json else [] if family_data: applicant_data['family_details'] = [ (0, 0, { 'relation_type': member.get('relation', ''), 'name': str(member.get('name', '')), 'contact_no': member.get('contact', ''), 'dob': datetime.strptime(member.get('dob'), '%Y-%m-%d').date() if member.get('dob') else None, 'location': member.get('location', ''), }) for member in family_data if member.get('name') and member.get('relation') # Optional filter to avoid empty members ] # education details education_data_json = post.get('education_data_json', '[]') education_data = json.loads(education_data_json) if education_data_json else [] if education_data: applicant_data['education_history'] = [ (0,0,{ 'education_type': education.get('education_type',''), 'name': education.get('specialization', ''), 'university': education.get('university', ''), 'start_year': education.get('start_year', ''), 'end_year': education.get('end_year', ''), 'marks_or_grade': education.get('marks_or_grade','') }) for education in education_data ] employer_history_data_json = post.get('employer_history_data_json', '[]') employer_data = json.loads(employer_history_data_json) if employer_history_data_json else [] if employer_data: applicant_data['employer_history'] = [ (0, 0, { 'company_name': company.get('company_name', ''), 'designation': company.get('designation', ''), 'date_of_joining': self.safe_date_parse(company.get('date_of_joining')), 'last_working_day': self.safe_date_parse(company.get('last_working_day')), 'ctc': company.get('ctc', ''), }) for company in employer_data ] #attachments attachments_data_json = post.get('attachments_data_json', '[]') attachments_data = json.loads(attachments_data_json) if attachments_data_json else [] if attachments_data: applicant_data['joining_attachment_ids'] = [ (4, existing_id) for existing_id in (applicant.joining_attachment_ids).ids ] + [ (0, 0, { 'name': attachment.get('file_name', ''), 'recruitment_attachment_id': attachment.get( 'attachment_rec_id', ''), 'file': attachment.get('file_content', '') }) for attachment in attachments_data if attachment.get('attachment_rec_id') ] 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' and user.employee_id.department_id.name == 'Human Resource'] } # 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",{ 'applicant': applicant }) def safe_date_parse(self,date_str): try: return datetime.strptime(date_str, '%Y-%m-%d').date() if date_str else None except (ValueError, TypeError): return None @http.route('/hr_recruitment_extended/fetch_related_state_ids', type='json', auth="public", website=True) def fetch_preferred_state_ids(self, country_id=None): state_ids = {} states = http.request.env['res.country.state'].sudo() if country_id: for state_id in states.search([('country_id', '=?', country_id)]): state_ids[state_id.id] = state_id.name return state_ids