Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion employee_self_service/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "2.2.3"
__version__ = "2.2.4"
8 changes: 8 additions & 0 deletions employee_self_service/constants/custom_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@
"insert_after": "disabled",
},
],
"Leave Application": [
{
"fieldname": "medical_supporting_document",
"label": "Medical Supporting Document",
"fieldtype": "Attach",
"insert_after": "follow_via_email",
},
],
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copyright (c) 2026, Nesscale Solutions Private Limited and contributors
// For license information, please see license.txt

frappe.ui.form.on("Employee Details Update Request", {
refresh(frm) {
render_diff(frm);
},
});

const FIELD_LABELS = {
first_name: __("Full Name"),
gender: __("Gender"),
date_of_birth: __("Date of Birth"),
date_of_joining: __("Date of Joining"),
cell_number: __("Mobile"),
personal_email: __("Personal Email"),
current_address: __("Current Address"),
emergency_phone_number: __("Emergency Phone Number"),
marital_status: __("Marital Status"),
blood_group: __("Blood Group"),
};

function render_diff(frm) {
const wrapper = frm.fields_dict.table_html.$wrapper;

if (!frm.doc.data) {
wrapper.empty();
return;
}

let data;

try {
data = JSON.parse(frm.doc.data);
} catch {
wrapper.empty();
return;
}

const old_values = data.old || {};
const new_values = data.new || {};

let rows = "";

Object.entries(new_values).forEach(([field, value]) => {

if (field === "education") return;

rows += `
<tr>
<td>${FIELD_LABELS[field] || field}</td>

<td style="background:#fff0f0;">
${frappe.utils.escape_html(String(old_values[field] || "—"))}
</td>

<td style="background:#f0fff4;">
${frappe.utils.escape_html(String(value || "—"))}
</td>
</tr>
`;
});

if (new_values.education?.length) {

const render_education_table = (list = []) => {

if (!list.length) {
return `<div class="text-muted">—</div>`;
}

return `
<table class="table table-sm table-bordered mb-0">
<thead>
<tr>
<th>${__("School/University")}</th>
<th>${__("Qualification")}</th>
<th>${__("Level")}</th>
<th>${__("Year")}</th>
</tr>
</thead>

<tbody>
${list.map(row => `
<tr>
<td>${frappe.utils.escape_html(row.school_univ || "")}</td>
<td>${frappe.utils.escape_html(row.qualification || "")}</td>
<td>${frappe.utils.escape_html(row.level || "")}</td>
<td>${frappe.utils.escape_html(row.year_of_passing || "")}</td>
</tr>
`).join("")}
</tbody>
</table>
`;
};

rows += `
<tr>
<td>${__("Education")}</td>

<td style="background:#fff0f0;">
${render_education_table(old_values.education)}
</td>

<td style="background:#f0fff4;">
${render_education_table(new_values.education)}
</td>
</tr>
`;
}

if (!rows) {
wrapper.html(`
<div class="text-muted">
${__("No changes requested.")}
</div>
`);
return;
}

wrapper.html(`
<table class="table table-bordered">
<thead>
<tr>
<th style="width:20%">
${__("Field")}
</th>

<th style="width:40%">
${__("Old Value")}
</th>

<th style="width:40%">
${__("New Value")}
</th>
</tr>
</thead>

<tbody>
${rows}
</tbody>
</table>
`);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
{
"actions": [],
"autoname": "EUR.#####",
"creation": "2026-05-06 19:17:52.072698",
"doctype": "DocType",
"document_type": "Setup",
"engine": "InnoDB",
"field_order": [
"employee",
"employee_name",
"column_break_header",
"status",
"requested_on",
"section_changes",
"table_html",
"is_applied",
"data",
"amended_from"
],
"fields": [
{
"fieldname": "employee",
"fieldtype": "Link",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Employee",
"options": "Employee",
"reqd": 1
},
{
"fetch_from": "employee.employee_name",
"fieldname": "employee_name",
"fieldtype": "Data",
"label": "Employee Name",
"read_only": 1
},
{
"fieldname": "column_break_header",
"fieldtype": "Column Break"
},
{
"default": "Now",
"fieldname": "requested_on",
"fieldtype": "Datetime",
"label": "Requested On",
"read_only": 1
},
{
"fieldname": "section_changes",
"fieldtype": "Section Break",
"label": "Values Changed"
},
{
"fieldname": "table_html",
"fieldtype": "HTML",
"label": "Change Summary"
},
{
"default": "0",
"fieldname": "is_applied",
"fieldtype": "Check",
"hidden": 1,
"label": "Is Applied"
},
{
"fieldname": "data",
"fieldtype": "Code",
"hidden": 1,
"label": "Change Data (JSON)"
},
{
"default": "Pending",
"fieldname": "status",
"fieldtype": "Select",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Status",
"no_copy": 1,
"options": "Pending\nApproved\nRejected\nCancelled",
"permlevel": 1,
"reqd": 1
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Employee Details Update Request",
"print_hide": 1,
"read_only": 1,
"search_index": 1
}
],
"icon": "fa fa-user-edit",
"is_submittable": 1,
"links": [],
"modified": "2026-05-16 16:03:51.378919",
"modified_by": "Administrator",
"module": "Employee Self Service",
"name": "Employee Details Update Request",
"naming_rule": "Expression",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"export": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"submit": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"read": 1,
"role": "Administrator",
"write": 1
},
{
"create": 1,
"read": 1,
"role": "Employee"
},
{
"amend": 1,
"cancel": 1,
"read": 1,
"role": "HR User",
"submit": 1,
"write": 1
},
{
"permlevel": 1,
"read": 1,
"role": "HR User",
"write": 1
},
{
"amend": 1,
"cancel": 1,
"read": 1,
"role": "HR Manager",
"submit": 1,
"write": 1
},
{
"permlevel": 1,
"read": 1,
"role": "HR Manager",
"write": 1
}
],
"row_format": "Compressed",
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"title_field": "employee_name",
"track_changes": 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Copyright (c) 2026, Nesscale Solutions Private Limited and contributors
# For license information, please see license.txt

