diff --git a/addons_extensions/document_parser/__manifest__.py b/addons_extensions/document_parser/__manifest__.py index 05d96bc4f..65eec6e90 100644 --- a/addons_extensions/document_parser/__manifest__.py +++ b/addons_extensions/document_parser/__manifest__.py @@ -14,6 +14,6 @@ "application": False, "auto_install": False, "external_dependencies": { - "python": ["requests"], + "python": ["requests","python-docx"], }, } diff --git a/addons_extensions/document_parser/models/document_parser_service.py b/addons_extensions/document_parser/models/document_parser_service.py index a672c9706..ff5d8da16 100644 --- a/addons_extensions/document_parser/models/document_parser_service.py +++ b/addons_extensions/document_parser/models/document_parser_service.py @@ -421,6 +421,91 @@ Document: def _get_param(self, key): return self.env["ir.config_parameter"].sudo().get_param(key) + @api.model + def validate_explicit_skills(self, resume_text, skills): + if not skills: + return [] + + prompt = f""" + You are validating resume skills. + + Resume: + {resume_text[:30000]} + + Extracted Skills: + {json.dumps(skills)} + + Keep ONLY skills explicitly claimed by the candidate. + + A skill is explicit if: + - It is presented as the candidate's expertise. + - It appears in a skill list or competency list. + + Reject skills appearing only in: + - job responsibilities + - project descriptions + - achievements + - employer history + + Return ONLY JSON. + + Example: + {{ + "skills": ["Python", "Django"] + }} + """ + + errors = [] + + together_key = self._get_param( + "document_parser.together_ai_key" + ) or self._get_param( + "document_parser.together_api_key" + ) + + openrouter_key = self._get_param( + "document_parser.openrouter_ai_key" + ) or self._get_param( + "document_parser.openrouter_api_key" + ) + + if together_key: + result, provider_errors = self._call_provider( + provider_name="Together", + endpoint=self.TOGETHER_ENDPOINT, + headers={ + "Authorization": f"Bearer {together_key}", + "Content-Type": "application/json", + }, + models=self.TOGETHER_MODELS, + prompt=prompt, + ) + + if result: + return result.get("skills", skills) + + errors.extend(provider_errors) + + if openrouter_key: + result, provider_errors = self._call_provider( + provider_name="OpenRouter", + endpoint=self.OPENROUTER_ENDPOINT, + headers={ + "Authorization": f"Bearer {openrouter_key}", + "Content-Type": "application/json", + "HTTP-Referer": self._get_param("web.base.url") or "odoo.local", + "X-Title": "Document Parser", + }, + models=self.OPENROUTER_MODELS, + prompt=prompt, + ) + + if result: + return result.get("skills", skills) + + errors.extend(provider_errors) + + return skills def _normalize_required_fields(self, fields): if isinstance(fields, dict): normalized = {} diff --git a/addons_extensions/employee_it_declaration/models/emp_it_declaration.py b/addons_extensions/employee_it_declaration/models/emp_it_declaration.py index 2df9f0f4d..76035b6b5 100644 --- a/addons_extensions/employee_it_declaration/models/emp_it_declaration.py +++ b/addons_extensions/employee_it_declaration/models/emp_it_declaration.py @@ -37,38 +37,44 @@ class ITDeclarationSubmittedLockMixin(models.AbstractModel): class EmpITDeclaration(models.Model): - _name = 'emp.it.declaration' - _rec_name = 'employee_id' - _description = "IT Declaration" - - # @api.depends('period_id', 'employee_id') - # def _compute_name(self): - # for sheet in self: - # # sheet.name = _('%(period_id)s, %(emp_name)s', period_id=sheet.period_id.name, emp_name=sheet.employee_id.name) - # sheet.name='hello world' - - employee_id = fields.Many2one( - 'hr.employee', - string="Employee", - default=lambda self: self.env.user.employee_id, - required=True - ) - period_id = fields.Many2one( - 'payroll.period', - string="Payroll Period", - required=True - ) - - display_period_label = fields.Char(string="Period Label", compute='_compute_display_period_label') - - @api.depends('period_id.name') - def _compute_display_period_label(self): - for rec in self: - if rec.period_id: - rec.display_period_label = f"Financial Year {rec.period_id.name}" - else: - rec.display_period_label = "" - + _name = 'emp.it.declaration' + _rec_name = 'employee_id' + _description = "IT Declaration" + + _sql_constraints = [ + ('name_emp_tax_period', 'unique(employee_id, period_id, tax_regime)', + "Avoid creating duplicate records for the same period_id and tax_regime."), + ] + + # @api.depends('period_id', 'employee_id') + # def _compute_name(self): + # for sheet in self: + # # sheet.name = _('%(period_id)s, %(emp_name)s', period_id=sheet.period_id.name, emp_name=sheet.employee_id.name) + # sheet.name='hello world' + + employee_id = fields.Many2one( + 'hr.employee', + string="Employee", + default=lambda self: self.env.user.employee_id, + required=True + ) + period_id = fields.Many2one( + 'payroll.period', + string="Payroll Period", + required=True, + copy=False + ) + + display_period_label = fields.Char(string="Period Label", compute='_compute_display_period_label') + + @api.depends('period_id.name') + def _compute_display_period_label(self): + for rec in self: + if rec.period_id: + rec.display_period_label = f"Financial Year {rec.period_id.name}" + else: + rec.display_period_label = "" + tax_regime = fields.Selection([ ('new', 'New Regime'), ('old', 'Old Regime') @@ -119,8 +125,8 @@ class EmpITDeclaration(models.Model): for rec in self: if rec.investment_costing_ids and rec.costing_details_generated: rec.house_rent_costing_id = rec.investment_costing_ids.filtered( - lambda e: e.investment_type_id.investment_type == 'house_rent' - )[:1] + lambda e: e.investment_type_id.investment_type == 'house_rent' + )[:1] else: rec.house_rent_costing_id = False @@ -166,13 +172,13 @@ class EmpITDeclaration(models.Model): other_il_costings_new = fields.One2many('other.il.costing.type','it_declaration_id',domain=[('investment_type_line_id.tax_regime', 'in', ['new', 'both'])]) other_declaration_costings = fields.One2many('other.declaration.costing.type','it_declaration_id',domain=[('investment_type_line_id.tax_regime', 'in', ['old', 'both'])]) other_declaration_costings_new = fields.One2many('other.declaration.costing.type','it_declaration_id',domain=[('investment_type_line_id.tax_regime', 'in', ['new', 'both'])]) - show_past_employment = fields.Boolean(compute="_compute_show_records") - show_us_80c = fields.Boolean(compute="_compute_show_records") - show_us_80d = fields.Boolean(compute="_compute_show_records") - show_us_10 = fields.Boolean(compute="_compute_show_records") - show_us_80g = fields.Boolean(compute="_compute_show_records") - show_chapter_via = fields.Boolean(compute="_compute_show_records") - show_us_17 = fields.Boolean(compute="_compute_show_records") + show_past_employment = fields.Boolean(compute="_compute_show_records") + show_us_80c = fields.Boolean(compute="_compute_show_records") + show_us_80d = fields.Boolean(compute="_compute_show_records") + show_us_10 = fields.Boolean(compute="_compute_show_records") + show_us_80g = fields.Boolean(compute="_compute_show_records") + show_chapter_via = fields.Boolean(compute="_compute_show_records") + show_us_17 = fields.Boolean(compute="_compute_show_records") show_house_rent = fields.Boolean(compute="_compute_show_records") show_other_i_or_l = fields.Boolean(compute="_compute_show_records") show_other_declaration = fields.Boolean(compute="_compute_show_records") @@ -353,7 +359,7 @@ class EmpITDeclaration(models.Model): rec[field_name] = bool(visible_investment_types.filtered( lambda inv: inv.investment_type == investment_type_key )) - + def toggle_section_visibility(self): for rec in self: rec.is_section_open = not rec.is_section_open @@ -372,37 +378,37 @@ class EmpITDeclaration(models.Model): # self.fields_get() if self.tax_regime == 'new': domain = [('investment_type_line_id.tax_regime', 'in', ['new', 'both'])] - elif self.tax_regime == 'old': - domain = [('investment_type_line_id.tax_regime', 'in', ['old', 'both'])] - else: - domain = [] # Default case, although 'tax_regime' is required - return {'domain': {'past_employment_costings': domain}} - else: - return {'domain': {'past_employment_costings': []}} # Handle potential empty state - - # def fields_get(self, allfields=None, attributes=None): - # import pdb - # pdb.set_trace() - # res = super(empITDeclaration, self).fields_get(allfields, attributes) - # print(res) - # - # # Example: Modify domain of field_1 based on field_2 - # if 'tax_regime' in res: - # if self.tax_regime == '': - # res['field_1']['domain'] = [('some_field', '=', 123)] - # else: - # res['field_1']['domain'] = [('some_field', '=', 456)] - # - # return res - # import pdb - # pdb.set_trace() - # if rec.tax_regime: - # return {'domain': {'past_employment_costings': [('investment_type_line_id.tax_regime', 'in', ['new','both'])]}} - # return { - # 'domain': { - # 'past_employment_costings': [('investment_type_line_id.tax_regime', 'in', ['new','both'])] - # } - # } + elif self.tax_regime == 'old': + domain = [('investment_type_line_id.tax_regime', 'in', ['old', 'both'])] + else: + domain = [] # Default case, although 'tax_regime' is required + return {'domain': {'past_employment_costings': domain}} + else: + return {'domain': {'past_employment_costings': []}} # Handle potential empty state + + # def fields_get(self, allfields=None, attributes=None): + # import pdb + # pdb.set_trace() + # res = super(empITDeclaration, self).fields_get(allfields, attributes) + # print(res) + # + # # Example: Modify domain of field_1 based on field_2 + # if 'tax_regime' in res: + # if self.tax_regime == '': + # res['field_1']['domain'] = [('some_field', '=', 123)] + # else: + # res['field_1']['domain'] = [('some_field', '=', 456)] + # + # return res + # import pdb + # pdb.set_trace() + # if rec.tax_regime: + # return {'domain': {'past_employment_costings': [('investment_type_line_id.tax_regime', 'in', ['new','both'])]}} + # return { + # 'domain': { + # 'past_employment_costings': [('investment_type_line_id.tax_regime', 'in', ['new','both'])] + # } + # } def generate_declarations(self): for rec in self: diff --git a/addons_extensions/employee_it_declaration/models/employee_payslip_download_wiz.py b/addons_extensions/employee_it_declaration/models/employee_payslip_download_wiz.py index 358b0da2f..2e82ad7cd 100644 --- a/addons_extensions/employee_it_declaration/models/employee_payslip_download_wiz.py +++ b/addons_extensions/employee_it_declaration/models/employee_payslip_download_wiz.py @@ -42,8 +42,6 @@ class EmployeePayslipDownloadWizard(models.TransientModel): def _compute_is_hr_manager(self): for rec in self: - import pdb - pdb.set_trace() rec.is_hr_manager = self.env.user.has_group('hr.group_hr_manager') @api.onchange('download_type', 'period_id') diff --git a/addons_extensions/employee_it_declaration/views/emp_it_declaration.xml b/addons_extensions/employee_it_declaration/views/emp_it_declaration.xml index b19816111..af6d56b98 100644 --- a/addons_extensions/employee_it_declaration/views/emp_it_declaration.xml +++ b/addons_extensions/employee_it_declaration/views/emp_it_declaration.xml @@ -5,60 +5,60 @@ emp.it.declaration.list emp.it.declaration - - - - - - - - - - + + + + + + + + + + emp.it.declarations.form - emp.it.declaration - -
-
-
- -
-
- -
-
- - - - - - - - - - - + emp.it.declaration + + +
+
+ +
+
+ +
+
+ + + + + + + + + + + @@ -73,9 +73,9 @@

