Skip to content

fix: apply company filter in leave period in leave control panel doctype#4234

Open
anwarpatelnoori wants to merge 3 commits intofrappe:developfrom
anwarpatelnoori:TASK-2026-00001
Open

fix: apply company filter in leave period in leave control panel doctype#4234
anwarpatelnoori wants to merge 3 commits intofrappe:developfrom
anwarpatelnoori:TASK-2026-00001

Conversation

@anwarpatelnoori
Copy link
Contributor

@anwarpatelnoori anwarpatelnoori commented Mar 14, 2026

Closes #4233

Please provide enough information so that others can review your pull request:

Information about bug

Pre-requisites

  • Setup multi-company environment

    • Example: Demo Company and Test Company
  • Create Leave Period for each company.

Steps to Reproduce

  • In Leave Control Panel By default:

    • Default Company and its latest Leave Period will be auto-selected.

    • Employees of that company will be rendered in the table.

    • Example:

      • Default Company: Demo Company
      • Leave Period: HR-LPR-2026-00001
  • Now select the Leave Period of another company.

    • Example:

      • Company: Test Company
      • Leave Period: HR-LPR-2026-00004

Bug

  • The employee table still shows employees of the default company (Demo Company).

  • Employees belonging to the selected company (Test Company) are not rendered.

  • If employees are selected and Leave Policy is assigned:

    • The Leave Policy Assignment uses the Leave Period of the default company, not the selected one.

Explain the details for making this change. What existing problem does the pull request solve?

  • As Company is a mandatory field in Leave Period, and Leave Period is the default for Dates Based On.
  • Move the Company field from Quick Filters to the main section and make it mandatory when Dates Based On = Leave Period.
  • Set the default company on setup and apply a company filter to the Leave Period field.
  • In set_leave_details, use frm.doc.company instead of the default company.

Screen Recording

Before

Leave.Control.Panel.Bug.1.mp4

After

Leave.Control.Bug.Fixed.mp4

Summary by CodeRabbit

  • New Features
    • Company now pre-populates from the current company on form load for faster entry.
    • Changing company always refreshes department and leave-period filters and the employee list.
    • Company is required when filtering by Leave Period.
    • Form field order improved and designation repositioned for clearer layout and consistency.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 14, 2026

Walkthrough

The Leave Control Panel changes make company the propagated context: on setup the form uses the current company value, set_leave_details reads frm.doc.company, and the company is added as a filter for the leave_period query. The company change handler now always calls set_leave_details, always applies a department filter scoped to the selected company, and always triggers get_employees. The doctype field order now includes company, the Company field gains mandatory_depends_on: "eval:doc.dates_based_on == 'Leave Period'", and a duplicate designation entry was removed.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: applying a company filter to the Leave Period field in the Leave Control Panel doctype.
Linked Issues check ✅ Passed The PR successfully addresses all coding requirements from issue #4233: company field moved to main form with conditional mandatory requirement, company filter applied to Leave Period field, and frm.doc.company used instead of default company.
Out of Scope Changes check ✅ Passed All changes are directly related to the linked issue #4233. The modifications to company initialization, department filtering, leave period querying, and employee fetching are all necessary to resolve the multi-company bug.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can use Trivy to scan for security misconfigurations and secrets in Infrastructure as Code files.

Add a .trivyignore file to your project to customize which findings Trivy reports.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
hrms/hr/doctype/leave_control_panel/leave_control_panel.json (1)

1-3: ⚠️ Potential issue | 🟡 Minor

Fix Prettier formatting.

