Skip to main content

Overview

ION Career extends Frappe’s standard recruitment doctypes with custom fields to support screening questions, applicant scoring, and answer storage.
All custom fields are defined in ion_career/fixtures/custom_field.json

Job Opening Extensions

The Job Opening doctype receives one custom field:

Job Question Set

custom_job_question_set
Link
required
Links to the Job Question Set doctype. This field:
  • Is inserted after the designation field
  • Is required (reqd: 1) - you must select a question set
  • Links to “Job Question Set” doctype
  • Allows you to select which screening questions to ask applicants
Defined in custom_field.json:1-58

Usage

When creating a Job Opening:
1

Fill Basic Details

Enter job title, designation, company, etc.
2

Select Question Set

Choose an existing Job Question Set from the dropdown. This is required.
3

Publish

Once published, applicants will see questions from the selected set on the web form.
You cannot publish a Job Opening without selecting a question set, as this field is required.

Job Applicant Extensions

The Job Applicant doctype receives seven custom fields organized into a dedicated tab:

Questions Tab

custom_questions
Tab Break
Creates a new “Questions” tab in the Job Applicant form.Inserted after upper_range.Defined in custom_field.json:173-229
All question-related fields appear under this tab.

Section Break

custom_section_break_msyzp
Section Break
Provides visual separation within the Questions tab.Inserted after custom_questions tab.Defined in custom_field.json:116-172

Score Field

custom_score
Data
Displays the automatically calculated applicant score based on screening question answers.Properties:
  • Read-only - automatically calculated, users cannot edit
  • Translatable
  • Inserted after applicant_rating
  • Appears in the main details section (not in Questions tab)
Calculation: See Applicant Scoring for details.Defined in custom_field.json:59-115

Job Question Answers (JSON)

Question Answers (Table)

custom_question_answers
Table
Displays question answers in a human-readable table format.Properties:
  • Links to “Job Applicant Question Answer” child doctype
  • Shows question text, fieldname, answer, and job opening
  • Populated automatically by the process_job_questions handler
  • Visible in the Questions tab
Columns:
  • Question
  • Fieldname
  • Answer
  • Job Opening
Defined in custom_field.json:287-343

Questions HTML Display

custom_questions_html
HTML
HTML field for custom rendering of questions and answers.Properties:
  • Can be used for custom visualizations
  • Inserted after custom_question_answers table
  • Currently unused by default (available for customization)
Defined in custom_field.json:344-400

Field Insertion Order

The fields are inserted in a specific order to create a logical flow:
1

Job Opening Section

  1. Standard fields (job title, company, etc.)
  2. designation
  3. custom_job_question_set ← Custom field
2

Job Applicant - Main Form

  1. Standard fields (name, email, etc.)
  2. applicant_rating
  3. custom_score ← Custom field (visible)
  4. More standard fields…
  5. upper_range
3

Job Applicant - Questions Tab

  1. custom_questions ← Tab break
  2. custom_section_break_msyzp ← Section
  3. custom_job_question_answers ← Hidden JSON
  4. custom_question_answers ← Visible table
  5. custom_questions_html ← HTML field

Custom Field Properties Reference

All custom fields share these common properties:
doctype
string
Always “Custom Field”
dt
string
Target doctype: “Job Opening” or “Job Applicant”
module
string
Always “ION Career”
is_system_generated
boolean
Always false - these are custom, not auto-generated
insert_after
string
The field after which this custom field should be inserted

Data Flow

Accessing Custom Fields

In Python

import frappe

# Get question set from job opening
job_opening = frappe.get_doc("Job Opening", "Software Engineer")
question_set = job_opening.custom_job_question_set

# Get applicant score
applicant = frappe.get_doc("Job Applicant", "APP-001")
score = applicant.custom_score

# Get question answers
answers_json = applicant.custom_job_question_answers
answers_table = applicant.custom_question_answers

for row in answers_table:
    print(f"{row.question}: {row.answer}")

In JavaScript

// In Job Opening form
frappe.ui.form.on('Job Opening', {
    custom_job_question_set: function(frm) {
        console.log('Selected question set:', frm.doc.custom_job_question_set);
    }
});

// In Job Applicant form
frappe.ui.form.on('Job Applicant', {
    refresh: function(frm) {
        console.log('Applicant score:', frm.doc.custom_score);
    }
});

In Reports

-- Job openings with their question sets
SELECT 
    name,
    job_title,
    custom_job_question_set
FROM `tabJob Opening`
WHERE status = 'Open';

-- Applicants with scores
SELECT 
    name,
    applicant_name,
    job_title,
    custom_score,
    applicant_rating
FROM `tabJob Applicant`
ORDER BY CAST(custom_score AS DECIMAL) DESC;

Permissions

Custom fields inherit permissions from their parent doctype:
  • Job Opening: Controlled by Job Opening doctype permissions
  • Job Applicant: Controlled by Job Applicant doctype permissions
The custom_score field is read-only, so even users with write permissions on Job Applicant cannot manually edit scores.

Migration and Installation

These custom fields are installed automatically via fixtures when you:
  1. Install the ION Career app
  2. Run bench migrate
bench install-app ion_career
# Custom fields are created automatically
If you uninstall the app, custom fields will be removed and all data in those fields will be lost.

Best Practices

Don't Modify Field Names

Changing fieldnames will break the integration between web forms and scoring logic

Backup Before Changes

Always backup your site before making changes to custom field definitions

Test in Development

Test custom field changes in a development environment first

Document Customizations

If you extend these fields, document your changes for future reference

Extending Custom Fields

You can add more custom fields to support additional features:

Example: Adding Interview Score

{
  "dt": "Job Applicant",
  "fieldname": "custom_interview_score",
  "fieldtype": "Float",
  "label": "Interview Score",
  "insert_after": "custom_score",
  "precision": 2
}

Example: Adding Question Notes

{
  "dt": "Job Applicant",
  "fieldname": "custom_screening_notes",
  "fieldtype": "Text Editor",
  "label": "Screening Notes",
  "insert_after": "custom_questions_html"
}
When adding custom fields programmatically, use frappe.get_doc() with the Custom Field doctype or add them to your app’s fixtures.

Build docs developers (and LLMs) love