From 571e9f0ab07b2e23ef1954968ba8266bec4eabbe Mon Sep 17 00:00:00 2001 From: pranaysaidurga Date: Thu, 30 Jul 2026 13:48:19 +0530 Subject: [PATCH] Recruimtent changes & Vendor update --- .../wizard/hr_recruitment_auto_doc_wizard.py | 9 +- .../hr_recruitment_auto_doc_wizard_views.xml | 10 +- .../models/hr_applicant.py | 91 +++++---- .../models/res_partner.py | 44 ++++- .../views/hr_applicant_views.xml | 11 ++ .../views/hr_job_recruitment.xml | 14 ++ .../views/hr_recruitment.xml | 13 ++ .../views/res_partner.xml | 173 +++++++++++++++--- .../views/hr_applicant_form.xml | 3 +- .../views/hr_candidate_form.xml | 3 +- 10 files changed, 288 insertions(+), 83 deletions(-) diff --git a/addons_extensions/hr_recruitment_auto_doc/wizard/hr_recruitment_auto_doc_wizard.py b/addons_extensions/hr_recruitment_auto_doc/wizard/hr_recruitment_auto_doc_wizard.py index edde98818..665b3479d 100644 --- a/addons_extensions/hr_recruitment_auto_doc/wizard/hr_recruitment_auto_doc_wizard.py +++ b/addons_extensions/hr_recruitment_auto_doc/wizard/hr_recruitment_auto_doc_wizard.py @@ -1906,8 +1906,9 @@ class HrRecruitmentAutoDocWizard(models.TransientModel): "target_to": self._parse_date_value(parsed_data.get("end_date")), "job_category": int(job_category_id) if job_category_id and job_category_id.isdigit() else False, "address_id":False, - "recruitment_type": 'external' - + "recruitment_type": 'external', + "requested_by": parsed_data.get("requested_by") or False, + "no_of_recruitment": parsed_data.get("no_of_positions") or 1, } if request_id: create_vals["recruitment_sequence"] = request_id @@ -2207,6 +2208,8 @@ class HrRecruitmentAutoDocWizardLine(models.TransientModel): budget = fields.Char() experience_years = fields.Float() job_category = fields.Char() + requested_by = fields.Many2one('res.partner',domain="[('contact_type', '=', 'external')]") + no_of_positions = fields.Integer('Number of Positions') def action_save_line_changes(self): self.ensure_one() @@ -2286,6 +2289,8 @@ class HrRecruitmentAutoDocWizardLine(models.TransientModel): "budget": self.budget, "experience_years": self.experience_years, "job_category": self.job_category, + "requested_by": self.requested_by.id if self.requested_by else False, + "no_of_positions": self.no_of_positions if self.no_of_positions else 1, }) else: data.update({ diff --git a/addons_extensions/hr_recruitment_auto_doc/wizard/hr_recruitment_auto_doc_wizard_views.xml b/addons_extensions/hr_recruitment_auto_doc/wizard/hr_recruitment_auto_doc_wizard_views.xml index ce46616fa..15c3a46bc 100644 --- a/addons_extensions/hr_recruitment_auto_doc/wizard/hr_recruitment_auto_doc_wizard_views.xml +++ b/addons_extensions/hr_recruitment_auto_doc/wizard/hr_recruitment_auto_doc_wizard_views.xml @@ -160,14 +160,20 @@ + + - - + + + + + + diff --git a/addons_extensions/hr_recruitment_extended/models/hr_applicant.py b/addons_extensions/hr_recruitment_extended/models/hr_applicant.py index bda613c88..616cf8ab0 100644 --- a/addons_extensions/hr_recruitment_extended/models/hr_applicant.py +++ b/addons_extensions/hr_recruitment_extended/models/hr_applicant.py @@ -284,12 +284,12 @@ class HRApplicant(models.Model): 'applicant_id', string='Request Forms' ) - post_onboarding_form_status = fields.Selection([('draft','Draft'),('email_sent_to_candidate','Email Sent to Candidate'),('done','Done')], default='draft') - doc_requests_form_status = fields.Selection( - [('draft', 'Draft'), ('email_sent_to_candidate', 'Email Sent to Candidate'), ('done', 'Done')], - default='draft', - ) - legend_blocked = fields.Char(related='recruitment_stage_id.legend_blocked', string='Kanban Blocked') + post_onboarding_form_status = fields.Selection([('draft','Draft'),('email_sent_to_candidate','Email Sent to Candidate'),('done','Done')], default='draft') + doc_requests_form_status = fields.Selection( + [('draft', 'Draft'), ('email_sent_to_candidate', 'Email Sent to Candidate'), ('done', 'Done')], + default='draft', + ) + legend_blocked = fields.Char(related='recruitment_stage_id.legend_blocked', string='Kanban Blocked') legend_done = fields.Char(related='recruitment_stage_id.legend_done', string='Kanban Valid') legend_normal = fields.Char(related='recruitment_stage_id.legend_normal', string='Kanban Ongoing') employee_code = fields.Char(related="employee_id.employee_id") @@ -304,14 +304,14 @@ class HRApplicant(models.Model): ('validated', 'Validated')], default='pending') approval_required = fields.Boolean(related='recruitment_stage_id.require_approval') - application_submitted = fields.Boolean(string="Application Submitted") - send_second_application_form = fields.Boolean(string="Send Second Application Form") - send_post_onboarding_form = fields.Boolean(string="Send Post Onboarding Form") - second_application_form_status = fields.Selection( - [('draft', 'Draft'), ('email_sent_to_candidate', 'Email Sent to Candidate'), ('done', 'Done')], - default='draft', - ) - resume = fields.Binary(related='candidate_id.resume', readonly=False, compute_sudo=True) + application_submitted = fields.Boolean(string="Application Submitted") + send_second_application_form = fields.Boolean(string="Send Second Application Form") + send_post_onboarding_form = fields.Boolean(string="Send Post Onboarding Form") + second_application_form_status = fields.Selection( + [('draft', 'Draft'), ('email_sent_to_candidate', 'Email Sent to Candidate'), ('done', 'Done')], + default='draft', + ) + 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) @@ -360,11 +360,11 @@ class HRApplicant(models.Model): 'joining_attachment_ids': [(0, 0, values) for values in latest_attachments.values()] }) - def action_share_applicant(self): - self.ensure_one() - return { - 'type': 'ir.actions.act_window', - 'name': _('Share Applicant'), + def action_share_applicant(self): + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'name': _('Share Applicant'), 'res_model': 'client.submission.mails.template.wizard', 'view_mode': 'form', 'view_id': self.env.ref('hr_recruitment_extended.view_client_submission_mails_template_wizard_form').id, @@ -372,15 +372,15 @@ class HRApplicant(models.Model): 'context': { 'default_template_id': self.env.ref( 'hr_recruitment_extended.application_client_submission_email_template' - ).id, - }, - } - - def submit_to_client(self): - return self.action_share_applicant() - - def submit_for_approval(self): - for rec in self: + ).id, + }, + } + + def submit_to_client(self): + return self.action_share_applicant() + + def submit_for_approval(self): + for rec in self: manager_id = self.env['ir.config_parameter'].sudo().get_param('requisitions.requisition_manager') if not manager_id: raise ValidationError(_("Recruitment Manager is not selected please go into the Configuration->Settings and add the Manager")) @@ -389,8 +389,7 @@ class HRApplicant(models.Model): render_ctx = dict(recruitment_manager=manager_id) mail_template.with_context(render_ctx).send_mail( self.id, - force_send=True, - email_layout_xmlid='mail.mail_notification_light') + force_send=True) rec.application_submitted = True def approve_applicant(self): @@ -432,11 +431,11 @@ class HRApplicant(models.Model): template.send_mail(applicant.id, force_send=True) applicant.second_application_form_status = 'email_sent_to_candidate' - def send_jod_form_to_employee(self): - for rec in self: - if not rec.employee_id: - raise ValidationError(_('You must first create the employee before before Sending the Post Onboarding Form')) - + def send_jod_form_to_employee(self): + for rec in self: + if not rec.employee_id: + raise ValidationError(_('You must first create the employee before before Sending the Post Onboarding Form')) + elif not rec.employee_id.employee_id: raise ValidationError(_('Employee Code for the Employee (%s) is missing')%(rec.employee_id.name)) return { @@ -445,17 +444,17 @@ class HRApplicant(models.Model): 'res_model': 'post.onboarding.attachment.wizard', 'view_mode': 'form', 'view_type': 'form', - 'target': 'new', - 'context': {'default_req_attachment_ids': []} - } - - def send_post_onboarding_form_to_candidate(self): - return self.send_jod_form_to_employee() - - - def send_pre_onboarding_doc_request_form_to_candidate(self): - return { - 'type': 'ir.actions.act_window', + 'target': 'new', + 'context': {'default_req_attachment_ids': []} + } + + def send_post_onboarding_form_to_candidate(self): + return self.send_jod_form_to_employee() + + + def send_pre_onboarding_doc_request_form_to_candidate(self): + return { + 'type': 'ir.actions.act_window', 'name': 'Select Attachments', 'res_model': 'post.onboarding.attachment.wizard', 'view_mode': 'form', diff --git a/addons_extensions/hr_recruitment_extended/models/res_partner.py b/addons_extensions/hr_recruitment_extended/models/res_partner.py index 48c43cfc1..6b9782834 100644 --- a/addons_extensions/hr_recruitment_extended/models/res_partner.py +++ b/addons_extensions/hr_recruitment_extended/models/res_partner.py @@ -5,4 +5,46 @@ class ResPartner(models.Model): _inherit = 'res.partner' contact_type = fields.Selection([('internal','In-House'),('external','Client-Side')], required=True, default='internal') - company_id = fields.Many2one('res.company', default=lambda self: self.env.user.company_id) \ No newline at end of file + company_id = fields.Many2one('res.company', default=lambda self: self.env.user.company_id) + + # Vendor Statistics Fields + total_positions_requested = fields.Integer(compute='_compute_vendor_statistics', string='Positions Requested') + total_submitted = fields.Integer(compute='_compute_vendor_statistics', string='Total Submitted') + total_hired = fields.Integer(compute='_compute_vendor_statistics', string='Total Hired') + total_rejected = fields.Integer(compute='_compute_vendor_statistics', string='Total Rejected') + job_recruitment_ids = fields.One2many('hr.job.recruitment', 'requested_by', string='Job Requests') + + @api.depends('contact_type') + def _compute_vendor_statistics(self): + for partner in self: + if partner.contact_type == 'external': + # Get all job recruitments requested by this vendor + job_recruitments = self.env['hr.job.recruitment'].search([ + ('requested_by', '=', partner.id) + ]) + + partner.total_positions_requested = sum(job_recruitments.mapped('no_of_recruitment')) + partner.total_submitted = sum(job_recruitments.mapped('no_of_submissions')) + partner.total_hired = sum(job_recruitments.mapped('no_of_hired_employee')) + partner.total_rejected = sum(job_recruitments.mapped('no_of_refused_submissions')) + else: + partner.total_positions_requested = 0 + partner.total_submitted = 0 + partner.total_hired = 0 + partner.total_rejected = 0 + + def action_create_job_request(self): + """Action to create a new job request from vendor""" + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'name': _('Create Job Request'), + 'res_model': 'hr.job.recruitment', + 'view_mode': 'form', + 'target': 'current', + 'context': { + 'default_requested_by': self.id, + 'default_recruitment_type': 'external', + 'default_address_id': self.id if self.is_company else (self.parent_id.id if self.parent_id else False), + } + } \ No newline at end of file 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 4dcd1632a..c39b68b8b 100644 --- a/addons_extensions/hr_recruitment_extended/views/hr_applicant_views.xml +++ b/addons_extensions/hr_recruitment_extended/views/hr_applicant_views.xml @@ -218,6 +218,17 @@ Rejected + + + + + + + + + + + Reject Reason diff --git a/addons_extensions/hr_recruitment_extended/views/hr_job_recruitment.xml b/addons_extensions/hr_recruitment_extended/views/hr_job_recruitment.xml index 2a89d280a..dae26aee3 100644 --- a/addons_extensions/hr_recruitment_extended/views/hr_job_recruitment.xml +++ b/addons_extensions/hr_recruitment_extended/views/hr_job_recruitment.xml @@ -246,6 +246,20 @@ + + + + + + + + + + + diff --git a/addons_extensions/hr_recruitment_extended/views/hr_recruitment.xml b/addons_extensions/hr_recruitment_extended/views/hr_recruitment.xml index f72f90023..358eab4a9 100644 --- a/addons_extensions/hr_recruitment_extended/views/hr_recruitment.xml +++ b/addons_extensions/hr_recruitment_extended/views/hr_recruitment.xml @@ -268,6 +268,19 @@ hr.candidate + + + + + + + + + + + + + diff --git a/addons_extensions/hr_recruitment_extended/views/res_partner.xml b/addons_extensions/hr_recruitment_extended/views/res_partner.xml index d8cc9e9a5..e85acc6d5 100644 --- a/addons_extensions/hr_recruitment_extended/views/res_partner.xml +++ b/addons_extensions/hr_recruitment_extended/views/res_partner.xml @@ -98,6 +98,58 @@ +
+
+
+
+
Recruitment Statistics
+
+
+
+
+
+
Positions Requested
+
+ +
+
+
+
+
+
Total Submitted
+
+ +
+
+
+
+
+
Total Hired
+
+ +
+
+
+
+
+
Total Rejected
+
+ +
+
+
+
+
+
+
+
+ +
+ +
+ @@ -137,6 +189,22 @@ + + + + + + + + + + + + + + + + @@ -182,43 +250,88 @@ + + + + - -
- - -
- +
+
+
+
+ +
+
+ + + +
+ + +
+
+ + +
+
+
- - -
- -
- +
+
+
+
+
+
Requested
+
+ +
+
+
+
+
+
+
+
Submitted
+
+ +
+
+
+
+
+
+
+
Hired
+
+ +
+
+
+
+
+
+
+
Rejected
+
+ +
+
+
+
- -
- - -
- -
- - -
-
- +
+ +
- diff --git a/addons_extensions/web_portal_form_custom/views/hr_applicant_form.xml b/addons_extensions/web_portal_form_custom/views/hr_applicant_form.xml index c1febe092..069b02622 100644 --- a/addons_extensions/web_portal_form_custom/views/hr_applicant_form.xml +++ b/addons_extensions/web_portal_form_custom/views/hr_applicant_form.xml @@ -571,7 +571,8 @@ - + + diff --git a/addons_extensions/web_portal_form_custom/views/hr_candidate_form.xml b/addons_extensions/web_portal_form_custom/views/hr_candidate_form.xml index 7a8930a14..575fc678b 100644 --- a/addons_extensions/web_portal_form_custom/views/hr_candidate_form.xml +++ b/addons_extensions/web_portal_form_custom/views/hr_candidate_form.xml @@ -343,7 +343,8 @@ - + +