Same as the JS file - run prettier --write to resolve the formatting check failure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hrms/hr/doctype/leave_control_panel/leave_control_panel.json` around lines 1
- 3, The JSON file leave_control_panel.json (and the corresponding JS file
leave_control_panel.js) fails formatting checks; run Prettier to fix it by
executing prettier --write on these files (or your repo) to normalize
indentation and trailing commas so the JSON keys/arrays match Prettier output;
commit the reformatted files (ensure leave_control_panel.json valid JSON after
formatting).
hrms/hr/doctype/leave_control_panel/leave_control_panel.js (1)

1-4: ⚠️ Potential issue | 🟡 Minor

Fix Prettier formatting to pass CI.

The pipeline is failing due to a Prettier formatting check. Run prettier --write on this file to resolve the formatting issues.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hrms/hr/doctype/leave_control_panel/leave_control_panel.js` around lines 1 -
4, Run Prettier on the JavaScript file to fix formatting so CI passes: format
leave_control_panel.js (the module that starts with frappe.ui.form.on("Leave
Control Panel", { ... })) by running prettier --write against this file or
applying your project's Prettier configuration, then commit the reformatted file
so the Prettier check in the pipeline is satisfied.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@hrms/hr/doctype/leave_control_panel/leave_control_panel.js`:
- Around line 38-44: The current frm.set_query for the "leave_period" field
replaces the earlier query and only filters by company, causing inactive periods
to surface; update the frm.set_query callback for "leave_period" to include both
filters (company: frm.doc.company and is_active: 1) so the dropdown only shows
active leave periods for the selected company (locate the set_query call for
"leave_period" in leave_control_panel.js and add the is_active filter to the
returned filters object).

---

Outside diff comments:
In `@hrms/hr/doctype/leave_control_panel/leave_control_panel.js`:
- Around line 1-4: Run Prettier on the JavaScript file to fix formatting so CI
passes: format leave_control_panel.js (the module that starts with
frappe.ui.form.on("Leave Control Panel", { ... })) by running prettier --write
against this file or applying your project's Prettier configuration, then commit
the reformatted file so the Prettier check in the pipeline is satisfied.

In `@hrms/hr/doctype/leave_control_panel/leave_control_panel.json`:
- Around line 1-3: The JSON file leave_control_panel.json (and the corresponding
JS file leave_control_panel.js) fails formatting checks; run Prettier to fix it
by executing prettier --write on these files (or your repo) to normalize
indentation and trailing commas so the JSON keys/arrays match Prettier output;
commit the reformatted files (ensure leave_control_panel.json valid JSON after
formatting).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fd072326-17b7-4683-a380-185927465db5

📥 Commits

Reviewing files that changed from the base of the PR and between f95c031 and 15a7827.

📒 Files selected for processing (2)
  • hrms/hr/doctype/leave_control_panel/leave_control_panel.js
  • hrms/hr/doctype/leave_control_panel/leave_control_panel.json

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
hrms/hr/doctype/leave_control_panel/leave_control_panel.js (1)

104-117: ⚠️ Potential issue | 🟠 Major

Tie the async leave-period response to the company that initiated it.

The callback currently reads frm.doc.company when the promise resolves. If the user switches company twice before the first request returns, a slower response can still write the old company's leave_period into the new company context. Returning the promise here also lets company(frm) wait for the refresh.

🐛 Proposed fix
 	set_leave_details(frm) {
-		frm.call("get_latest_leave_period").then((r) => {
+		const company = frm.doc.company;
+		if (!company) return Promise.resolve();
+
+		return frm.call("get_latest_leave_period").then((r) => {
+			if (frm.doc.company !== company) return;
+
 			frm.set_value({
 				dates_based_on: "Leave Period",
 				from_date: frappe.datetime.get_today(),
 				to_date: null,
 				leave_period: r.message,
 				carry_forward: 1,
 				allocate_based_on_leave_policy: 1,
 				leave_type: null,
 				no_of_days: 0,
 				leave_policy: null,
-				company: frm.doc.company,
 			});
 		});
 	},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hrms/hr/doctype/leave_control_panel/leave_control_panel.js` around lines 104
- 117, The async callback in set_leave_details uses frm.doc.company when the
get_latest_leave_period promise resolves, causing a race where a stale company
can be written; change set_leave_details so it captures the current company
before calling frm.call (e.g., const currentCompany = frm.doc.company),
pass/compare that captured value in the then handler and only call frm.set_value
when it still matches, and return the frm.call Promise so callers like
company(frm) can await the refresh; reference: set_leave_details,
get_latest_leave_period, frm.set_value, company(frm).
♻️ Duplicate comments (1)
hrms/hr/doctype/leave_control_panel/leave_control_panel.js (1)

38-44: ⚠️ Potential issue | 🟠 Major

Keep a single leave_period query definition.

This second frm.set_query("leave_period", ...) diverges from the one at Line 173 and drops is_active: 1, so inactive periods reappear after a company change and the final filter depends on which callback ran last.

🐛 Proposed fix
-		frm.set_query("leave_period", function () {
-			return {
-				filters: {
-					company: frm.doc.company,
-				},
-			};
-		});
// Prefer defining `leave_period` once in `set_query()`:
frm.set_query("leave_period", function () {
	return {
		filters: {
			is_active: 1,
			company: frm.doc.company,
		},
	};
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hrms/hr/doctype/leave_control_panel/leave_control_panel.js` around lines 38 -
44, There are two conflicting frm.set_query("leave_period", ...) definitions
causing the is_active: 1 filter to be dropped depending on which callback runs
last; remove the duplicate and keep a single frm.set_query("leave_period", ...)
that returns filters including both is_active: 1 and company: frm.doc.company so
inactive periods are always excluded (update the existing set_query call used
for leave_period rather than adding a second one).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@hrms/hr/doctype/leave_control_panel/leave_control_panel.js`:
- Around line 29-45: The company handler runs frm.trigger("get_employees")
before the async server call in set_leave_details finishes, causing stale
leave_period; modify set_leave_details(frm) to return the promise from its
frm.call (so it resolves after get_latest_leave_period completes) and make the
company function async so it awaits frm.trigger("set_leave_details") when
frm.doc.dates_based_on === "Leave Period"; keep the department/leave_period
frm.set_query calls intact before awaiting and only call
frm.trigger("get_employees") after the await to ensure employees load with the
updated leave_period.

---

Outside diff comments:
In `@hrms/hr/doctype/leave_control_panel/leave_control_panel.js`:
- Around line 104-117: The async callback in set_leave_details uses
frm.doc.company when the get_latest_leave_period promise resolves, causing a
race where a stale company can be written; change set_leave_details so it
captures the current company before calling frm.call (e.g., const currentCompany
= frm.doc.company), pass/compare that captured value in the then handler and
only call frm.set_value when it still matches, and return the frm.call Promise
so callers like company(frm) can await the refresh; reference:
set_leave_details, get_latest_leave_period, frm.set_value, company(frm).

---

Duplicate comments:
In `@hrms/hr/doctype/leave_control_panel/leave_control_panel.js`:
- Around line 38-44: There are two conflicting frm.set_query("leave_period",
...) definitions causing the is_active: 1 filter to be dropped depending on
which callback runs last; remove the duplicate and keep a single
frm.set_query("leave_period", ...) that returns filters including both
is_active: 1 and company: frm.doc.company so inactive periods are always
excluded (update the existing set_query call used for leave_period rather than
adding a second one).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3af591d2-5f3b-46b1-81c0-823009f4b4e5

📥 Commits

Reviewing files that changed from the base of the PR and between 15a7827 and 63f4c9d.

📒 Files selected for processing (1)
  • hrms/hr/doctype/leave_control_panel/leave_control_panel.js

Comment on lines 29 to 45
company: function (frm) {
if (frm.doc.company) {
frm.set_query("department", function () {
return {
filters: {
company: frm.doc.company,
},
};
});
}
frm.trigger("set_leave_details");
frm.set_query("department", function () {
return {
filters: {
company: frm.doc.company,
},
};
});
frm.set_query("leave_period", function () {
return {
filters: {
company: frm.doc.company,
},
};
});
frm.trigger("get_employees");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's find and examine the leave_control_panel.js file
find . -name "leave_control_panel.js" -type f

Repository: frappe/hrms

Length of output: 115


🏁 Script executed:

# Read the full file to understand the implementation
cat -n hrms/hr/doctype/leave_control_panel/leave_control_panel.js

Repository: frappe/hrms

Length of output: 5963


Wait for leave details to sync when company changes.

set_leave_details() initiates a server call on line 30, but get_employees() on line 38 runs immediately. When switching companies in "Leave Period" mode, the employee load receives the previous company's stale leave_period value. Make set_leave_details() return its promise and await it:

set_leave_details(frm) {
    return frm.call("get_latest_leave_period").then((r) => {
        frm.set_value({
            dates_based_on: "Leave Period",
            from_date: frappe.datetime.get_today(),
            to_date: null,
            leave_period: r.message,
            carry_forward: 1,
            allocate_based_on_leave_policy: 1,
            leave_type: null,
            no_of_days: 0,
            leave_policy: null,
            company: frm.doc.company,
        });
    });
},

Then in the company function:

company: async function (frm) {
    frm.set_query("department", function () {
        return {
            filters: {
                company: frm.doc.company,
            },
        };
    });
    if (frm.doc.dates_based_on === "Leave Period") {
        await frm.trigger("set_leave_details");
    }
    frm.trigger("get_employees");
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hrms/hr/doctype/leave_control_panel/leave_control_panel.js` around lines 29 -
45, The company handler runs frm.trigger("get_employees") before the async
server call in set_leave_details finishes, causing stale leave_period; modify
set_leave_details(frm) to return the promise from its frm.call (so it resolves
after get_latest_leave_period completes) and make the company function async so
it awaits frm.trigger("set_leave_details") when frm.doc.dates_based_on ===
"Leave Period"; keep the department/leave_period frm.set_query calls intact
before awaiting and only call frm.trigger("get_employees") after the await to
ensure employees load with the updated leave_period.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
hrms/hr/doctype/leave_control_panel/leave_control_panel.js (1)

29-39: ⚠️ Potential issue | 🟠 Major

Race condition: get_employees runs before set_leave_details completes.

set_leave_details (line 30) makes an async server call via frm.call("get_latest_leave_period"), but get_employees (line 38) executes immediately without waiting. When switching companies in "Leave Period" mode, employees may load with stale leave_period/date values.

🐛 Proposed fix: await set_leave_details before fetching employees
-	company: function (frm) {
-		frm.trigger("set_leave_details");
+	company: async function (frm) {
 		frm.set_query("department", function () {
 			return {
 				filters: {
 					company: frm.doc.company,
 				},
 			};
 		});
+		frm.set_query("leave_period", function () {
+			return {
+				filters: {
+					is_active: 1,
+					company: frm.doc.company,
+				},
+			};
+		});
+		if (frm.doc.dates_based_on === "Leave Period") {
+			await frm.trigger("set_leave_details");
+		}
 		frm.trigger("get_employees");
 	},

And modify set_leave_details to return the promise:

 	set_leave_details(frm) {
-		frm.call("get_latest_leave_period").then((r) => {
+		return frm.call("get_latest_leave_period").then((r) => {
 			frm.set_value({
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hrms/hr/doctype/leave_control_panel/leave_control_panel.js` around lines 29 -
39, The company handler calls frm.trigger("set_leave_details") but immediately
calls frm.trigger("get_employees"), causing a race; change the company function
to wait for set_leave_details to complete (await
frm.trigger("set_leave_details") or use .then) before calling
frm.trigger("get_employees"), and update set_leave_details to return the promise
from its async call (the frm.call("get_latest_leave_period") promise) so that
frm.trigger resolves only after the server call finishes; this ensures
get_employees runs after set_leave_details (and its get_latest_leave_period)
completes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@hrms/hr/doctype/leave_control_panel/leave_control_panel.js`:
- Around line 29-39: The company handler calls frm.trigger("set_leave_details")
but immediately calls frm.trigger("get_employees"), causing a race; change the
company function to wait for set_leave_details to complete (await
frm.trigger("set_leave_details") or use .then) before calling
frm.trigger("get_employees"), and update set_leave_details to return the promise
from its async call (the frm.call("get_latest_leave_period") promise) so that
frm.trigger resolves only after the server call finishes; this ensures
get_employees runs after set_leave_details (and its get_latest_leave_period)
completes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d1a5c586-2bb6-493b-8eb1-05976514e111

📥 Commits

Reviewing files that changed from the base of the PR and between 63f4c9d and b6f179e.

📒 Files selected for processing (1)
  • hrms/hr/doctype/leave_control_panel/leave_control_panel.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug in Leave Control Panel

1 participant