import json
import frappe
from frappe import _
from frappe.model.document import Document


class EmployeeDetailsUpdateRequest(Document):
def on_submit(self):
if self.status in ["Pending", "Cancelled"]:
frappe.throw(
_("Only Employee Details Update Requests with status 'Approved' or 'Rejected' can be submitted")
)

if self.status == "Approved" and not self.is_applied:
self.apply_to_employee()
frappe.db.set_value(self.doctype, self.name, "is_applied", 1)

def before_cancel(self):
self.status = "Cancelled"

def on_cancel(self):
if self.is_applied:
self.apply_to_employee(reverse=True)
frappe.db.set_value(self.doctype, self.name, "is_applied", 0)

def apply_to_employee(self, reverse=False):
if not self.data:
return

data = json.loads(self.data)
key = "old" if reverse else "new"
values = data.get(key, {})
education = values.pop("education", None)

emp_doc = frappe.get_doc("Employee", self.employee)

for field, value in values.items():
if field in emp_doc.meta.get_valid_columns():
emp_doc.set(field, value or None)

if education is not None:
emp_doc.set("education", [])

for row in education:
emp_doc.append("education", {
"school_univ": row.get("school_univ"),
"qualification": row.get("qualification"),
"level": row.get("level"),
"year_of_passing": row.get("year_of_passing"),
})

emp_doc.save()
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
frappe.listview_settings["Employee Details Update Request"] = {
add_fields: ["status"],
has_indicator_for_draft: 1,
get_indicator: function (doc) {
const status_color = {
Pending: "orange",
Approved: "green",
Rejected: "red",
Cancelled: "red",
};
return [__(doc.status), status_color[doc.status] || "grey", "status,=," + doc.status];
},
};
Loading
Loading