- +
- - + @@ -25,14 +22,27 @@ -
- - -
-
- - -
+ +
+ + +
+ +
+ + +
+
diff --git a/addons_extensions/web_portal_form_custom/models/application_candidate_changes.py b/addons_extensions/web_portal_form_custom/models/application_candidate_changes.py index 915f6c211..7765427cf 100644 --- a/addons_extensions/web_portal_form_custom/models/application_candidate_changes.py +++ b/addons_extensions/web_portal_form_custom/models/application_candidate_changes.py @@ -1,4 +1,5 @@ from odoo import api, fields, models,_ +from odoo.exceptions import ValidationError class ApplicantCandidate(models.Model): @@ -40,7 +41,6 @@ class ApplicantCandidate(models.Model): if not self.resume: return - return { 'type': 'ir.actions.act_url', 'url': f'/web/content/hr.candidate/{self.id}/resume/{self.resume_name}?download=false', diff --git a/addons_extensions/web_portal_form_custom/static/src/css/candidate_card.css b/addons_extensions/web_portal_form_custom/static/src/css/candidate_card.css index f57e17ee8..92ad5230e 100644 --- a/addons_extensions/web_portal_form_custom/static/src/css/candidate_card.css +++ b/addons_extensions/web_portal_form_custom/static/src/css/candidate_card.css @@ -30,7 +30,7 @@ .container-fluid.mt-2.mb-2 { overflow: visible !important; position: relative; - z-index: 100; + z-index: 30; } /* Fix notebook stacking context */ @@ -42,4 +42,13 @@ /* Ensure dropdown appears above everything */ .modal-open .o_field_many2one .o_m2o_dropdown { z-index: 1061 !important; +} + +.candidate-name { + font-size: 1.4rem; + font-weight: 500; +} + +.job-name { + font-size: 0.9rem; } \ No newline at end of file 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 b8d8767bd..0e1632d1f 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 @@ -26,29 +26,6 @@ overflow: visible !important; } - /* Critical fix - make sure the dropdown appears above notebook */ - .ui-autocomplete, - .ui-menu, - .o_m2o_dropdown, - .o_field_many2one .o_m2o_dropdown, - .dropdown-menu { - z-index: 9999 !important; - position: absolute !important; - } - - /* Fix for the specific many2one field container */ - .o_field_many2one { - position: relative !important; - z-index: 100 !important; - } - - /* When dropdown is open, ensure it's on top */ - .o_field_many2one.o_focused .o_m2o_dropdown { - z-index: 10000 !important; - position: fixed !important; - max-height: 300px !important; - overflow-y: auto !important; - } /* Override any overflow hidden on parent containers */ .container-fluid, @@ -65,6 +42,14 @@ .modal .o_field_many2one .o_m2o_dropdown { z-index: 10001 !important; } + .ui-autocomplete, + .ui-menu, + .o_m2o_dropdown, + .o_field_many2one .o_m2o_dropdown, + .dropdown-menu { + z-index: 9999 !important; + position: absolute !important; + }
@@ -105,10 +90,10 @@
-
-
+
+
-
+
-
-

- -

-
- +
+ +
+
+ +
+ +
+
- +
+
-
+
-
-
+
@@ -174,13 +169,13 @@ groups="hr_recruitment.group_hr_recruitment_user" col="1">