From f2cb45443de5bdcd994a7443cee5c85b8b324a0f Mon Sep 17 00:00:00 2001 From: Pranay Date: Thu, 17 Apr 2025 12:20:46 +0530 Subject: [PATCH] ATS CHANGES --- .../models/approval_required_docs.py | 10 +++ .../models/hr_applicant.py | 42 ++++++++- .../models/hr_job_recruitment.py | 76 ++++++++-------- .../models/hr_recruitment.py | 17 ++++ .../views/hr_applicant_views.xml | 89 ++++++++++++++++++- .../hr_employee_education_employer_family.xml | 18 ++-- .../views/hr_job_recruitment.xml | 28 ++++++ .../views/hr_recruitment.xml | 34 ++++++- .../wizards/applicant_refuse_reason.py | 2 +- .../post_onboarding_attachment_wizard.py | 8 ++ 10 files changed, 269 insertions(+), 55 deletions(-) create mode 100644 addons_extensions/hr_recruitment_extended/models/approval_required_docs.py diff --git a/addons_extensions/hr_recruitment_extended/models/approval_required_docs.py b/addons_extensions/hr_recruitment_extended/models/approval_required_docs.py new file mode 100644 index 000000000..1d719d84c --- /dev/null +++ b/addons_extensions/hr_recruitment_extended/models/approval_required_docs.py @@ -0,0 +1,10 @@ +from odoo import models, fields, api, _ +from odoo.exceptions import ValidationError +from datetime import date +from datetime import timedelta +import datetime + +class approvalRequiredDocs(models.Model): + _name = "approval.req.docs" + + name = fields.Char(string='name') \ No newline at end of file diff --git a/addons_extensions/hr_recruitment_extended/models/hr_applicant.py b/addons_extensions/hr_recruitment_extended/models/hr_applicant.py index 20c720d6f..35d84a9ed 100644 --- a/addons_extensions/hr_recruitment_extended/models/hr_applicant.py +++ b/addons_extensions/hr_recruitment_extended/models/hr_applicant.py @@ -6,6 +6,7 @@ from datetime import datetime from odoo.exceptions import ValidationError import warnings +from odoo.tools.mimetypes import guess_mimetype, fix_filename_extension class HRApplicant(models.Model): @@ -17,9 +18,16 @@ class HRApplicant(models.Model): submitted_to_client = fields.Boolean(string="Submitted_to_client", default=False, readonly=True, tracking=True) client_submission_date = fields.Datetime(string="Submission Date") submitted_stage = fields.Many2one('hr.recruitment.stage') - refused_stage = fields.Many2one('hr.recruitment.stage') - refused_comments = fields.Text() + refused_stage = fields.Many2one('hr.recruitment.stage', string="Reject Stage") + refused_comments = fields.Text(string='Reject Comments') + is_on_hold = fields.Boolean(string="Is On Hold", default=False) + def hold_unhold_button(self): + for rec in self: + if rec.is_on_hold: + rec.is_on_hold = False + else: + rec.is_on_hold = True @api.constrains('candidate_id','hr_job_recruitment') def hr_applicant_constrains(self): for rec in self: @@ -92,7 +100,7 @@ class HRApplicant(models.Model): """) - refused_state = fields.Many2one('hr.recruitment.stage', readonly=True, force_save=True) + refused_state = fields.Many2one('hr.recruitment.stage', readonly=True, force_save=True, string="Reject state") hr_job_recruitment = fields.Many2one('hr.job.recruitment') job_id = fields.Many2one('hr.job', related='hr_job_recruitment.job_id', store=True) @@ -126,7 +134,35 @@ class HRApplicant(models.Model): approval_required = fields.Boolean(related='recruitment_stage_id.require_approval') application_submitted = fields.Boolean(string="Application Submitted") resume = fields.Binary(related='candidate_id.resume', readonly=False, compute_sudo=True) + resume_type = fields.Char(related='candidate_id.resume_type', readonly=False, compute_sudo=True) + resume_name = fields.Char(related='candidate_id.resume_name', readonly=False, compute_sudo=True) + @api.onchange('resume') + def onchange_resume(self): + for rec in self: + if rec.resume: + attachment = self.env.ref("hr_recruitment_extended.employee_recruitment_attachments_preview") + file = attachment.write({ + 'datas': rec.resume, + }) + if file: + rec.resume_type = attachment.mimetype + else: + rec.resume_type = '' + rec.resume_name = '' + + def preview_resume(self): + pass + # for record in self: + # if record.resume: + # attachment = self.env.ref("hr_recruitment_extended.employee_recruitment_attachments_preview") + # attachment.datas = record.resume + # return { + # 'name': "File Preview", + # 'type': 'ir.actions.act_url', + # 'url': f'/web/content/{attachment.id}?download=false', + # 'target': 'current', # Opens in a new tab + # } def submit_to_client(self): for rec in self: diff --git a/addons_extensions/hr_recruitment_extended/models/hr_job_recruitment.py b/addons_extensions/hr_recruitment_extended/models/hr_job_recruitment.py index 5a51c91af..b8f4efd16 100644 --- a/addons_extensions/hr_recruitment_extended/models/hr_job_recruitment.py +++ b/addons_extensions/hr_recruitment_extended/models/hr_job_recruitment.py @@ -83,12 +83,12 @@ class HRJobRecruitment(models.Model): for job in self: job.new_application_count = new_applicant_count.get(job.id, 0) - application_count = fields.Integer(compute='_compute_application_count', string="Application Count") - all_application_count = fields.Integer(compute='_compute_all_application_count', string="All Application Count") + application_count = fields.Integer(compute='_compute_application_count', string="Application Count", tracking=True) + all_application_count = fields.Integer(compute='_compute_all_application_count', string="All Application Count", tracking=True) new_application_count = fields.Integer( compute='_compute_new_application_count', string="New Application", help="Number of applications that are new in the flow (typically at first step of the flow)") - applicant_hired = fields.Integer(compute='_compute_applicant_hired', string="Applicants Hired") + applicant_hired = fields.Integer(compute='_compute_applicant_hired', string="Applicants Hired", tracking=True) def _get_default_favorite_user_ids(self): """this function is used to set the default users i.e current user""" @@ -104,24 +104,24 @@ class HRJobRecruitment(models.Model): else: return self.env.company.partner_id - job_id = fields.Many2one('hr.job', required=True) - name = fields.Char(string='Job Position', required=True, index='trigram', translate=True, related='job_id.name') + job_id = fields.Many2one('hr.job', required=True, tracking=True) + name = fields.Char(string='Job Position', required=True, index='trigram', translate=True, related='job_id.name', tracking=True) - recruitment_sequence = fields.Char(string='Recruitment Sequence', readonly=False, default='/', copy=False) + recruitment_sequence = fields.Char(string='Recruitment Sequence', readonly=False, default='/', copy=False, tracking=True) - favorite_user_ids = fields.Many2many('res.users', 'job_recruitment_favorite_user_rel', 'job_id', 'user_id', default=_get_default_favorite_user_ids) + favorite_user_ids = fields.Many2many('res.users', 'job_recruitment_favorite_user_rel', 'job_id', 'user_id', default=_get_default_favorite_user_ids, tracking=True) secondary_skill_ids = fields.Many2many('hr.skill', "hr_job_recruitment_hr_skill_rel", - 'job_recruitment_id', 'hr_skill_id', "Secondary Skills") + 'job_recruitment_id', 'hr_skill_id', "Secondary Skills", tracking=True) no_of_recruitment = fields.Integer(string='Number of Positions', copy=False, - help='Number of new employees you expect to recruit.', default=1) + help='Number of new employees you expect to recruit.', default=1, tracking=True) no_of_eligible_submissions = fields.Integer(string='Eligible Submissions', copy=False, - help='Number of Submissions you expected to send.', default=1) + help='Number of Submissions you expected to send.', default=1, tracking=True) - submission_status = fields.Selection([('zero','Zero Submissions'),('partial','Partial Submissions'),('filled','Filled Submissions')],default='zero', compute="_compute_submission_status", store=True) + submission_status = fields.Selection([('zero','Zero Submissions'),('partial','Partial Submissions'),('filled','Filled Submissions')],default='zero', compute="_compute_submission_status", store=True, tracking=True) @api.onchange("no_of_recruitment") def onchange_no_of_recruitments(self): @@ -131,36 +131,36 @@ class HRJobRecruitment(models.Model): rec.no_of_eligible_submissions = rec.no_of_recruitment - locations = fields.Many2many('hr.location') + locations = fields.Many2many('hr.location', tracking=True) target_from = fields.Date(string="This is the date in which we starting the recruitment process", - default=fields.Date.today) - target_to = fields.Date(string='This is the target end date') + default=fields.Date.today, tracking=True) + target_to = fields.Date(string='This is the target end date', tracking=True) # hiring_history = fields.One2many('recruitment.status.history', 'job_id', string='History') - is_favorite = fields.Boolean(compute='_compute_is_favorite', inverse='_inverse_is_favorite',store=True) - department_id = fields.Many2one('hr.department', string='Department', check_company=True) - description = fields.Html(string='Job Description', sanitize_attributes=False) - requirements = fields.Text('Requirements') + is_favorite = fields.Boolean(compute='_compute_is_favorite', inverse='_inverse_is_favorite',store=True, tracking=True) + department_id = fields.Many2one('hr.department', string='Department', check_company=True, tracking=True) + description = fields.Html(string='Job Description', tracking=True, sanitize_attributes=False) + requirements = fields.Text('Requirements', tracking=True) expected_employees = fields.Integer(compute='_compute_employees', string='Total Forecasted Employees', store=True, help='Expected number of employees for this job position after new recruitment.') no_of_employee = fields.Integer(compute='_compute_employees', string="Current Number of Employees", store=True, help='Number of employees currently occupying this job position.') - company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.company, exportable=False) - contract_type_id = fields.Many2one('hr.contract.type', string='Employment Type') + company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.company, tracking=True, exportable=False) + contract_type_id = fields.Many2one('hr.contract.type', string='Employment Type', tracking=True) # active = fields.Boolean(default=True) user_id = fields.Many2one('res.users', "Recruiter", domain="[('share', '=', False), ('company_ids', 'in', company_id)]", default=lambda self: self.env.user, tracking=True, help="The Recruiter will be the default value for all Applicants in this job \ position. The Recruiter is automatically added to all meetings with the Applicant.") - interviewer_ids = fields.Many2many('res.users', string='Interviewers', domain="[('share', '=', False), ('company_ids', 'in', company_id)]", help="The Interviewers set on the job position can see all Applicants in it. They have access to the information, the attachments, the meeting management and they can refuse him. You don't need to have Recruitment rights to be set as an interviewer.") - skill_ids = fields.Many2many('hr.skill','hr_job_recruitment_hr_primary_skill_rel','job_id', 'user_id', string="Primary Skills") + interviewer_ids = fields.Many2many('res.users', string='Interviewers', domain="[('share', '=', False), ('company_ids', 'in', company_id)]", tracking=True, help="The Interviewers set on the job position can see all Applicants in it. They have access to the information, the attachments, the meeting management and they can refuse him. You don't need to have Recruitment rights to be set as an interviewer.") + skill_ids = fields.Many2many('hr.skill','hr_job_recruitment_hr_primary_skill_rel','job_id', 'user_id', string="Primary Skills", tracking=True) address_id = fields.Many2one( 'res.partner', "Job Location", default=_default_address_id, domain="[('is_company','=',True),('contact_type','=',recruitment_type)]", - help="Select the location where the applicant will work. Addresses listed here are defined on the company's contact information.", exportable=False) - recruitment_type = fields.Selection([('internal','In-House'),('external','Client-Side')], required=True, default='internal') + help="Select the location where the applicant will work. Addresses listed here are defined on the company's contact information.", exportable=False, tracking=True) + recruitment_type = fields.Selection([('internal','In-House'),('external','Client-Side')], required=True, default='internal', tracking=True) requested_by = fields.Many2one('res.partner', string="Requested By", - default=lambda self: self.env.user.partner_id, domain="[('contact_type','=',recruitment_type)]") + default=lambda self: self.env.user.partner_id, domain="[('contact_type','=',recruitment_type)]", tracking=True) @api.onchange('recruitment_type') def _onchange_recruitment_type(self): @@ -203,33 +203,35 @@ class HRJobRecruitment(models.Model): return """ [('is_client','=',True)] """ - document_ids = fields.One2many('ir.attachment', compute='_compute_document_ids', string="Documents", readonly=True) - documents_count = fields.Integer(compute='_compute_document_ids', string="Document Count") - color = fields.Integer("Color Index") - application_ids = fields.One2many('hr.applicant', 'hr_job_recruitment', "Job Applications") + document_ids = fields.One2many('ir.attachment', compute='_compute_document_ids', string="Documents", readonly=True, tracking=True) + documents_count = fields.Integer(compute='_compute_document_ids', string="Document Count", tracking=True) + color = fields.Integer("Color Index", tracking=True) + application_ids = fields.One2many('hr.applicant', 'hr_job_recruitment', "Job Applications", tracking=True) no_of_hired_employee = fields.Integer( compute='_compute_no_of_hired_employee', string='Hired', copy=False, help='Number of hired employees for this job position during recruitment phase.', - store=True) + store=True, tracking=True) no_of_submissions = fields.Integer( compute='_compute_no_of_submissions', string='Submitted', copy=False, store=True, - help='Number of Application submissions for this job position during recruitment phase.', + help='Number of Application submissions for this job position during recruitment phase.', tracking=True ) no_of_refused_submissions = fields.Integer( compute='_compute_no_of_refused_submissions', string='Hired', copy=False, help='Number of Refused Application submissions for this job position during recruitment phase.', - ) + tracking=True) - experience = fields.Many2one('candidate.experience', string="Experience") + experience = fields.Many2one('candidate.experience', string="Experience", tracking=True) - budget = fields.Char(string='Budget') + budget = fields.Char(string='Budget', tracking=True) - job_category = fields.Many2one("job.category", string="Category") + job_category = fields.Many2one("job.category", string="Category", tracking=True) - job_priority = fields.Selection([('low','Low'),('medium','Medium'),('high','High')], string="Pirority") + job_priority = fields.Selection([('low','Low'),('medium','Medium'),('high','High')], string="Pirority", tracking=True) + + recruitment_status = fields.Selection([('open','Open'),('closed','Closed'),('hold','Hold'),('modified','Modified'),('cancelled','Cancelled')], default='open', tracking=True) @api.onchange('job_id','job_category') def onchange_job_id(self): @@ -445,6 +447,6 @@ class HRJobRecruitment(models.Model): class HRSkill(models.Model): _inherit = 'hr.skill' - job_recruitment_id = fields.Many2one('hr.job.recruitment') + job_recruitment_id = fields.Many2one('hr.job.recruitment', tracking=True) diff --git a/addons_extensions/hr_recruitment_extended/models/hr_recruitment.py b/addons_extensions/hr_recruitment_extended/models/hr_recruitment.py index aeceef317..13071954e 100644 --- a/addons_extensions/hr_recruitment_extended/models/hr_recruitment.py +++ b/addons_extensions/hr_recruitment_extended/models/hr_recruitment.py @@ -27,10 +27,27 @@ class HrCandidate(models.Model): candidate_image = fields.Image() employee_code = fields.Char(related="employee_id.employee_id") resume = fields.Binary() + resume_type = fields.Char() + resume_name = fields.Char() applications_stages_stat = fields.Many2many('application.stage.status',string="Applications History", compute="_compute_applications_stages_stat") # availability_status = fields.Selection([('available','Available'),('not_available','Not Available'),('hired','Hired'),('abscond','Abscond')]) + + @api.onchange('resume') + def onchange_resume(self): + for rec in self: + if rec.resume: + attachment = self.env.ref("hr_recruitment_extended.employee_recruitment_attachments_preview") + file = attachment.write({ + 'datas':rec.resume, + }) + if file: + rec.resume_type = attachment.mimetype + else: + rec.resume_type = '' + rec.resume_name = '' + def action_open_applications(self): self.ensure_one() return { diff --git a/addons_extensions/hr_recruitment_extended/views/hr_applicant_views.xml b/addons_extensions/hr_recruitment_extended/views/hr_applicant_views.xml index 46bcd9fd0..adc194d58 100644 --- a/addons_extensions/hr_recruitment_extended/views/hr_applicant_views.xml +++ b/addons_extensions/hr_recruitment_extended/views/hr_applicant_views.xml @@ -18,7 +18,17 @@ hr.applicant + + Reject + + +