enhancements of resume parsing
This commit is contained in:
parent
957e832abd
commit
b12c76d315
|
|
@ -269,6 +269,9 @@ RULES:
|
|||
- Scan the entire document carefully before answering.
|
||||
- Extract ONLY what exists in text.
|
||||
- FOR ANY DATES CHANGE FORMAT TO %Y-%m-%d
|
||||
- For list fields, always return an array. Return [] when no explicit entries exist.
|
||||
- For list fields that describe objects, return one object per real document entry, not a sentence blob.
|
||||
- Do not skip education, employer, project, certification, or family sections if they appear later in the document.
|
||||
|
||||
FIELD RULES:
|
||||
- If "skills" exists, extract only explicit technical skills written in the document.
|
||||
|
|
@ -283,6 +286,9 @@ FIELD RULES:
|
|||
- If "name" exists, prefer the full name at the top and exclude titles, companies, and addresses.
|
||||
- If "phone" exists, return the most complete phone number found.
|
||||
- If "experience" exists, return only clearly supported numeric values.
|
||||
- If "education_history" exists, extract every education row/section with education_type, specialization, university, start_year, end_year, and marks_or_grade.
|
||||
- If "employer_history" exists, extract every employer/project/work-experience entry with company_name, designation, date_of_joining, last_working_day, ctc, and work_description.
|
||||
- If "family_details" exists, extract only explicitly written family members with relation_type, name, contact_no, dob, and location.
|
||||
|
||||
Schema:
|
||||
{schema_text}
|
||||
|
|
@ -301,7 +307,7 @@ Document:
|
|||
"model": model,
|
||||
"messages": [{"role": "user", "content": prompt}],
|
||||
"temperature": 0,
|
||||
"max_tokens": 1500,
|
||||
"max_tokens": 4000,
|
||||
}
|
||||
try:
|
||||
response = requests.post(endpoint, headers=headers, json=payload, timeout=90)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_hr_recruitment_auto_doc_wizard,hr.recruitment.auto.doc.wizard,model_hr_recruitment_auto_doc_wizard,base.group_user,1,1,1,1
|
||||
access_hr_recruitment_auto_doc_wizard_line,hr.recruitment.auto.doc.wizard.line,model_hr_recruitment_auto_doc_wizard_line,base.group_user,1,1,1,1
|
||||
access_hr_recruitment_auto_doc_education_line,hr.recruitment.auto.doc.education.line,model_hr_recruitment_auto_doc_education_line,base.group_user,1,1,1,1
|
||||
access_hr_recruitment_auto_doc_employer_line,hr.recruitment.auto.doc.employer.line,model_hr_recruitment_auto_doc_employer_line,base.group_user,1,1,1,1
|
||||
access_hr_recruitment_auto_doc_family_line,hr.recruitment.auto.doc.family.line,model_hr_recruitment_auto_doc_family_line,base.group_user,1,1,1,1
|
||||
access_hr_recruitment_auto_doc_certification_line,hr.recruitment.auto.doc.certification.line,model_hr_recruitment_auto_doc_certification_line,base.group_user,1,1,1,1
|
||||
access_hr_recruitment_auto_doc_project_line,hr.recruitment.auto.doc.project.line,model_hr_recruitment_auto_doc_project_line,base.group_user,1,1,1,1
|
||||
access_hr_recruitment_auto_doc_other_line,hr.recruitment.auto.doc.other.line,model_hr_recruitment_auto_doc_other_line,base.group_user,1,1,1,1
|
||||
|
|
|
|||
|
|
|
@ -484,6 +484,17 @@ class HrRecruitmentAutoDocWizard(models.TransientModel):
|
|||
"relation_type (father/mother/spouse/kid1/kid2), name, contact_no, dob, and location."
|
||||
),
|
||||
},
|
||||
"certifications": {
|
||||
"type": "list",
|
||||
"description": "List of explicit certifications, courses, training, or licenses if present.",
|
||||
},
|
||||
"projects": {
|
||||
"type": "list",
|
||||
"description": (
|
||||
"List of explicit resume projects if present. Each item should include project_name, role, "
|
||||
"technologies, duration, and description when available."
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
def _get_jd_required_fields(self):
|
||||
|
|
@ -514,6 +525,10 @@ class HrRecruitmentAutoDocWizard(models.TransientModel):
|
|||
"Normalize skills into clean individual names. "
|
||||
"For experience values, return numeric years when clearly inferable. "
|
||||
"If the resume contains education details, previous employer details, or family details, return them as structured arrays of objects using the requested field names. "
|
||||
"Read the complete resume including tables, side columns, and later pages before returning JSON. "
|
||||
"education_history must include every school, intermediate, diploma, graduation, post graduation, or additional qualification row that is explicitly present. "
|
||||
"employer_history must include internships, current employer, previous employers, project/company experience, and work periods when explicitly present. "
|
||||
"family_details must include every explicitly mentioned family member and must be [] if the resume has no family section. "
|
||||
"For each employer entry, extract the role-specific work description into work_description. "
|
||||
"Only include entries that are explicitly present in the document. "
|
||||
"Do not consider certifications, responsibilities, Non Technical Stuff as skills"
|
||||
|
|
@ -2145,6 +2160,40 @@ class HrRecruitmentAutoDocWizardLine(models.TransientModel):
|
|||
degree = fields.Char()
|
||||
skills_text = fields.Text(string="Skills")
|
||||
summary = fields.Text()
|
||||
education_history_json = fields.Text(string="Education History")
|
||||
employer_history_json = fields.Text(string="Employer History")
|
||||
family_details_json = fields.Text(string="Family Details")
|
||||
other_payload_json = fields.Text(string="Other Parsed Data")
|
||||
education_line_ids = fields.One2many(
|
||||
"hr.recruitment.auto.doc.education.line",
|
||||
"wizard_line_id",
|
||||
string="Education History",
|
||||
)
|
||||
employer_line_ids = fields.One2many(
|
||||
"hr.recruitment.auto.doc.employer.line",
|
||||
"wizard_line_id",
|
||||
string="Employer History",
|
||||
)
|
||||
family_line_ids = fields.One2many(
|
||||
"hr.recruitment.auto.doc.family.line",
|
||||
"wizard_line_id",
|
||||
string="Family Details",
|
||||
)
|
||||
certification_line_ids = fields.One2many(
|
||||
"hr.recruitment.auto.doc.certification.line",
|
||||
"wizard_line_id",
|
||||
string="Certifications",
|
||||
)
|
||||
project_line_ids = fields.One2many(
|
||||
"hr.recruitment.auto.doc.project.line",
|
||||
"wizard_line_id",
|
||||
string="Projects",
|
||||
)
|
||||
other_line_ids = fields.One2many(
|
||||
"hr.recruitment.auto.doc.other.line",
|
||||
"wizard_line_id",
|
||||
string="Other Parsed Data",
|
||||
)
|
||||
|
||||
request_id = fields.Char(string="Request ID")
|
||||
start_date = fields.Char()
|
||||
|
|
@ -2197,6 +2246,16 @@ class HrRecruitmentAutoDocWizardLine(models.TransientModel):
|
|||
"degree": parsed_data.get("degree"),
|
||||
"skills_text": self._list_to_text(parsed_data.get("skills")),
|
||||
"summary": parsed_data.get("summary"),
|
||||
"education_history_json": self._json_to_text(parsed_data.get("education_history")),
|
||||
"employer_history_json": self._json_to_text(parsed_data.get("employer_history")),
|
||||
"family_details_json": self._json_to_text(parsed_data.get("family_details")),
|
||||
"other_payload_json": self._json_to_text(self._get_other_parsed_data(parsed_data)),
|
||||
"education_line_ids": self._prepare_education_line_commands(parsed_data.get("education_history")),
|
||||
"employer_line_ids": self._prepare_employer_line_commands(parsed_data.get("employer_history"), parsed_data.get("projects")),
|
||||
"family_line_ids": self._prepare_family_line_commands(parsed_data.get("family_details")),
|
||||
"certification_line_ids": self._prepare_certification_line_commands(parsed_data.get("certifications")),
|
||||
"project_line_ids": self._prepare_project_line_commands(parsed_data.get("projects")),
|
||||
"other_line_ids": self._prepare_other_line_commands(self._get_other_parsed_data(parsed_data)),
|
||||
"request_id": parsed_data.get("request_id"),
|
||||
"start_date": parsed_data.get("start_date"),
|
||||
"end_date": parsed_data.get("end_date"),
|
||||
|
|
@ -2245,9 +2304,158 @@ class HrRecruitmentAutoDocWizardLine(models.TransientModel):
|
|||
"degree": self.degree,
|
||||
"skills": self._text_to_list(self.skills_text),
|
||||
"summary": self.summary,
|
||||
"education_history": self._get_education_history_from_lines(),
|
||||
"employer_history": self._get_employer_history_from_lines(),
|
||||
"family_details": self._get_family_details_from_lines(),
|
||||
"certifications": self._get_certifications_from_lines(),
|
||||
"projects": self._get_projects_from_lines(),
|
||||
})
|
||||
data.update(self._get_other_payload_from_lines())
|
||||
return data
|
||||
|
||||
def _prepare_education_line_commands(self, education_history):
|
||||
commands = [(5, 0, 0)]
|
||||
for education in self._normalize_dict_list(education_history):
|
||||
commands.append((0, 0, {
|
||||
"education_type": education.get("education_type"),
|
||||
"specialization": education.get("specialization") or education.get("name") or education.get("degree"),
|
||||
"university": education.get("university") or education.get("institution") or education.get("college"),
|
||||
"start_year": education.get("start_year"),
|
||||
"end_year": education.get("end_year"),
|
||||
"marks_or_grade": education.get("marks_or_grade") or education.get("marks") or education.get("grade") or education.get("cgpa") or education.get("percentage"),
|
||||
}))
|
||||
return commands
|
||||
|
||||
def _prepare_employer_line_commands(self, employer_history, projects=None):
|
||||
commands = [(5, 0, 0)]
|
||||
employers = self._normalize_shared_employer_descriptions(
|
||||
self._normalize_dict_list(employer_history),
|
||||
projects,
|
||||
)
|
||||
for employer in employers:
|
||||
commands.append((0, 0, {
|
||||
"company_name": employer.get("company_name") or employer.get("employer"),
|
||||
"designation": employer.get("designation") or employer.get("role") or employer.get("job_title"),
|
||||
"date_of_joining": employer.get("date_of_joining") or employer.get("start_date"),
|
||||
"last_working_day": employer.get("last_working_day") or employer.get("end_date"),
|
||||
"ctc": employer.get("ctc") or employer.get("salary"),
|
||||
"work_description": employer.get("work_description") or employer.get("summary") or employer.get("description") or employer.get("responsibilities"),
|
||||
}))
|
||||
return commands
|
||||
|
||||
def _prepare_family_line_commands(self, family_details):
|
||||
commands = [(5, 0, 0)]
|
||||
for member in self._normalize_dict_list(family_details):
|
||||
commands.append((0, 0, {
|
||||
"relation_type": member.get("relation_type") or member.get("relation"),
|
||||
"name": member.get("name"),
|
||||
"contact_no": member.get("contact_no") or member.get("contact") or member.get("phone"),
|
||||
"dob": member.get("dob"),
|
||||
"location": member.get("location") or member.get("address"),
|
||||
}))
|
||||
return commands
|
||||
|
||||
def _prepare_certification_line_commands(self, certifications):
|
||||
commands = [(5, 0, 0)]
|
||||
for certification in self._normalize_value_list(certifications):
|
||||
if isinstance(certification, dict):
|
||||
commands.append((0, 0, {
|
||||
"certification_name": certification.get("certification_name") or certification.get("name") or certification.get("title"),
|
||||
"provider": certification.get("provider") or certification.get("issuer") or certification.get("organization"),
|
||||
"completion_date": certification.get("completion_date") or certification.get("date") or certification.get("year"),
|
||||
}))
|
||||
else:
|
||||
commands.append((0, 0, {"certification_name": str(certification)}))
|
||||
return commands
|
||||
|
||||
def _prepare_project_line_commands(self, projects):
|
||||
commands = [(5, 0, 0)]
|
||||
for project in self._normalize_dict_list(projects):
|
||||
technologies = project.get("technologies")
|
||||
commands.append((0, 0, {
|
||||
"project_name": project.get("project_name") or project.get("name") or project.get("title"),
|
||||
"role": project.get("role"),
|
||||
"technologies": ", ".join(technologies) if isinstance(technologies, list) else technologies,
|
||||
"duration": project.get("duration"),
|
||||
"description": project.get("description") or project.get("summary") or project.get("work_description"),
|
||||
}))
|
||||
return commands
|
||||
|
||||
def _prepare_other_line_commands(self, other_payload):
|
||||
commands = [(5, 0, 0)]
|
||||
for key, value in (other_payload or {}).items():
|
||||
commands.append((0, 0, {
|
||||
"key": key,
|
||||
"value": self._json_to_text(value) or str(value),
|
||||
}))
|
||||
return commands
|
||||
|
||||
def _get_education_history_from_lines(self):
|
||||
return [line._to_payload() for line in self.education_line_ids if line._to_payload()]
|
||||
|
||||
def _get_employer_history_from_lines(self):
|
||||
return [line._to_payload() for line in self.employer_line_ids if line._to_payload()]
|
||||
|
||||
def _get_family_details_from_lines(self):
|
||||
return [line._to_payload() for line in self.family_line_ids if line._to_payload()]
|
||||
|
||||
def _get_certifications_from_lines(self):
|
||||
return [line._to_payload() for line in self.certification_line_ids if line._to_payload()]
|
||||
|
||||
def _get_projects_from_lines(self):
|
||||
return [line._to_payload() for line in self.project_line_ids if line._to_payload()]
|
||||
|
||||
def _get_other_payload_from_lines(self):
|
||||
return {
|
||||
line.key: line._json_value()
|
||||
for line in self.other_line_ids
|
||||
if line.key and line.value not in (False, None, "")
|
||||
}
|
||||
|
||||
def _normalize_dict_list(self, value):
|
||||
if isinstance(value, list):
|
||||
return [item for item in value if isinstance(item, dict)]
|
||||
if isinstance(value, dict):
|
||||
return [value]
|
||||
return []
|
||||
|
||||
def _normalize_value_list(self, value):
|
||||
if isinstance(value, list):
|
||||
return [item for item in value if item not in (False, None, "", {})]
|
||||
if value in (False, None, "", {}):
|
||||
return []
|
||||
return [value]
|
||||
|
||||
def _normalize_shared_employer_descriptions(self, employers, projects=None):
|
||||
if len(employers) < 2:
|
||||
return employers
|
||||
|
||||
descriptions = [
|
||||
employer.get("work_description") or employer.get("summary") or employer.get("description") or employer.get("responsibilities")
|
||||
for employer in employers
|
||||
]
|
||||
descriptions = [description for description in descriptions if description]
|
||||
if descriptions:
|
||||
shared_description = max(descriptions, key=lambda item: len(str(item)))
|
||||
else:
|
||||
project_descriptions = []
|
||||
for project in self._normalize_dict_list(projects):
|
||||
description = project.get("description") or project.get("summary") or project.get("work_description")
|
||||
if description:
|
||||
project_descriptions.append("%s: %s" % (
|
||||
project.get("project_name") or project.get("name") or project.get("title") or "Project",
|
||||
description,
|
||||
))
|
||||
shared_description = "\n".join(project_descriptions) if project_descriptions else False
|
||||
|
||||
if not shared_description:
|
||||
return employers
|
||||
|
||||
for employer in employers:
|
||||
if not (employer.get("work_description") or employer.get("summary") or employer.get("description") or employer.get("responsibilities")):
|
||||
employer["work_description"] = shared_description
|
||||
return employers
|
||||
|
||||
def _list_to_text(self, value):
|
||||
if not value:
|
||||
return False
|
||||
|
|
@ -2260,6 +2468,47 @@ class HrRecruitmentAutoDocWizardLine(models.TransientModel):
|
|||
return []
|
||||
return [item.strip(" -,\t") for item in str(value).splitlines() if item.strip(" -,\t")]
|
||||
|
||||
def _json_to_text(self, value):
|
||||
if value in (False, None, "", [], {}):
|
||||
return False
|
||||
return json.dumps(value, indent=2, ensure_ascii=False)
|
||||
|
||||
def _text_to_json_list(self, value):
|
||||
parsed = self._text_to_json_value(value, [])
|
||||
if isinstance(parsed, list):
|
||||
return [item for item in parsed if item not in (False, None, "", {})]
|
||||
if isinstance(parsed, dict):
|
||||
return [parsed]
|
||||
return []
|
||||
|
||||
def _text_to_json_dict(self, value):
|
||||
parsed = self._text_to_json_value(value, {})
|
||||
return parsed if isinstance(parsed, dict) else {}
|
||||
|
||||
def _text_to_json_value(self, value, default):
|
||||
if not value:
|
||||
return default
|
||||
try:
|
||||
return json.loads(value)
|
||||
except Exception:
|
||||
return default
|
||||
|
||||
def _get_other_parsed_data(self, parsed_data):
|
||||
known_keys = {
|
||||
"full_name", "first_name", "last_name", "email", "phone", "alternate_phone",
|
||||
"linkedin_profile", "current_location", "current_organization",
|
||||
"total_experience_years", "relevant_experience_years", "notice_period",
|
||||
"degree", "skills", "summary", "education_history", "employer_history",
|
||||
"family_details", "certifications", "projects", "request_id", "start_date", "end_date", "site_location",
|
||||
"job_title", "job_summary", "requirements", "primary_skills",
|
||||
"secondary_skills", "budget", "experience_years", "job_category",
|
||||
}
|
||||
return {
|
||||
key: value
|
||||
for key, value in (parsed_data or {}).items()
|
||||
if key not in known_keys and value not in (False, None, "", [], {})
|
||||
}
|
||||
|
||||
def _float_or_zero(self, value):
|
||||
if value in (False, None, ""):
|
||||
return 0.0
|
||||
|
|
@ -2267,3 +2516,166 @@ class HrRecruitmentAutoDocWizardLine(models.TransientModel):
|
|||
return float(value)
|
||||
except Exception:
|
||||
return 0.0
|
||||
|
||||
|
||||
class HrRecruitmentAutoDocEducationLine(models.TransientModel):
|
||||
_name = "hr.recruitment.auto.doc.education.line"
|
||||
_description = "HR Recruitment Auto Document Education Line"
|
||||
_order = "id"
|
||||
|
||||
wizard_line_id = fields.Many2one(
|
||||
"hr.recruitment.auto.doc.wizard.line",
|
||||
required=True,
|
||||
ondelete="cascade",
|
||||
)
|
||||
education_type = fields.Char()
|
||||
specialization = fields.Char()
|
||||
university = fields.Char()
|
||||
start_year = fields.Char()
|
||||
end_year = fields.Char()
|
||||
marks_or_grade = fields.Char()
|
||||
|
||||
def _to_payload(self):
|
||||
self.ensure_one()
|
||||
payload = {
|
||||
"education_type": self.education_type,
|
||||
"specialization": self.specialization,
|
||||
"university": self.university,
|
||||
"start_year": self.start_year,
|
||||
"end_year": self.end_year,
|
||||
"marks_or_grade": self.marks_or_grade,
|
||||
}
|
||||
return {key: value for key, value in payload.items() if value not in (False, None, "")}
|
||||
|
||||
|
||||
class HrRecruitmentAutoDocEmployerLine(models.TransientModel):
|
||||
_name = "hr.recruitment.auto.doc.employer.line"
|
||||
_description = "HR Recruitment Auto Document Employer Line"
|
||||
_order = "id"
|
||||
|
||||
wizard_line_id = fields.Many2one(
|
||||
"hr.recruitment.auto.doc.wizard.line",
|
||||
required=True,
|
||||
ondelete="cascade",
|
||||
)
|
||||
company_name = fields.Char()
|
||||
designation = fields.Char()
|
||||
date_of_joining = fields.Char()
|
||||
last_working_day = fields.Char()
|
||||
ctc = fields.Char()
|
||||
work_description = fields.Text()
|
||||
|
||||
def _to_payload(self):
|
||||
self.ensure_one()
|
||||
payload = {
|
||||
"company_name": self.company_name,
|
||||
"designation": self.designation,
|
||||
"date_of_joining": self.date_of_joining,
|
||||
"last_working_day": self.last_working_day,
|
||||
"ctc": self.ctc,
|
||||
"work_description": self.work_description,
|
||||
}
|
||||
return {key: value for key, value in payload.items() if value not in (False, None, "")}
|
||||
|
||||
|
||||
class HrRecruitmentAutoDocFamilyLine(models.TransientModel):
|
||||
_name = "hr.recruitment.auto.doc.family.line"
|
||||
_description = "HR Recruitment Auto Document Family Line"
|
||||
_order = "id"
|
||||
|
||||
wizard_line_id = fields.Many2one(
|
||||
"hr.recruitment.auto.doc.wizard.line",
|
||||
required=True,
|
||||
ondelete="cascade",
|
||||
)
|
||||
relation_type = fields.Char()
|
||||
name = fields.Char()
|
||||
contact_no = fields.Char()
|
||||
dob = fields.Char()
|
||||
location = fields.Char()
|
||||
|
||||
def _to_payload(self):
|
||||
self.ensure_one()
|
||||
payload = {
|
||||
"relation_type": self.relation_type,
|
||||
"name": self.name,
|
||||
"contact_no": self.contact_no,
|
||||
"dob": self.dob,
|
||||
"location": self.location,
|
||||
}
|
||||
return {key: value for key, value in payload.items() if value not in (False, None, "")}
|
||||
|
||||
|
||||
class HrRecruitmentAutoDocCertificationLine(models.TransientModel):
|
||||
_name = "hr.recruitment.auto.doc.certification.line"
|
||||
_description = "HR Recruitment Auto Document Certification Line"
|
||||
_order = "id"
|
||||
|
||||
wizard_line_id = fields.Many2one(
|
||||
"hr.recruitment.auto.doc.wizard.line",
|
||||
required=True,
|
||||
ondelete="cascade",
|
||||
)
|
||||
certification_name = fields.Char()
|
||||
provider = fields.Char()
|
||||
completion_date = fields.Char()
|
||||
|
||||
def _to_payload(self):
|
||||
self.ensure_one()
|
||||
payload = {
|
||||
"certification_name": self.certification_name,
|
||||
"provider": self.provider,
|
||||
"completion_date": self.completion_date,
|
||||
}
|
||||
return {key: value for key, value in payload.items() if value not in (False, None, "")}
|
||||
|
||||
|
||||
class HrRecruitmentAutoDocProjectLine(models.TransientModel):
|
||||
_name = "hr.recruitment.auto.doc.project.line"
|
||||
_description = "HR Recruitment Auto Document Project Line"
|
||||
_order = "id"
|
||||
|
||||
wizard_line_id = fields.Many2one(
|
||||
"hr.recruitment.auto.doc.wizard.line",
|
||||
required=True,
|
||||
ondelete="cascade",
|
||||
)
|
||||
project_name = fields.Char()
|
||||
role = fields.Char()
|
||||
technologies = fields.Char()
|
||||
duration = fields.Char()
|
||||
description = fields.Text()
|
||||
|
||||
def _to_payload(self):
|
||||
self.ensure_one()
|
||||
payload = {
|
||||
"project_name": self.project_name,
|
||||
"role": self.role,
|
||||
"technologies": [item.strip() for item in (self.technologies or "").split(",") if item.strip()],
|
||||
"duration": self.duration,
|
||||
"description": self.description,
|
||||
}
|
||||
return {key: value for key, value in payload.items() if value not in (False, None, "", [])}
|
||||
|
||||
|
||||
class HrRecruitmentAutoDocOtherLine(models.TransientModel):
|
||||
_name = "hr.recruitment.auto.doc.other.line"
|
||||
_description = "HR Recruitment Auto Document Other Parsed Data Line"
|
||||
_order = "id"
|
||||
|
||||
wizard_line_id = fields.Many2one(
|
||||
"hr.recruitment.auto.doc.wizard.line",
|
||||
required=True,
|
||||
ondelete="cascade",
|
||||
)
|
||||
key = fields.Char(required=True)
|
||||
value = fields.Text()
|
||||
|
||||
def _json_value(self):
|
||||
self.ensure_one()
|
||||
if not self.value:
|
||||
return False
|
||||
try:
|
||||
return json.loads(self.value)
|
||||
except Exception:
|
||||
return self.value
|
||||
|
|
|
|||
|
|
@ -95,6 +95,58 @@
|
|||
<page string="Skills">
|
||||
<field name="skills_text" nolabel="1" placeholder="Enter one skill per line"/>
|
||||
</page>
|
||||
<page string="Education">
|
||||
<field name="education_line_ids" nolabel="1">
|
||||
<list editable="bottom">
|
||||
<field name="education_type"/>
|
||||
<field name="specialization"/>
|
||||
<field name="university"/>
|
||||
<field name="start_year"/>
|
||||
<field name="end_year"/>
|
||||
<field name="marks_or_grade"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Experience">
|
||||
<field name="employer_line_ids" nolabel="1">
|
||||
<list editable="bottom">
|
||||
<field name="company_name"/>
|
||||
<field name="designation"/>
|
||||
<field name="date_of_joining"/>
|
||||
<field name="last_working_day"/>
|
||||
<field name="ctc"/>
|
||||
<field name="work_description"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Family">
|
||||
<field name="family_line_ids" nolabel="1">
|
||||
<list editable="bottom">
|
||||
<field name="relation_type"/>
|
||||
<field name="name"/>
|
||||
<field name="contact_no"/>
|
||||
<field name="dob"/>
|
||||
<field name="location"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Certifications">
|
||||
<field name="certification_line_ids" nolabel="1">
|
||||
<list editable="bottom">
|
||||
<field name="certification_name"/>
|
||||
<field name="provider"/>
|
||||
<field name="completion_date"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Other">
|
||||
<field name="other_line_ids" nolabel="1">
|
||||
<list editable="bottom">
|
||||
<field name="key"/>
|
||||
<field name="value"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
|
||||
<notebook invisible="target_model != 'job_recruitment'">
|
||||
|
|
|
|||
Loading…
Reference in New Issue