diff --git a/hrms/api/roster.py b/hrms/api/roster.py
index fa26bc4ef6..3e15d419f2 100644
--- a/hrms/api/roster.py
+++ b/hrms/api/roster.py
@@ -6,6 +6,7 @@
from hrms.hr.doctype.shift_assignment.shift_assignment import ShiftAssignment
from hrms.hr.doctype.shift_assignment_tool.shift_assignment_tool import create_shift_assignment
+from hrms.hr.doctype.shift_schedule.shift_schedule import get_or_insert_shift_schedule
@frappe.whitelist()
@@ -37,7 +38,17 @@ def get_events(
@frappe.whitelist()
-def create_shift_assignment_schedule(
+def get_schedule_from_assignment(shift_schedule_assignment: str):
+ shift_schedule = frappe.db.get_value(
+ "Shift Schedule Assignment", shift_schedule_assignment, "shift_schedule"
+ )
+ frequency = frappe.db.get_value("Shift Schedule", shift_schedule, "frequency")
+ repeat_on_days = frappe.get_all("Assignment Rule Day", filters={"parent": shift_schedule}, pluck="day")
+ return {"frequency": frequency, "repeat_on_days": repeat_on_days}
+
+
+@frappe.whitelist()
+def create_shift_schedule_assignment(
employee: str,
company: str,
shift_type: str,
@@ -46,34 +57,39 @@ def create_shift_assignment_schedule(
end_date: str | None,
repeat_on_days: list[str],
frequency: str,
+ shift_location: str | None = None,
) -> None:
- schedule = frappe.get_doc(
+ shift_schedule = get_or_insert_shift_schedule(shift_type, frequency, repeat_on_days)
+ shift_schedule_assignment = frappe.get_doc(
{
- "doctype": "Shift Assignment Schedule",
- "frequency": frequency,
- "repeat_on_days": [{"day": day} for day in repeat_on_days],
- "enabled": 0 if end_date else 1,
+ "doctype": "Shift Schedule Assignment",
+ "shift_schedule": shift_schedule,
"employee": employee,
"company": company,
- "shift_type": shift_type,
"shift_status": status,
+ "shift_location": shift_location,
+ "enabled": 0 if end_date else 1,
}
).insert()
if not end_date or date_diff(end_date, start_date) <= 90:
- return schedule.create_shifts(start_date, end_date)
+ return shift_schedule_assignment.create_shifts(start_date, end_date)
- frappe.enqueue(schedule.create_shifts, timeout=4500, start_date=start_date, end_date=end_date)
+ frappe.enqueue(
+ shift_schedule_assignment.create_shifts, timeout=4500, start_date=start_date, end_date=end_date
+ )
@frappe.whitelist()
-def delete_shift_assignment_schedule(schedule: str) -> None:
- for shift_assignment in frappe.get_all("Shift Assignment", {"schedule": schedule}, pluck="name"):
+def delete_shift_schedule_assignment(shift_schedule_assignment: str) -> None:
+ for shift_assignment in frappe.get_all(
+ "Shift Assignment", {"shift_schedule_assignment": shift_schedule_assignment}, pluck="name"
+ ):
doc = frappe.get_doc("Shift Assignment", shift_assignment)
if doc.docstatus == 1:
doc.cancel()
frappe.delete_doc("Shift Assignment", shift_assignment)
- frappe.delete_doc("Shift Assignment Schedule", schedule)
+ frappe.delete_doc("Shift Schedule Assignment", shift_schedule_assignment)
@frappe.whitelist()
@@ -93,7 +109,13 @@ def swap_shift(
src_shift_doc = frappe.get_doc("Shift Assignment", src_shift)
break_shift(src_shift_doc, src_date)
insert_shift(
- tgt_employee, tgt_company, src_shift_doc.shift_type, tgt_date, tgt_date, src_shift_doc.status
+ tgt_employee,
+ tgt_company,
+ src_shift_doc.shift_type,
+ tgt_date,
+ tgt_date,
+ src_shift_doc.status,
+ src_shift_doc.shift_location,
)
if tgt_shift:
@@ -104,6 +126,7 @@ def swap_shift(
src_date,
src_date,
tgt_shift_doc.status,
+ tgt_shift_doc.shift_location,
)
@@ -122,6 +145,7 @@ def break_shift(assignment: str | ShiftAssignment, date: str) -> None:
shift_type = assignment.shift_type
status = assignment.status
end_date = assignment.end_date
+ shift_location = assignment.shift_location
if date_diff(date, assignment.start_date) == 0:
assignment.cancel()
@@ -131,12 +155,20 @@ def break_shift(assignment: str | ShiftAssignment, date: str) -> None:
assignment.save()
if not end_date or date_diff(end_date, date) > 0:
- create_shift_assignment(employee, company, shift_type, add_days(date, 1), end_date, status)
+ create_shift_assignment(
+ employee, company, shift_type, add_days(date, 1), end_date, status, shift_location
+ )
@frappe.whitelist()
def insert_shift(
- employee: str, company: str, shift_type: str, start_date: str, end_date: str | None, status: str
+ employee: str,
+ company: str,
+ shift_type: str,
+ start_date: str,
+ end_date: str | None,
+ status: str,
+ shift_location: str | None = None,
) -> None:
filters = {
"doctype": "Shift Assignment",
@@ -144,6 +176,7 @@ def insert_shift(
"company": company,
"shift_type": shift_type,
"status": status,
+ "shift_location": shift_location,
}
prev_shift = frappe.db.exists(dict({"end_date": add_days(start_date, -1)}, **filters))
next_shift = (
@@ -161,7 +194,7 @@ def insert_shift(
frappe.db.set_value("Shift Assignment", next_shift, "start_date", start_date)
else:
- create_shift_assignment(employee, company, shift_type, start_date, end_date, status)
+ create_shift_assignment(employee, company, shift_type, start_date, end_date, status, shift_location)
def get_holidays(month_start: str, month_end: str, employee_filters: dict[str, str]) -> dict[str, list[dict]]:
@@ -223,9 +256,11 @@ def get_shifts(
ShiftAssignment.name,
ShiftAssignment.employee,
ShiftAssignment.shift_type,
+ ShiftAssignment.shift_location,
ShiftAssignment.start_date,
ShiftAssignment.end_date,
ShiftAssignment.status,
+ ShiftAssignment.shift_schedule_assignment,
ShiftType.start_time,
ShiftType.end_time,
ShiftType.color,
diff --git a/hrms/hooks.py b/hrms/hooks.py
index 3632c09d55..aed1712649 100644
--- a/hrms/hooks.py
+++ b/hrms/hooks.py
@@ -223,7 +223,7 @@
],
"hourly_long": [
"hrms.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts",
- "hrms.hr.doctype.shift_assignment_schedule.shift_assignment_schedule.process_auto_shift_creation",
+ "hrms.hr.doctype.shift_schedule_assignment.shift_schedule_assignment.process_auto_shift_creation",
],
"daily": [
"hrms.controllers.employee_reminders.send_birthday_reminders",
diff --git a/hrms/hr/doctype/shift_assignment/shift_assignment.json b/hrms/hr/doctype/shift_assignment/shift_assignment.json
index ecbedb8f8b..fc91ff176e 100644
--- a/hrms/hr/doctype/shift_assignment/shift_assignment.json
+++ b/hrms/hr/doctype/shift_assignment/shift_assignment.json
@@ -21,7 +21,7 @@
"start_date",
"end_date",
"shift_request",
- "schedule",
+ "shift_schedule_assignment",
"amended_from"
],
"fields": [
@@ -129,16 +129,16 @@
"fieldtype": "Column Break"
},
{
- "fieldname": "schedule",
+ "fieldname": "shift_schedule_assignment",
"fieldtype": "Link",
- "label": "Schedule",
- "options": "Shift Assignment Schedule",
+ "label": "Shift Schedule Assignment",
+ "options": "Shift Schedule Assignment",
"read_only": 1
}
],
"is_submittable": 1,
"links": [],
- "modified": "2024-09-16 15:29:41.502080",
+ "modified": "2024-11-13 12:59:50.928962",
"modified_by": "Administrator",
"module": "HR",
"name": "Shift Assignment",
diff --git a/hrms/hr/doctype/shift_assignment/shift_assignment_list.js b/hrms/hr/doctype/shift_assignment/shift_assignment_list.js
index 897348a624..cb9126a711 100644
--- a/hrms/hr/doctype/shift_assignment/shift_assignment_list.js
+++ b/hrms/hr/doctype/shift_assignment/shift_assignment_list.js
@@ -1,19 +1,3 @@
frappe.listview_settings["Shift Assignment"] = {
- onload: function (list_view) {
- list_view.page.add_inner_button(
- __("Shift Assignment Tool"),
- function () {
- frappe.set_route("Form", "Shift Assignment Tool");
- },
- __("View"),
- );
-
- list_view.page.add_inner_button(
- __("Roster"),
- function () {
- window.location.href = "/hr/roster";
- },
- __("View"),
- );
- },
+ onload: (list_view) => hrms.add_shift_tools_button_to_list(list_view),
};
diff --git a/hrms/hr/doctype/shift_assignment_schedule/test_shift_assignment_schedule.py b/hrms/hr/doctype/shift_assignment_schedule/test_shift_assignment_schedule.py
deleted file mode 100644
index 69a5782c29..0000000000
--- a/hrms/hr/doctype/shift_assignment_schedule/test_shift_assignment_schedule.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-# import frappe
-from frappe.tests import IntegrationTestCase
-
-
-class TestShiftAssignmentSchedule(IntegrationTestCase):
- pass
diff --git a/hrms/hr/doctype/shift_assignment_tool/shift_assignment_tool.js b/hrms/hr/doctype/shift_assignment_tool/shift_assignment_tool.js
index d034ef3d61..000294af8f 100644
--- a/hrms/hr/doctype/shift_assignment_tool/shift_assignment_tool.js
+++ b/hrms/hr/doctype/shift_assignment_tool/shift_assignment_tool.js
@@ -17,6 +17,11 @@ frappe.ui.form.on("Shift Assignment Tool", {
"completed_bulk_shift_assignment",
"Shift Assignment",
);
+ hrms.handle_realtime_bulk_action_notification(
+ frm,
+ "completed_bulk_shift_schedule_assignment",
+ "Shift Schedule Assignment",
+ );
hrms.handle_realtime_bulk_action_notification(
frm,
"completed_bulk_shift_request_processing",
@@ -55,6 +60,10 @@ frappe.ui.form.on("Shift Assignment Tool", {
frm.trigger("get_employees");
},
+ shift_schedule(frm) {
+ frm.trigger("get_employees");
+ },
+
approver(frm) {
frm.trigger("get_employees");
},
@@ -93,38 +102,45 @@ frappe.ui.form.on("Shift Assignment Tool", {
const select_rows_section_head = document
.querySelector('[data-fieldname="select_rows_section"]')
.querySelector(".section-head");
+ select_rows_section_head.textContent = __("Select Employees");
+ frm.clear_custom_buttons();
+ frm.page.clear_primary_action();
- if (frm.doc.action === "Assign Shift") {
- frm.clear_custom_buttons();
+ if (frm.doc.action === "Assign Shift")
frm.page.set_primary_action(__("Assign Shift"), () => {
- frm.trigger("assign_shift");
+ frm.trigger("bulk_assign");
+ });
+ else if (frm.doc.action === "Assign Shift Schedule")
+ frm.page.set_primary_action(__("Assign Shift Schedule"), () => {
+ frm.trigger("bulk_assign");
});
- select_rows_section_head.textContent = __("Select Employees");
- return;
+ else {
+ frm.page.add_inner_button(
+ __("Approve"),
+ () => {
+ frm.events.process_shift_requests(frm, "Approved");
+ },
+ __("Process Requests"),
+ );
+ frm.page.add_inner_button(
+ __("Reject"),
+ () => {
+ frm.events.process_shift_requests(frm, "Rejected");
+ },
+ __("Process Requests"),
+ );
+ frm.page.set_inner_btn_group_as_primary(__("Process Requests"));
+ frm.page.clear_menu();
+ select_rows_section_head.textContent = __("Select Shift Requests");
}
-
- frm.page.clear_primary_action();
- frm.page.add_inner_button(
- __("Approve"),
- () => {
- frm.events.process_shift_requests(frm, "Approved");
- },
- __("Process Requests"),
- );
- frm.page.add_inner_button(
- __("Reject"),
- () => {
- frm.events.process_shift_requests(frm, "Rejected");
- },
- __("Process Requests"),
- );
- frm.page.set_inner_btn_group_as_primary(__("Process Requests"));
- frm.page.clear_menu();
- select_rows_section_head.textContent = __("Select Shift Requests");
},
get_employees(frm) {
- if (frm.doc.action === "Assign Shift" && !(frm.doc.shift_type && frm.doc.start_date))
+ if (
+ (frm.doc.action === "Assign Shift" && !(frm.doc.shift_type && frm.doc.start_date)) ||
+ (frm.doc.action === "Assign Shift Schedule" &&
+ !(frm.doc.shift_schedule && frm.doc.start_date))
+ )
return frm.events.render_employees_datatable(frm, []);
frm.call({
@@ -146,6 +162,13 @@ frappe.ui.form.on("Shift Assignment Tool", {
? "There are no employees without Shift Assignments for these dates based on the given filters."
: "Please select Shift Type and assignment date(s).",
);
+ } else if (frm.doc.action === "Assign Shift Schedule") {
+ columns = frm.events.get_assign_shift_datatable_columns();
+ no_data_message = __(
+ frm.doc.shift_schedule && frm.doc.start_date
+ ? "There are no employees without active overlapping Shift Schedule Assignments based on the given filters."
+ : "Please select Shift Schedule and assignment date(s).",
+ );
} else {
columns = frm.events.get_process_shift_requests_datatable_columns();
no_data_message = "There are no open Shift Requests based on the given filters.";
@@ -224,7 +247,8 @@ frappe.ui.form.on("Shift Assignment Tool", {
align: "left",
}));
},
- assign_shift(frm) {
+
+ bulk_assign(frm, employees) {
const rows = frm.employees_datatable.datamanager.data;
const selected_employees = [];
const checked_row_indexes = frm.employees_datatable.rowmanager.getCheckedRows();
@@ -233,21 +257,20 @@ frappe.ui.form.on("Shift Assignment Tool", {
});
hrms.validate_mandatory_fields(frm, selected_employees);
- frappe.confirm(__("Assign Shift to {0} employee(s)?", [selected_employees.length]), () => {
- frm.events.bulk_assign_shift(frm, selected_employees);
- });
- },
-
- bulk_assign_shift(frm, employees) {
- frm.call({
- method: "bulk_assign_shift",
- doc: frm.doc,
- args: {
- employees: employees,
+ frappe.confirm(
+ __("{0} to {1} employee(s)?", [__(frm.doc.action), selected_employees.length]),
+ () => {
+ frm.call({
+ method: "bulk_assign",
+ doc: frm.doc,
+ args: {
+ employees: selected_employees,
+ },
+ freeze: true,
+ freeze_message: __("Assigning..."),
+ });
},
- freeze: true,
- freeze_message: __("Assigning Shift"),
- });
+ );
},
process_shift_requests(frm, status) {
diff --git a/hrms/hr/doctype/shift_assignment_tool/shift_assignment_tool.json b/hrms/hr/doctype/shift_assignment_tool/shift_assignment_tool.json
index 64801f6bb3..ace22bbcfc 100644
--- a/hrms/hr/doctype/shift_assignment_tool/shift_assignment_tool.json
+++ b/hrms/hr/doctype/shift_assignment_tool/shift_assignment_tool.json
@@ -11,6 +11,8 @@
"company",
"shift_assignment_details_section",
"shift_type",
+ "shift_schedule",
+ "shift_location",
"status",
"column_break_ybmd",
"start_date",
@@ -40,7 +42,7 @@
"fieldtype": "Select",
"in_list_view": 1,
"label": "Action",
- "options": "Assign Shift\nProcess Shift Requests",
+ "options": "Assign Shift\nAssign Shift Schedule\nProcess Shift Requests",
"reqd": 1
},
{
@@ -56,6 +58,7 @@
"reqd": 1
},
{
+ "depends_on": "eval:doc.action === \"Assign Shift\"",
"fieldname": "shift_type",
"fieldtype": "Link",
"in_list_view": 1,
@@ -80,7 +83,7 @@
"fieldtype": "Date",
"in_list_view": 1,
"label": "Start Date",
- "mandatory_depends_on": "eval:doc.action === \"Assign Shift\""
+ "mandatory_depends_on": "eval:doc.action === \"Assign Shift\" || doc.action === \"Assign Shift Schedule\""
},
{
"fieldname": "end_date",
@@ -172,7 +175,7 @@
"label": "Shift Request Filters"
},
{
- "depends_on": "eval:doc.action === \"Assign Shift\"",
+ "depends_on": "eval:doc.action === \"Assign Shift\" || doc.action === \"Assign Shift Schedule\"",
"fieldname": "shift_assignment_details_section",
"fieldtype": "Section Break",
"label": "Shift Assignment Details"
@@ -187,12 +190,26 @@
"fieldname": "select_rows_section",
"fieldtype": "Section Break",
"label": "Select Employees"
+ },
+ {
+ "fieldname": "shift_location",
+ "fieldtype": "Link",
+ "label": "Shift Location",
+ "options": "Shift Location"
+ },
+ {
+ "depends_on": "eval:doc.action === \"Assign Shift Schedule\"",
+ "fieldname": "shift_schedule",
+ "fieldtype": "Link",
+ "label": "Shift Schedule",
+ "mandatory_depends_on": "eval:doc.action === \"Assign Shift Schedule\"",
+ "options": "Shift Schedule"
}
],
"hide_toolbar": 1,
"issingle": 1,
"links": [],
- "modified": "2024-03-27 17:54:33.829320",
+ "modified": "2024-12-13 17:38:45.675004",
"modified_by": "Administrator",
"module": "HR",
"name": "Shift Assignment Tool",
diff --git a/hrms/hr/doctype/shift_assignment_tool/shift_assignment_tool.py b/hrms/hr/doctype/shift_assignment_tool/shift_assignment_tool.py
index fb33da5094..19bbf608d1 100644
--- a/hrms/hr/doctype/shift_assignment_tool/shift_assignment_tool.py
+++ b/hrms/hr/doctype/shift_assignment_tool/shift_assignment_tool.py
@@ -30,9 +30,9 @@ def get_employees(self, advanced_filters: list | None = None) -> list:
filters = [[d, "=", self.get(d)] for d in quick_filter_fields if self.get(d)]
filters += advanced_filters
- if self.action == "Assign Shift":
- return self.get_employees_for_assigning_shift(filters)
- return self.get_shift_requests(filters)
+ if self.action == "Process Shift Requests":
+ return self.get_shift_requests(filters)
+ return self.get_employees_for_assigning_shift(filters)
def get_employees_for_assigning_shift(self, filters):
Employee = frappe.qb.DocType("Employee")
@@ -55,8 +55,17 @@ def get_employees_for_assigning_shift(self, filters):
query = query.where(
(Employee.relieving_date >= self.end_date) | (Employee.relieving_date.isnull())
)
- if self.status == "Active":
+
+ self.allow_multiple_shifts = frappe.db.get_single_value(
+ "HR Settings", "allow_multiple_shift_assignments"
+ )
+ if self.action == "Assign Shift Schedule":
+ query = query.where(
+ Employee.employee.notin(SubQuery(self.get_query_for_employees_with_same_shift_schedule()))
+ )
+ elif self.status == "Active":
query = query.where(Employee.employee.notin(SubQuery(self.get_query_for_employees_with_shifts())))
+
return query.run(as_dict=True)
def get_shift_requests(self, filters):
@@ -97,16 +106,9 @@ def get_shift_requests(self, filters):
def get_query_for_employees_with_shifts(self):
ShiftAssignment = frappe.qb.DocType("Shift Assignment")
- query = frappe.qb.from_(ShiftAssignment)
-
- allow_multiple_shifts = frappe.db.get_single_value("HR Settings", "allow_multiple_shift_assignments")
- # join Shift Type if multiple shifts are allowed as we need to know shift timings only in this case
- if allow_multiple_shifts:
- ShiftType = frappe.qb.DocType("Shift Type")
- query = query.left_join(ShiftType).on(ShiftAssignment.shift_type == ShiftType.name)
-
query = (
- query.select(ShiftAssignment.employee)
+ frappe.qb.from_(ShiftAssignment)
+ .select(ShiftAssignment.employee)
.distinct()
.where(
(ShiftAssignment.status == "Active")
@@ -115,68 +117,131 @@ def get_query_for_employees_with_shifts(self):
& ((ShiftAssignment.end_date >= self.start_date) | (ShiftAssignment.end_date.isnull()))
)
)
+
if self.end_date:
query = query.where(ShiftAssignment.start_date <= self.end_date)
- # check for overlapping timings if multiple shifts are allowed
- if allow_multiple_shifts:
- shift_start, shift_end = frappe.db.get_value(
- "Shift Type", self.shift_type, ["start_time", "end_time"]
- )
- # turn it into a 48 hour clock for easier conditioning while considering overnight shifts
- if shift_end < shift_start:
- shift_end += timedelta(hours=24)
- end_time_case = (
- Case()
- .when(ShiftType.end_time < ShiftType.start_time, ShiftType.end_time + Interval(hours=24))
- .else_(ShiftType.end_time)
- )
- query = query.where((end_time_case >= shift_start) & (ShiftType.start_time <= shift_end))
+ if self.allow_multiple_shifts:
+ query = self.get_query_checking_overlapping_shift_timings(query, ShiftAssignment, self.shift_type)
+
+ return query
+
+ def get_query_for_employees_with_same_shift_schedule(self):
+ days = frappe.get_all("Assignment Rule Day", {"parent": self.shift_schedule}, pluck="day")
+
+ ShiftScheduleAssignment = frappe.qb.DocType("Shift Schedule Assignment")
+ ShiftSchedule = frappe.qb.DocType("Shift Schedule")
+ Day = frappe.qb.DocType("Assignment Rule Day")
+
+ query = (
+ frappe.qb.from_(ShiftScheduleAssignment)
+ .left_join(ShiftSchedule)
+ .on(ShiftSchedule.name == ShiftScheduleAssignment.shift_schedule)
+ .left_join(Day)
+ .on(ShiftSchedule.name == Day.parent)
+ .select(ShiftScheduleAssignment.employee)
+ .distinct()
+ .where((ShiftScheduleAssignment.enabled == 1) & (Day.day.isin(days)))
+ )
+
+ if self.allow_multiple_shifts:
+ shift_type = frappe.db.get_value("Shift Schedule", self.shift_schedule, "shift_type")
+ query = self.get_query_checking_overlapping_shift_timings(query, ShiftSchedule, shift_type)
return query
+ def get_query_checking_overlapping_shift_timings(self, query, doctype, shift_type):
+ shift_start, shift_end = frappe.db.get_value("Shift Type", shift_type, ["start_time", "end_time"])
+ # turn it into a 48 hour clock for easier conditioning while considering overnight shifts
+ if shift_end < shift_start:
+ shift_end += timedelta(hours=24)
+
+ ShiftType = frappe.qb.DocType("Shift Type")
+ end_time_case = (
+ Case()
+ .when(ShiftType.end_time < ShiftType.start_time, ShiftType.end_time + Interval(hours=24))
+ .else_(ShiftType.end_time)
+ )
+
+ return (
+ query.left_join(ShiftType)
+ .on(doctype.shift_type == ShiftType.name)
+ .where((end_time_case >= shift_start) & (ShiftType.start_time <= shift_end))
+ )
+
@frappe.whitelist()
- def bulk_assign_shift(self, employees: list):
- mandatory_fields = ["company", "shift_type", "start_date"]
+ def bulk_assign(self, employees: list):
+ if self.action == "Assign Shift":
+ mandatory_fields = ["shift_type"]
+ doctype = "Shift Assignments"
+
+ elif self.action == "Assign Shift Schedule":
+ mandatory_fields = ["shift_schedule"]
+ doctype = "Shift Schedule Assignments"
+
+ else:
+ frappe.throw(_("Invalid Action"))
+
+ mandatory_fields.extend(["company", "start_date"])
+
validate_bulk_tool_fields(self, mandatory_fields, employees, "start_date", "end_date")
- if len(employees) <= 30:
- return self._bulk_assign_shift(employees)
+ if self.action == "Assign Shift" and len(employees) <= 30:
+ return self._bulk_assign(employees)
- frappe.enqueue(self._bulk_assign_shift, timeout=3000, employees=employees)
+ frappe.enqueue(self._bulk_assign, timeout=3000, employees=employees)
frappe.msgprint(
- _("Creation of Shift Assignments has been queued. It may take a few minutes."),
+ _("Creation of {0} has been queued. It may take a few minutes.").format(doctype),
alert=True,
indicator="blue",
)
- def _bulk_assign_shift(self, employees: list):
+ def _bulk_assign(self, employees: list):
success, failure = [], []
count = 0
- savepoint = "before_shift_assignment"
+ savepoint = "before_assignment"
+ if self.action == "Assign Shift":
+ doctype = "Shift Assignment"
+ event = "completed_bulk_shift_assignment"
+ else:
+ doctype = "Shift Schedule Assignment"
+ event = "completed_bulk_shift_schedule_assignment"
for d in employees:
try:
frappe.db.savepoint(savepoint)
- assignment = create_shift_assignment(
- d, self.company, self.shift_type, self.start_date, self.end_date, self.status
+ assignment = (
+ self.create_shift_schedule_assignment(d)
+ if self.action == "Assign Shift Schedule"
+ else create_shift_assignment(
+ d,
+ self.company,
+ self.shift_type,
+ self.start_date,
+ self.end_date,
+ self.status,
+ self.shift_location,
+ )
)
+ if self.action == "Assign Shift Schedule":
+ assignment.create_shifts(self.start_date, self.end_date)
+
except Exception:
frappe.db.rollback(save_point=savepoint)
frappe.log_error(
- f"Bulk Assignment - Shift Assignment failed for employee {d}.",
- reference_doctype="Shift Assignment",
+ f"Bulk Assignment - {doctype} failed for employee {d}.",
+ reference_doctype=doctype,
)
failure.append(d)
else:
- success.append({"doc": get_link_to_form("Shift Assignment", assignment), "employee": d})
+ success.append({"doc": get_link_to_form(doctype, assignment.name), "employee": d})
count += 1
- frappe.publish_progress(count * 100 / len(employees), title=_("Assigning Shift..."))
+ frappe.publish_progress(count * 100 / len(employees), title=_("Creating {0}...").format(doctype))
frappe.clear_messages()
frappe.publish_realtime(
- "completed_bulk_shift_assignment",
+ event,
message={"success": success, "failure": failure},
doctype="Shift Assignment Tool",
after_commit=True,
@@ -235,6 +300,18 @@ def _bulk_process_shift_requests(self, shift_requests: list, status: str):
after_commit=True,
)
+ def create_shift_schedule_assignment(self, employee: str) -> str:
+ assignment = frappe.new_doc("Shift Schedule Assignment")
+ assignment.shift_schedule = self.shift_schedule
+ assignment.employee = employee
+ assignment.company = self.company
+ assignment.shift_status = self.status
+ assignment.shift_location = self.shift_location
+ assignment.enabled = 0 if self.end_date else 1
+ assignment.create_shifts_after = self.start_date
+ assignment.save()
+ return assignment
+
def create_shift_assignment(
employee: str,
@@ -243,7 +320,8 @@ def create_shift_assignment(
start_date: str,
end_date: str,
status: str,
- schedule: str | None = None,
+ shift_location: str | None = None,
+ shift_schedule_assignment: str | None = None,
) -> str:
assignment = frappe.new_doc("Shift Assignment")
assignment.employee = employee
@@ -252,7 +330,8 @@ def create_shift_assignment(
assignment.start_date = start_date
assignment.end_date = end_date
assignment.status = status
- assignment.schedule = schedule
+ assignment.shift_location = shift_location
+ assignment.shift_schedule_assignment = shift_schedule_assignment
assignment.save()
assignment.submit()
- return assignment.name
+ return assignment
diff --git a/hrms/hr/doctype/shift_assignment_tool/test_shift_assignment_tool.py b/hrms/hr/doctype/shift_assignment_tool/test_shift_assignment_tool.py
index c4c236f5b2..c7cf446582 100644
--- a/hrms/hr/doctype/shift_assignment_tool/test_shift_assignment_tool.py
+++ b/hrms/hr/doctype/shift_assignment_tool/test_shift_assignment_tool.py
@@ -2,13 +2,14 @@
# See license.txt
import frappe
-from frappe.tests import IntegrationTestCase
+from frappe.tests import IntegrationTestCase, change_settings
from frappe.utils import add_days, getdate
from erpnext.setup.doctype.employee.test_employee import make_employee
from hrms.hr.doctype.shift_assignment_tool.shift_assignment_tool import ShiftAssignmentTool
from hrms.hr.doctype.shift_request.test_shift_request import make_shift_request
+from hrms.hr.doctype.shift_schedule.shift_schedule import get_or_insert_shift_schedule
from hrms.hr.doctype.shift_type.test_shift_type import make_shift_assignment, setup_shift_type
from hrms.tests.test_utils import create_company
@@ -20,6 +21,10 @@ def setUp(self):
self.shift1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00")
self.shift2 = setup_shift_type(shift_type="Shift 2", start_time="11:00:00", end_time="15:00:00")
self.shift3 = setup_shift_type(shift_type="Shift 3", start_time="14:00:00", end_time="18:00:00")
+ self.schedule1 = get_or_insert_shift_schedule(self.shift1.name, "Every Week", ["Monday"])
+ self.schedule2 = get_or_insert_shift_schedule(self.shift2.name, "Every Week", ["Monday"])
+ self.schedule3 = get_or_insert_shift_schedule(self.shift3.name, "Every Week", ["Monday"])
+ self.schedule4 = get_or_insert_shift_schedule(self.shift1.name, "Every Week", ["Tuesday"])
self.emp1 = make_employee("employee1@test.com", company="_Test Company")
self.emp2 = make_employee("employee2@test.com", company="_Test Company")
self.emp3 = make_employee("employee3@test.com", company="_Test Company")
@@ -29,6 +34,7 @@ def setUp(self):
def tearDown(self):
frappe.db.rollback()
+ @change_settings("HR Settings", {"allow_multiple_shift_assignments": 0})
def test_get_employees_for_assigning_shifts(self):
today = getdate()
@@ -72,6 +78,38 @@ def test_get_employees_for_assigning_shifts(self):
self.assertIn(self.emp1, employee_names)
self.assertIn(self.emp2, employee_names)
+ def test_get_employees_for_assigning_shift_schedule(self):
+ today = getdate()
+
+ args = {
+ "doctype": "Shift Assignment Tool",
+ "action": "Assign Shift Schedule",
+ "company": "_Test Company", # excludes emp4
+ "shift_schedule": self.schedule1,
+ "start_date": today,
+ }
+ shift_assignment_tool = ShiftAssignmentTool(args)
+ advanced_filters = [["employee_name", "like", "%test.com%"]] # excludes emp5
+
+ # does not exclude emp1 as days don't overlap
+ make_shift_schedule_assignment(self.schedule4, self.emp1)
+ # excludes emp2 due to overlapping days
+ make_shift_schedule_assignment(self.schedule2, self.emp2)
+ # excludes emp3 due to overlapping days
+ make_shift_schedule_assignment(self.schedule3, self.emp3)
+
+ employees = shift_assignment_tool.get_employees(advanced_filters)
+ self.assertEqual(len(employees), 1) # emp1
+
+ # includes emp3 as multiple shifts in a day are allowed and timings don't overlap
+ frappe.db.set_single_value("HR Settings", "allow_multiple_shift_assignments", 1)
+ employees = shift_assignment_tool.get_employees(advanced_filters)
+ self.assertEqual(len(employees), 2) # emp1, emp3
+
+ employee_names = [d.employee for d in employees]
+ self.assertIn(self.emp1, employee_names)
+ self.assertIn(self.emp3, employee_names)
+
def test_get_shift_requests(self):
today = getdate()
@@ -162,7 +200,7 @@ def test_bulk_assign_shift(self):
shift_assignment_tool = ShiftAssignmentTool(args)
employees = [self.emp1, self.emp2, self.emp3]
- shift_assignment_tool.bulk_assign_shift(employees)
+ shift_assignment_tool.bulk_assign(employees)
shift_assignment_employees = frappe.get_list(
"Shift Assignment",
filters={
@@ -178,6 +216,35 @@ def test_bulk_assign_shift(self):
self.assertIn(self.emp2, shift_assignment_employees)
self.assertIn(self.emp3, shift_assignment_employees)
+ def test_bulk_assign_shift_schedule(self):
+ today = getdate()
+
+ args = {
+ "doctype": "Shift Assignment Tool",
+ "action": "Assign Shift Schedule",
+ "company": "_Test Company",
+ "shift_schedule": self.schedule1,
+ "status": "Active",
+ "start_date": today,
+ "end_date": add_days(today, 10),
+ }
+ shift_assignment_tool = ShiftAssignmentTool(args)
+
+ employees = [self.emp1, self.emp2, self.emp3]
+ shift_assignment_tool._bulk_assign(employees)
+ assigned_employees = frappe.get_list(
+ "Shift Schedule Assignment",
+ filters={
+ "shift_schedule": self.schedule1,
+ "shift_status": "Active",
+ "enabled": 0,
+ },
+ pluck="employee",
+ )
+ self.assertIn(self.emp1, assigned_employees)
+ self.assertIn(self.emp2, assigned_employees)
+ self.assertIn(self.emp3, assigned_employees)
+
def test_bulk_process_shift_requests(self):
for emp in [self.emp1, self.emp2, self.emp3]:
employee = frappe.get_doc("Employee", emp)
@@ -231,3 +298,15 @@ def test_bulk_process_shift_requests(self):
shift_assignment = frappe.db.exists("Shift Assignment", {"shift_request": request3.name})
self.assertTrue(shift_assignment)
+
+
+def make_shift_schedule_assignment(schedule, employee, create_shifts_after=None, enabled=1):
+ assignment = frappe.new_doc("Shift Schedule Assignment")
+ assignment.shift_schedule = schedule
+ assignment.employee = employee
+ assignment.company = "_Test Company"
+ assignment.enabled = enabled
+ assignment.create_shifts_after = create_shifts_after or getdate()
+ assignment.save()
+
+ return assignment.name
diff --git a/hrms/hr/doctype/shift_location/shift_location.js b/hrms/hr/doctype/shift_location/shift_location.js
index 3439f8d097..dff91f02f8 100644
--- a/hrms/hr/doctype/shift_location/shift_location.js
+++ b/hrms/hr/doctype/shift_location/shift_location.js
@@ -2,7 +2,7 @@
// For license information, please see license.txt
frappe.ui.form.on("Shift Location", {
- refresh: async () => {
+ refresh: async (frm) => {
const allow_geolocation_tracking = await frappe.db.get_single_value(
"HR Settings",
"allow_geolocation_tracking",
@@ -16,6 +16,12 @@ frappe.ui.form.on("Shift Location", {
"longitude",
"geolocation",
]);
+
+ if (!frm.doc.__islocal)
+ hrms.add_shift_tools_button_to_form(frm, {
+ action: "Assign Shift",
+ shift_location: frm.doc.name,
+ });
},
fetch_geolocation: (frm) => {
diff --git a/hrms/hr/doctype/shift_location/shift_location_list.js b/hrms/hr/doctype/shift_location/shift_location_list.js
new file mode 100644
index 0000000000..291ee4997b
--- /dev/null
+++ b/hrms/hr/doctype/shift_location/shift_location_list.js
@@ -0,0 +1,3 @@
+frappe.listview_settings["Shift Location"] = {
+ onload: (list_view) => hrms.add_shift_tools_button_to_list(list_view),
+};
diff --git a/hrms/hr/doctype/shift_request/shift_request_list.js b/hrms/hr/doctype/shift_request/shift_request_list.js
index 30c4bdb7be..b456a2c4a4 100644
--- a/hrms/hr/doctype/shift_request/shift_request_list.js
+++ b/hrms/hr/doctype/shift_request/shift_request_list.js
@@ -1,9 +1,4 @@
frappe.listview_settings["Shift Request"] = {
- onload: function (list_view) {
- list_view.page.add_inner_button(__("Shift Assignment Tool"), function () {
- const doc = frappe.model.get_new_doc("Shift Assignment Tool");
- doc.action = "Process Shift Requests";
- frappe.set_route("Form", "Shift Assignment Tool", doc.name);
- });
- },
+ onload: (list_view) =>
+ hrms.add_shift_tools_button_to_list(list_view, "Process Shift Requests"),
};
diff --git a/hrms/hr/doctype/shift_assignment_schedule/__init__.py b/hrms/hr/doctype/shift_schedule/__init__.py
similarity index 100%
rename from hrms/hr/doctype/shift_assignment_schedule/__init__.py
rename to hrms/hr/doctype/shift_schedule/__init__.py
diff --git a/hrms/hr/doctype/shift_schedule/shift_schedule.js b/hrms/hr/doctype/shift_schedule/shift_schedule.js
new file mode 100644
index 0000000000..df5c6e7d0e
--- /dev/null
+++ b/hrms/hr/doctype/shift_schedule/shift_schedule.js
@@ -0,0 +1,12 @@
+// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on("Shift Schedule", {
+ refresh(frm) {
+ if (frm.doc.docstatus === 1)
+ hrms.add_shift_tools_button_to_form(frm, {
+ action: "Assign Shift Schedule",
+ shift_schedule: frm.doc.name,
+ });
+ },
+});
diff --git a/hrms/hr/doctype/shift_schedule/shift_schedule.json b/hrms/hr/doctype/shift_schedule/shift_schedule.json
new file mode 100644
index 0000000000..eacf1cd2a4
--- /dev/null
+++ b/hrms/hr/doctype/shift_schedule/shift_schedule.json
@@ -0,0 +1,114 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "prompt",
+ "creation": "2024-11-11 16:56:33.536882",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+ "schedule_settings_section",
+ "shift_type",
+ "column_break_iprq",
+ "frequency",
+ "repeat_on_days",
+ "amended_from"
+ ],
+ "fields": [
+ {
+ "fieldname": "schedule_settings_section",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "frequency",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Frequency",
+ "options": "Every Week\nEvery 2 Weeks\nEvery 3 Weeks\nEvery 4 Weeks",
+ "reqd": 1
+ },
+ {
+ "fieldname": "repeat_on_days",
+ "fieldtype": "Table",
+ "label": "Repeat On Days",
+ "options": "Assignment Rule Day",
+ "reqd": 1
+ },
+ {
+ "fieldname": "column_break_iprq",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "shift_type",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Shift Type",
+ "options": "Shift Type",
+ "reqd": 1
+ },
+ {
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "label": "Amended From",
+ "no_copy": 1,
+ "options": "Shift Schedule",
+ "print_hide": 1,
+ "read_only": 1,
+ "search_index": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [
+ {
+ "link_doctype": "Shift Schedule Assignment",
+ "link_fieldname": "shift_schedule"
+ }
+ ],
+ "modified": "2024-12-19 13:34:43.731635",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Shift Schedule",
+ "naming_rule": "Set by user",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Employee",
+ "share": 1
+ },
+ {
+ "create": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR User",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR Manager",
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "creation",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/hrms/hr/doctype/shift_schedule/shift_schedule.py b/hrms/hr/doctype/shift_schedule/shift_schedule.py
new file mode 100644
index 0000000000..d286c23f76
--- /dev/null
+++ b/hrms/hr/doctype/shift_schedule/shift_schedule.py
@@ -0,0 +1,47 @@
+# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe.model.document import Document
+from frappe.utils import random_string
+
+
+class ShiftSchedule(Document):
+ def before_validate(self):
+ to_be_deleted = []
+ seen_days = set()
+
+ for d in self.repeat_on_days:
+ if d.day in seen_days:
+ to_be_deleted.append(d)
+ else:
+ seen_days.add(d.day)
+
+ for d in to_be_deleted:
+ self.remove(d)
+
+
+def get_or_insert_shift_schedule(shift_type: str, frequency: str, repeat_on_days: list[str]) -> str:
+ shift_schedules = frappe.get_all(
+ "Shift Schedule",
+ pluck="name",
+ filters={"shift_type": shift_type, "frequency": frequency, "docstatus": 1},
+ )
+
+ for shift_schedule in shift_schedules:
+ shift_schedule = frappe.get_doc("Shift Schedule", shift_schedule)
+ shift_schedule_days = [d.day for d in shift_schedule.repeat_on_days]
+ if sorted(repeat_on_days) == sorted(shift_schedule_days):
+ return shift_schedule.name
+
+ doc = frappe.get_doc(
+ {
+ "doctype": "Shift Schedule",
+ "name": random_string(10),
+ "shift_type": shift_type,
+ "frequency": frequency,
+ "repeat_on_days": [{"day": day} for day in repeat_on_days],
+ }
+ ).insert()
+ doc.submit()
+ return doc.name
diff --git a/hrms/hr/doctype/shift_schedule/shift_schedule_list.js b/hrms/hr/doctype/shift_schedule/shift_schedule_list.js
new file mode 100644
index 0000000000..2b9044e24a
--- /dev/null
+++ b/hrms/hr/doctype/shift_schedule/shift_schedule_list.js
@@ -0,0 +1,3 @@
+frappe.listview_settings["Shift Schedule"] = {
+ onload: (list_view) => hrms.add_shift_tools_button_to_list(list_view, "Assign Shift Schedule"),
+};
diff --git a/hrms/hr/doctype/shift_schedule/test_shift_schedule.py b/hrms/hr/doctype/shift_schedule/test_shift_schedule.py
new file mode 100644
index 0000000000..151b28bc30
--- /dev/null
+++ b/hrms/hr/doctype/shift_schedule/test_shift_schedule.py
@@ -0,0 +1,20 @@
+# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests import IntegrationTestCase, UnitTestCase
+
+# On IntegrationTestCase, the doctype test records and all
+# link-field test record depdendencies are recursively loaded
+# Use these module variables to add/remove to/from that list
+EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
+IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
+
+
+class TestShiftSchedule(IntegrationTestCase):
+ """
+ Integration tests for ShiftSchedule.
+ Use this class for testing interactions between multiple components.
+ """
+
+ pass
diff --git a/hrms/hr/doctype/shift_schedule_assignment/__init__.py b/hrms/hr/doctype/shift_schedule_assignment/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/hrms/hr/doctype/shift_assignment_schedule/shift_assignment_schedule.js b/hrms/hr/doctype/shift_schedule_assignment/shift_schedule_assignment.js
similarity index 75%
rename from hrms/hr/doctype/shift_assignment_schedule/shift_assignment_schedule.js
rename to hrms/hr/doctype/shift_schedule_assignment/shift_schedule_assignment.js
index 892b5eae44..bbc5b25f21 100644
--- a/hrms/hr/doctype/shift_assignment_schedule/shift_assignment_schedule.js
+++ b/hrms/hr/doctype/shift_schedule_assignment/shift_schedule_assignment.js
@@ -1,7 +1,7 @@
// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
-// frappe.ui.form.on("Shift Assignment Schedule", {
+// frappe.ui.form.on("Shift Schedule Assignment", {
// refresh(frm) {
// },
diff --git a/hrms/hr/doctype/shift_assignment_schedule/shift_assignment_schedule.json b/hrms/hr/doctype/shift_schedule_assignment/shift_schedule_assignment.json
similarity index 77%
rename from hrms/hr/doctype/shift_assignment_schedule/shift_assignment_schedule.json
rename to hrms/hr/doctype/shift_schedule_assignment/shift_schedule_assignment.json
index e60f62bce1..18d2b6a480 100644
--- a/hrms/hr/doctype/shift_assignment_schedule/shift_assignment_schedule.json
+++ b/hrms/hr/doctype/shift_schedule_assignment/shift_schedule_assignment.json
@@ -1,76 +1,63 @@
{
"actions": [],
- "autoname": "HR-SAS-.YY.-.MM.-.#####",
- "creation": "2024-05-28 15:19:50.016396",
+ "autoname": "HR-SHSA-.YY.-.MM.-.#####",
+ "creation": "2024-11-11 17:33:00.330488",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
- "schedule_settings_section",
- "frequency",
- "repeat_on_days",
- "column_break_iprq",
- "enabled",
- "create_shifts_after",
"shift_details_section",
"employee",
"employee_name",
- "shift_type",
"column_break_toss",
"company",
- "shift_status"
+ "schedule_settings_section",
+ "shift_schedule",
+ "shift_location",
+ "shift_status",
+ "column_break_iprq",
+ "enabled",
+ "create_shifts_after"
],
"fields": [
+ {
+ "fieldname": "schedule_settings_section",
+ "fieldtype": "Section Break",
+ "label": "Shift Details"
+ },
{
"fieldname": "column_break_iprq",
"fieldtype": "Column Break"
},
{
- "fieldname": "frequency",
- "fieldtype": "Select",
- "in_list_view": 1,
- "label": "Frequency",
- "options": "Every Week\nEvery 2 Weeks\nEvery 3 Weeks\nEvery 4 Weeks",
- "reqd": 1
+ "default": "1",
+ "description": "Select this if you want shift assignments to be automatically created indefinitely.",
+ "fieldname": "enabled",
+ "fieldtype": "Check",
+ "label": "Enabled"
+ },
+ {
+ "default": "Today",
+ "depends_on": "eval:doc.enabled",
+ "description": "New shift assignments will be created after this date.",
+ "fieldname": "create_shifts_after",
+ "fieldtype": "Date",
+ "label": "Create Shifts After",
+ "mandatory_depends_on": "eval:doc.status === 'Active'"
},
{
"fieldname": "shift_details_section",
"fieldtype": "Section Break",
- "label": "Shift Details"
+ "label": "Employee Details"
},
{
"fieldname": "employee",
"fieldtype": "Link",
+ "in_list_view": 1,
"in_standard_filter": 1,
"label": "Employee",
"options": "Employee",
"reqd": 1
},
- {
- "fetch_from": "employee.company",
- "fieldname": "company",
- "fieldtype": "Link",
- "label": "Company",
- "options": "Company",
- "read_only": 1,
- "reqd": 1
- },
- {
- "fieldname": "column_break_toss",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "shift_type",
- "fieldtype": "Link",
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Shift Type",
- "options": "Shift Type",
- "reqd": 1
- },
- {
- "fieldname": "schedule_settings_section",
- "fieldtype": "Section Break"
- },
{
"fetch_from": "employee.employee_name",
"fieldname": "employee_name",
@@ -79,47 +66,51 @@
"read_only": 1
},
{
- "default": "Today",
- "depends_on": "eval:doc.enabled",
- "description": "New shift assignments will be created after this date.",
- "fieldname": "create_shifts_after",
- "fieldtype": "Date",
- "label": "Create Shifts After",
- "mandatory_depends_on": "eval:doc.status === 'Active'"
+ "fieldname": "column_break_toss",
+ "fieldtype": "Column Break"
},
{
- "fieldname": "repeat_on_days",
- "fieldtype": "Table",
- "label": "Repeat On Days",
- "options": "Assignment Rule Day",
+ "fetch_from": "employee.company",
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Company",
+ "options": "Company",
+ "read_only": 1,
"reqd": 1
},
- {
- "default": "1",
- "description": "Select this if you want shift assignments to be automatically created indefinitely.",
- "fieldname": "enabled",
- "fieldtype": "Check",
- "label": "Enabled"
- },
{
"default": "Active",
"fieldname": "shift_status",
"fieldtype": "Select",
- "label": "Status",
+ "label": "Shift Status",
"options": "Active\nInactive"
+ },
+ {
+ "fieldname": "shift_schedule",
+ "fieldtype": "Link",
+ "label": "Shift Schedule",
+ "options": "Shift Schedule",
+ "reqd": 1
+ },
+ {
+ "fieldname": "shift_location",
+ "fieldtype": "Link",
+ "label": "Shift Location",
+ "options": "Shift Location"
}
],
"index_web_pages_for_search": 1,
"links": [
{
"link_doctype": "Shift Assignment",
- "link_fieldname": "schedule"
+ "link_fieldname": "shift_schedule_assignment"
}
],
- "modified": "2024-06-27 14:37:31.797684",
+ "modified": "2024-12-10 15:44:00.063685",
"modified_by": "Administrator",
"module": "HR",
- "name": "Shift Assignment Schedule",
+ "name": "Shift Schedule Assignment",
"naming_rule": "Expression (old style)",
"owner": "Administrator",
"permissions": [
diff --git a/hrms/hr/doctype/shift_assignment_schedule/shift_assignment_schedule.py b/hrms/hr/doctype/shift_schedule_assignment/shift_schedule_assignment.py
similarity index 59%
rename from hrms/hr/doctype/shift_assignment_schedule/shift_assignment_schedule.py
rename to hrms/hr/doctype/shift_schedule_assignment/shift_schedule_assignment.py
index 84c31ea3d6..430c1fdc82 100644
--- a/hrms/hr/doctype/shift_assignment_schedule/shift_assignment_schedule.py
+++ b/hrms/hr/doctype/shift_schedule_assignment/shift_schedule_assignment.py
@@ -8,19 +8,20 @@
from hrms.hr.doctype.shift_assignment_tool.shift_assignment_tool import create_shift_assignment
-class ShiftAssignmentSchedule(Document):
+class ShiftScheduleAssignment(Document):
def create_shifts(self, start_date: str, end_date: str | None = None) -> None:
+ shift_schedule = frappe.get_doc("Shift Schedule", self.shift_schedule)
gap = {
"Every Week": 0,
"Every 2 Weeks": 1,
"Every 3 Weeks": 2,
"Every 4 Weeks": 3,
- }[self.frequency]
+ }[shift_schedule.frequency]
date = start_date
individual_assignment_start = None
week_end_day = get_weekday(add_days(start_date, -1))
- repeat_on_days = [day.day for day in self.repeat_on_days]
+ repeat_on_days = [day.day for day in shift_schedule.repeat_on_days]
if not end_date:
end_date = add_days(start_date, 90)
@@ -31,34 +32,47 @@ def create_shifts(self, start_date: str, end_date: str | None = None) -> None:
if not individual_assignment_start:
individual_assignment_start = date
if date == end_date:
- self.create_individual_assignment(individual_assignment_start, date)
+ self.create_individual_assignment(
+ shift_schedule.shift_type, individual_assignment_start, date
+ )
elif individual_assignment_start:
- self.create_individual_assignment(individual_assignment_start, add_days(date, -1))
+ self.create_individual_assignment(
+ shift_schedule.shift_type, individual_assignment_start, add_days(date, -1)
+ )
individual_assignment_start = None
if weekday == week_end_day and gap:
if individual_assignment_start:
- self.create_individual_assignment(individual_assignment_start, date)
+ self.create_individual_assignment(
+ shift_schedule.shift_type, individual_assignment_start, date
+ )
individual_assignment_start = None
date = add_days(date, 7 * gap)
date = add_days(date, 1)
- def create_individual_assignment(self, start_date, end_date):
+ def create_individual_assignment(self, shift_type, start_date, end_date):
create_shift_assignment(
- self.employee, self.company, self.shift_type, start_date, end_date, self.shift_status, self.name
+ self.employee,
+ self.company,
+ shift_type,
+ start_date,
+ end_date,
+ self.shift_status,
+ self.shift_location,
+ self.name,
)
self.create_shifts_after = end_date
self.save()
def process_auto_shift_creation():
- schedules = frappe.get_all(
- "Shift Assignment Schedule",
+ shift_schedule_assignments = frappe.get_all(
+ "Shift Schedule Assignment",
filters={"enabled": 1, "create_shifts_after": ["<=", nowdate()]},
pluck="name",
)
- for d in schedules:
- doc = frappe.get_doc("Shift Assignment Schedule", d)
+ for d in shift_schedule_assignments:
+ doc = frappe.get_doc("Shift Schedule Assignment", d)
doc.create_shifts(add_days(doc.create_shifts_after, 1))
diff --git a/hrms/hr/doctype/shift_schedule_assignment/shift_schedule_assignment_list.js b/hrms/hr/doctype/shift_schedule_assignment/shift_schedule_assignment_list.js
new file mode 100644
index 0000000000..ecfd2d6d37
--- /dev/null
+++ b/hrms/hr/doctype/shift_schedule_assignment/shift_schedule_assignment_list.js
@@ -0,0 +1,3 @@
+frappe.listview_settings["Shift Schedule Assignment"] = {
+ onload: (list_view) => hrms.add_shift_tools_button_to_list(list_view, "Assign Shift Schedule"),
+};
diff --git a/hrms/hr/doctype/shift_schedule_assignment/test_shift_schedule_assignment.py b/hrms/hr/doctype/shift_schedule_assignment/test_shift_schedule_assignment.py
new file mode 100644
index 0000000000..d6d928ec48
--- /dev/null
+++ b/hrms/hr/doctype/shift_schedule_assignment/test_shift_schedule_assignment.py
@@ -0,0 +1,20 @@
+# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests import IntegrationTestCase, UnitTestCase
+
+# On IntegrationTestCase, the doctype test records and all
+# link-field test record depdendencies are recursively loaded
+# Use these module variables to add/remove to/from that list
+EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
+IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
+
+
+class TestShiftScheduleAssignment(IntegrationTestCase):
+ """
+ Integration tests for ShiftScheduleAssignment.
+ Use this class for testing interactions between multiple components.
+ """
+
+ pass
diff --git a/hrms/hr/doctype/shift_type/shift_type.js b/hrms/hr/doctype/shift_type/shift_type.js
index 85a2efd2aa..47d99e26cf 100644
--- a/hrms/hr/doctype/shift_type/shift_type.js
+++ b/hrms/hr/doctype/shift_type/shift_type.js
@@ -5,51 +5,35 @@ frappe.ui.form.on("Shift Type", {
refresh: function (frm) {
if (frm.doc.__islocal) return;
- frm.add_custom_button(
- __("Bulk Assign Shift"),
- () => {
- const doc = frappe.model.get_new_doc("Shift Assignment Tool");
- doc.action = "Assign Shift";
- doc.company = frappe.defaults.get_default("company");
- doc.shift_type = frm.doc.name;
- doc.status = "Active";
- frappe.set_route("Form", "Shift Assignment Tool", doc.name);
- },
- __("Actions"),
- );
+ hrms.add_shift_tools_button_to_form(frm, {
+ action: "Assign Shift",
+ shift_type: frm.doc.name,
+ });
- frm.add_custom_button(
- __("Mark Attendance"),
- () => {
- if (!frm.doc.enable_auto_attendance) {
- frm.scroll_to_field("enable_auto_attendance");
- frappe.throw(
- __("Please Enable Auto Attendance and complete the setup first."),
- );
- }
+ frm.add_custom_button(__("Mark Attendance"), () => {
+ if (!frm.doc.enable_auto_attendance) {
+ frm.scroll_to_field("enable_auto_attendance");
+ frappe.throw(__("Please Enable Auto Attendance and complete the setup first."));
+ }
- if (!frm.doc.process_attendance_after) {
- frm.scroll_to_field("process_attendance_after");
- frappe.throw(__("Please set {0}.", [__("Process Attendance After").bold()]));
- }
+ if (!frm.doc.process_attendance_after) {
+ frm.scroll_to_field("process_attendance_after");
+ frappe.throw(__("Please set {0}.", [__("Process Attendance After").bold()]));
+ }
- if (!frm.doc.last_sync_of_checkin) {
- frm.scroll_to_field("last_sync_of_checkin");
- frappe.throw(__("Please set {0}.", [__("Last Sync of Checkin").bold()]));
- }
+ if (!frm.doc.last_sync_of_checkin) {
+ frm.scroll_to_field("last_sync_of_checkin");
+ frappe.throw(__("Please set {0}.", [__("Last Sync of Checkin").bold()]));
+ }
- frm.call({
- doc: frm.doc,
- method: "process_auto_attendance",
- freeze: true,
- callback: () => {
- frappe.msgprint(
- __("Attendance has been marked as per employee check-ins"),
- );
- },
- });
- },
- __("Actions"),
- );
+ frm.call({
+ doc: frm.doc,
+ method: "process_auto_attendance",
+ freeze: true,
+ callback: () => {
+ frappe.msgprint(__("Attendance has been marked as per employee check-ins"));
+ },
+ });
+ });
},
});
diff --git a/hrms/hr/doctype/shift_type/shift_type_list.js b/hrms/hr/doctype/shift_type/shift_type_list.js
index 1d5773c4fe..8581089167 100644
--- a/hrms/hr/doctype/shift_type/shift_type_list.js
+++ b/hrms/hr/doctype/shift_type/shift_type_list.js
@@ -1,7 +1,3 @@
frappe.listview_settings["Shift Type"] = {
- onload: function (list_view) {
- list_view.page.add_inner_button(__("Shift Assignment Tool"), function () {
- frappe.set_route("Form", "Shift Assignment Tool");
- });
- },
+ onload: (list_view) => hrms.add_shift_tools_button_to_list(list_view),
};
diff --git a/hrms/hr/workspace/shift_&_attendance/shift_&_attendance.json b/hrms/hr/workspace/shift_&_attendance/shift_&_attendance.json
index c6fb135cea..e0e09f1573 100644
--- a/hrms/hr/workspace/shift_&_attendance/shift_&_attendance.json
+++ b/hrms/hr/workspace/shift_&_attendance/shift_&_attendance.json
@@ -204,9 +204,19 @@
{
"hidden": 0,
"is_query_report": 0,
- "label": "Shift Assignment Schedule",
+ "label": "Shift Schedule",
"link_count": 0,
- "link_to": "Shift Assignment Schedule",
+ "link_to": "Shift Schedule",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Shift Schedule Assignment",
+ "link_count": 0,
+ "link_to": "Shift Schedule Assignment",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
@@ -232,7 +242,7 @@
"type": "Link"
}
],
- "modified": "2024-10-09 15:40:48.672192",
+ "modified": "2024-12-10 17:51:08.275060",
"modified_by": "Administrator",
"module": "HR",
"name": "Shift & Attendance",
diff --git a/hrms/locale/hu.po b/hrms/locale/hu.po
index 20275c0571..81ce5ccb61 100644
--- a/hrms/locale/hu.po
+++ b/hrms/locale/hu.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: contact@frappe.io\n"
-"POT-Creation-Date: 2024-12-08 09:34+0000\n"
-"PO-Revision-Date: 2024-12-10 00:12\n"
+"POT-Creation-Date: 2024-12-15 09:34+0000\n"
+"PO-Revision-Date: 2024-12-19 01:27\n"
"Last-Translator: contact@frappe.io\n"
"Language-Team: Hungarian\n"
"MIME-Version: 1.0\n"
@@ -351,6 +351,7 @@ msgstr ""
#. Label of the absent_days (Float) field in DocType 'Salary Slip'
#: hrms/payroll/doctype/salary_slip/salary_slip.json
+#: hrms/payroll/report/salary_register/salary_register.py:180
msgid "Absent Days"
msgstr ""
@@ -585,7 +586,7 @@ msgstr ""
msgid "Actual Encashable Days"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:396
+#: hrms/hr/doctype/leave_application/leave_application.py:398
msgid "Actual balances aren't available because the leave application spans over different leave allocations. You can still apply for leaves which would be compensated during the next allocation."
msgstr ""
@@ -1624,7 +1625,7 @@ msgstr ""
msgid "Attendance for employee {0} is already marked for the date {1}: {2}"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:554
+#: hrms/hr/doctype/leave_application/leave_application.py:556
msgid "Attendance for employee {0} is already marked for this day"
msgstr ""
@@ -1950,7 +1951,7 @@ msgstr ""
#: hrms/payroll/doctype/salary_slip/salary_slip.json
#: hrms/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py:29
#: hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py:21
-#: hrms/payroll/report/salary_register/salary_register.py:133
+#: hrms/payroll/report/salary_register/salary_register.py:134
#: hrms/public/js/salary_slip_deductions_report_filters.js:48
msgid "Branch"
msgstr ""
@@ -2091,7 +2092,7 @@ msgstr ""
msgid "Cannot create or change transactions against an Appraisal Cycle with status {0}."
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:561
+#: hrms/hr/doctype/leave_application/leave_application.py:563
msgid "Cannot find active Leave Period"
msgstr ""
@@ -2466,7 +2467,7 @@ msgstr ""
#: hrms/payroll/report/bank_remittance/bank_remittance.js:9
#: hrms/payroll/report/income_tax_computation/income_tax_computation.js:9
#: hrms/payroll/report/salary_register/salary_register.js:39
-#: hrms/payroll/report/salary_register/salary_register.py:154
+#: hrms/payroll/report/salary_register/salary_register.py:155
#: hrms/public/js/salary_slip_deductions_report_filters.js:7
msgid "Company"
msgstr ""
@@ -2913,7 +2914,7 @@ msgstr ""
#: hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
#: hrms/payroll/report/bank_remittance/bank_remittance.py:51
#: hrms/payroll/report/salary_register/salary_register.js:26
-#: hrms/payroll/report/salary_register/salary_register.py:242
+#: hrms/payroll/report/salary_register/salary_register.py:249
#: hrms/payroll/workspace/salary_payout/salary_payout.json
msgid "Currency"
msgstr ""
@@ -3138,7 +3139,7 @@ msgstr ""
#: hrms/payroll/doctype/retention_bonus/retention_bonus.json
#: hrms/payroll/doctype/salary_withholding/salary_withholding.json
#: hrms/payroll/report/income_tax_computation/income_tax_computation.py:528
-#: hrms/payroll/report/salary_register/salary_register.py:127 hrms/setup.py:396
+#: hrms/payroll/report/salary_register/salary_register.py:128 hrms/setup.py:396
msgid "Date of Joining"
msgstr ""
@@ -3214,8 +3215,8 @@ msgstr ""
#. Option for the 'Type' (Select) field in DocType 'Salary Component'
#: hrms/payroll/doctype/salary_component/salary_component.json
-#: hrms/payroll/report/salary_register/salary_register.py:84
-#: hrms/payroll/report/salary_register/salary_register.py:90
+#: hrms/payroll/report/salary_register/salary_register.py:85
+#: hrms/payroll/report/salary_register/salary_register.py:91
msgid "Deduction"
msgstr ""
@@ -3435,7 +3436,7 @@ msgstr ""
#: hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
#: hrms/payroll/report/income_tax_computation/income_tax_computation.js:33
#: hrms/payroll/report/income_tax_computation/income_tax_computation.py:515
-#: hrms/payroll/report/salary_register/salary_register.py:140
+#: hrms/payroll/report/salary_register/salary_register.py:141
#: hrms/public/js/salary_slip_deductions_report_filters.js:42 hrms/setup.py:402
#: hrms/templates/generators/job_opening.html:87
msgid "Department"
@@ -3606,7 +3607,7 @@ msgstr ""
#: hrms/payroll/doctype/salary_slip/salary_slip.json
#: hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
#: hrms/payroll/report/income_tax_computation/income_tax_computation.py:522
-#: hrms/payroll/report/salary_register/salary_register.py:147
+#: hrms/payroll/report/salary_register/salary_register.py:148
msgid "Designation"
msgstr ""
@@ -3843,8 +3844,8 @@ msgstr ""
#. Option for the 'Type' (Select) field in DocType 'Salary Component'
#: hrms/payroll/doctype/salary_component/salary_component.json
-#: hrms/payroll/report/salary_register/salary_register.py:84
-#: hrms/payroll/report/salary_register/salary_register.py:90
+#: hrms/payroll/report/salary_register/salary_register.py:85
+#: hrms/payroll/report/salary_register/salary_register.py:91
msgid "Earning"
msgstr ""
@@ -3943,7 +3944,7 @@ msgstr ""
msgid "Email Template"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:659
+#: hrms/hr/doctype/leave_application/leave_application.py:661
msgid "Email sent to {0}"
msgstr ""
@@ -4110,7 +4111,7 @@ msgstr ""
#: hrms/payroll/report/provident_fund_deductions/provident_fund_deductions.py:20
#: hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py:35
#: hrms/payroll/report/salary_register/salary_register.js:32
-#: hrms/payroll/report/salary_register/salary_register.py:114
+#: hrms/payroll/report/salary_register/salary_register.py:115
#: hrms/public/js/templates/employees_with_unmarked_attendance.html:17
msgid "Employee"
msgstr ""
@@ -4502,7 +4503,7 @@ msgstr ""
#: hrms/payroll/report/professional_tax_deductions/professional_tax_deductions.py:28
#: hrms/payroll/report/provident_fund_deductions/provident_fund_deductions.py:27
#: hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py:28
-#: hrms/payroll/report/salary_register/salary_register.py:121
+#: hrms/payroll/report/salary_register/salary_register.py:122
#: hrms/public/js/templates/employees_with_unmarked_attendance.html:18
msgid "Employee Name"
msgstr ""
@@ -4758,7 +4759,7 @@ msgstr ""
msgid "Employee {0} has already applied for Shift {1}: {2} that overlaps within this period"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:450
+#: hrms/hr/doctype/leave_application/leave_application.py:452
msgid "Employee {0} has already applied for {1} between {2} and {3} : {4}"
msgstr ""
@@ -4786,7 +4787,7 @@ msgstr ""
msgid "Employee {0} relieved on {1} must be set as 'Left'"
msgstr ""
-#: hrms/payroll/doctype/gratuity/gratuity.py:166
+#: hrms/payroll/doctype/gratuity/gratuity.py:163
msgid "Employee: {0} have to complete minimum {1} years for gratuity"
msgstr ""
@@ -4953,7 +4954,7 @@ msgstr ""
#: hrms/payroll/doctype/payroll_period/payroll_period.json
#: hrms/payroll/doctype/payroll_period_date/payroll_period_date.json
#: hrms/payroll/doctype/salary_slip/salary_slip.json
-#: hrms/payroll/report/salary_register/salary_register.py:167
+#: hrms/payroll/report/salary_register/salary_register.py:168
msgid "End Date"
msgstr ""
@@ -6239,7 +6240,7 @@ msgstr ""
#: hrms/payroll/doctype/salary_slip/salary_slip.json
#: hrms/payroll/report/income_tax_deductions/income_tax_deductions.py:54
#: hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py:42
-#: hrms/payroll/report/salary_register/salary_register.py:199
+#: hrms/payroll/report/salary_register/salary_register.py:206
msgid "Gross Pay"
msgstr ""
@@ -6569,7 +6570,7 @@ msgid "Hold"
msgstr ""
#: hrms/hr/doctype/attendance/attendance.py:280
-#: hrms/hr/doctype/leave_application/leave_application.py:1290
+#: hrms/hr/doctype/leave_application/leave_application.py:1292
#: hrms/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.py:46
msgid "Holiday"
msgstr ""
@@ -6970,11 +6971,11 @@ msgstr ""
msgid "Install Frappe HR"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:409
+#: hrms/hr/doctype/leave_application/leave_application.py:411
msgid "Insufficient Balance"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:407
+#: hrms/hr/doctype/leave_application/leave_application.py:409
msgid "Insufficient leave balance for Leave Type {0}"
msgstr ""
@@ -7678,7 +7679,7 @@ msgstr ""
msgid "Leave Application"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:714
+#: hrms/hr/doctype/leave_application/leave_application.py:716
msgid "Leave Application period cannot be across two non-consecutive leave allocations {0} and {1}."
msgstr ""
@@ -7756,7 +7757,7 @@ msgstr ""
msgid "Leave Block List Name"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:1266
+#: hrms/hr/doctype/leave_application/leave_application.py:1268
msgid "Leave Blocked"
msgstr ""
@@ -7932,7 +7933,7 @@ msgstr ""
#. Label of the leave_without_pay (Float) field in DocType 'Salary Slip'
#: hrms/payroll/doctype/salary_slip/salary_slip.json
-#: hrms/payroll/report/salary_register/salary_register.py:173 hrms/setup.py:374
+#: hrms/payroll/report/salary_register/salary_register.py:174 hrms/setup.py:374
#: hrms/setup.py:375
msgid "Leave Without Pay"
msgstr ""
@@ -7967,7 +7968,7 @@ msgstr ""
msgid "Leave cannot be applied/cancelled before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:477
+#: hrms/hr/doctype/leave_application/leave_application.py:479
msgid "Leave of type {0} cannot be longer than {1}."
msgstr ""
@@ -8092,7 +8093,7 @@ msgstr ""
msgid "Loan Product"
msgstr ""
-#: hrms/payroll/report/salary_register/salary_register.py:221 hrms/setup.py:764
+#: hrms/payroll/report/salary_register/salary_register.py:228 hrms/setup.py:764
msgid "Loan Repayment"
msgstr ""
@@ -8368,7 +8369,7 @@ msgstr ""
msgid "Maximum Consecutive Leaves Allowed"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:487
+#: hrms/hr/doctype/leave_application/leave_application.py:489
msgid "Maximum Consecutive Leaves Exceeded"
msgstr ""
@@ -8653,7 +8654,7 @@ msgstr ""
#: hrms/payroll/doctype/salary_slip/salary_slip.json
#: hrms/payroll/doctype/salary_structure/salary_structure.json
#: hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py:49
-#: hrms/payroll/report/salary_register/salary_register.py:235
+#: hrms/payroll/report/salary_register/salary_register.py:242
msgid "Net Pay"
msgstr ""
@@ -8771,7 +8772,7 @@ msgstr ""
msgid "No Leaves Allocated to Employee: {0} for Leave Type: {1}"
msgstr ""
-#: hrms/payroll/doctype/gratuity/gratuity.py:269
+#: hrms/payroll/doctype/gratuity/gratuity.py:266
msgid "No Salary Slip found for Employee: {0}"
msgstr ""
@@ -8807,15 +8808,15 @@ msgstr ""
msgid "No additional expenses has been added"
msgstr ""
-#: hrms/payroll/doctype/gratuity/gratuity.py:285
+#: hrms/payroll/doctype/gratuity/gratuity.py:282
msgid "No applicable Earning component found in last salary slip for Gratuity Rule: {0}"
msgstr ""
-#: hrms/payroll/doctype/gratuity/gratuity.py:298
+#: hrms/payroll/doctype/gratuity/gratuity.py:295
msgid "No applicable Earning components found for Gratuity Rule: {0}"
msgstr ""
-#: hrms/payroll/doctype/gratuity/gratuity.py:258
+#: hrms/payroll/doctype/gratuity/gratuity.py:255
msgid "No applicable slab found for the calculation of gratuity amount as per the Gratuity Rule: {0}"
msgstr ""
@@ -9192,7 +9193,7 @@ msgstr ""
msgid "Opening closed."
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:567
+#: hrms/hr/doctype/leave_application/leave_application.py:569
msgid "Optional Holiday List not set for leave period {0}"
msgstr ""
@@ -9467,7 +9468,7 @@ msgstr ""
#. Label of the payment_days (Float) field in DocType 'Salary Slip'
#. Label of the payment_days_tab (Tab Break) field in DocType 'Salary Slip'
#: hrms/payroll/doctype/salary_slip/salary_slip.json
-#: hrms/payroll/report/salary_register/salary_register.py:179
+#: hrms/payroll/report/salary_register/salary_register.py:186
msgid "Payment Days"
msgstr ""
@@ -9902,7 +9903,7 @@ msgstr ""
msgid "Please set Payroll based on in Payroll settings"
msgstr ""
-#: hrms/payroll/doctype/gratuity/gratuity.py:178
+#: hrms/payroll/doctype/gratuity/gratuity.py:175
msgid "Please set Relieving Date for employee: {0}"
msgstr ""
@@ -9915,11 +9916,11 @@ msgstr ""
msgid "Please set account in Salary Component {0}"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:622
+#: hrms/hr/doctype/leave_application/leave_application.py:624
msgid "Please set default template for Leave Approval Notification in HR Settings."
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:597
+#: hrms/hr/doctype/leave_application/leave_application.py:599
msgid "Please set default template for Leave Status Notification in HR Settings."
msgstr ""
@@ -10457,7 +10458,7 @@ msgid "Reference Document Type"
msgstr ""
#: hrms/hr/doctype/leave_allocation/leave_allocation.py:355
-#: hrms/hr/doctype/leave_application/leave_application.py:481
+#: hrms/hr/doctype/leave_application/leave_application.py:483
#: hrms/payroll/doctype/additional_salary/additional_salary.py:137
#: hrms/payroll/doctype/payroll_entry/payroll_entry.py:91
msgid "Reference: {0}"
@@ -10969,7 +10970,7 @@ msgstr ""
msgid "Row {0}# Allocated amount {1} cannot be greater than unclaimed amount {2}"
msgstr ""
-#: hrms/payroll/doctype/gratuity/gratuity.py:138
+#: hrms/payroll/doctype/gratuity/gratuity.py:135
msgid "Row {0}# Paid Amount cannot be greater than Total amount"
msgstr ""
@@ -11151,7 +11152,7 @@ msgstr ""
msgid "Salary Slip Based on Timesheet"
msgstr ""
-#: hrms/payroll/report/salary_register/salary_register.py:107
+#: hrms/payroll/report/salary_register/salary_register.py:108
msgid "Salary Slip ID"
msgstr ""
@@ -12130,7 +12131,7 @@ msgstr ""
#: hrms/payroll/doctype/payroll_period/payroll_period.json
#: hrms/payroll/doctype/payroll_period_date/payroll_period_date.json
#: hrms/payroll/doctype/salary_slip/salary_slip.json
-#: hrms/payroll/report/salary_register/salary_register.py:161
+#: hrms/payroll/report/salary_register/salary_register.py:162
msgid "Start Date"
msgstr ""
@@ -12625,7 +12626,7 @@ msgstr ""
msgid "The day of the month when leaves should be allocated"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:365
+#: hrms/hr/doctype/leave_application/leave_application.py:367
msgid "The day(s) on which you are applying for leave are holidays. You need not apply for leave."
msgstr ""
@@ -13008,7 +13009,7 @@ msgstr ""
msgid "Total Amount Reimbursed"
msgstr ""
-#: hrms/payroll/doctype/gratuity/gratuity.py:105
+#: hrms/payroll/doctype/gratuity/gratuity.py:102
msgid "Total Amount cannot be zero"
msgstr ""
@@ -13040,7 +13041,7 @@ msgstr ""
#: hrms/payroll/doctype/salary_slip/salary_slip.json
#: hrms/payroll/doctype/salary_structure/salary_structure.json
#: hrms/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py:148
-#: hrms/payroll/report/salary_register/salary_register.py:228
+#: hrms/payroll/report/salary_register/salary_register.py:235
msgid "Total Deduction"
msgstr ""
@@ -13885,7 +13886,7 @@ msgstr ""
msgid "Walk In"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:404
+#: hrms/hr/doctype/leave_application/leave_application.py:406
#: hrms/payroll/doctype/additional_salary/additional_salary.py:153
#: hrms/payroll/doctype/salary_component/salary_component.py:56
#: hrms/payroll/doctype/salary_structure/salary_structure.js:322
@@ -13894,11 +13895,11 @@ msgstr ""
msgid "Warning"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:392
+#: hrms/hr/doctype/leave_application/leave_application.py:394
msgid "Warning: Insufficient leave balance for Leave Type {0} in this allocation."
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:400
+#: hrms/hr/doctype/leave_application/leave_application.py:402
msgid "Warning: Insufficient leave balance for Leave Type {0}."
msgstr ""
@@ -14343,7 +14344,7 @@ msgstr ""
msgid "{0} is not allowed to submit Interview Feedback for the Interview: {1}"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:575
+#: hrms/hr/doctype/leave_application/leave_application.py:577
msgid "{0} is not in Optional Holiday List"
msgstr ""
diff --git a/hrms/locale/tr.po b/hrms/locale/tr.po
index 3b192e890d..447c32f843 100644
--- a/hrms/locale/tr.po
+++ b/hrms/locale/tr.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: contact@frappe.io\n"
-"POT-Creation-Date: 2024-12-08 09:34+0000\n"
-"PO-Revision-Date: 2024-12-10 00:12\n"
+"POT-Creation-Date: 2024-12-15 09:34+0000\n"
+"PO-Revision-Date: 2024-12-20 01:40\n"
"Last-Translator: contact@frappe.io\n"
"Language-Team: Turkish\n"
"MIME-Version: 1.0\n"
@@ -376,6 +376,7 @@ msgstr "Gelmedi"
#. Label of the absent_days (Float) field in DocType 'Salary Slip'
#: hrms/payroll/doctype/salary_slip/salary_slip.json
+#: hrms/payroll/report/salary_register/salary_register.py:180
msgid "Absent Days"
msgstr "Devamsızlık Günleri"
@@ -610,7 +611,7 @@ msgstr "Gerçek Maliyet"
msgid "Actual Encashable Days"
msgstr "Paraya Çevrilebilir Günler"
-#: hrms/hr/doctype/leave_application/leave_application.py:396
+#: hrms/hr/doctype/leave_application/leave_application.py:398
msgid "Actual balances aren't available because the leave application spans over different leave allocations. You can still apply for leaves which would be compensated during the next allocation."
msgstr "Gerçek bakiyeler mevcut değil çünkü izin başvurusu farklı izin atamalarını kapsıyor. Yine de, bir sonraki atamada telafi edilecek izinler için başvuruda bulunabilirsiniz."
@@ -1409,7 +1410,7 @@ msgstr "Nisan"
#: hrms/hr/doctype/goal/goal.js:77
msgid "Archive"
-msgstr ""
+msgstr "Arşiv"
#. Option for the 'Status' (Select) field in DocType 'Goal'
#: hrms/hr/doctype/goal/goal.json
@@ -1422,7 +1423,7 @@ msgstr "Bu dosyayı silmek istediğinizden emin misiniz"
#: frontend/src/components/FormView.vue:223
msgid "Are you sure you want to delete the {0}"
-msgstr ""
+msgstr "{0} öğesini silmek istediğinizden emin misiniz?"
#: hrms/payroll/doctype/salary_slip/salary_slip_list.js:19
msgid "Are you sure you want to email the selected salary slips?"
@@ -1649,7 +1650,7 @@ msgstr ""
msgid "Attendance for employee {0} is already marked for the date {1}: {2}"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:554
+#: hrms/hr/doctype/leave_application/leave_application.py:556
msgid "Attendance for employee {0} is already marked for this day"
msgstr "Personel {0} için bu günün devamlılığı zaten işaretlendi"
@@ -1975,7 +1976,7 @@ msgstr "Bonus Ödeme Tarihi bir tarih olamaz"
#: hrms/payroll/doctype/salary_slip/salary_slip.json
#: hrms/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py:29
#: hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py:21
-#: hrms/payroll/report/salary_register/salary_register.py:133
+#: hrms/payroll/report/salary_register/salary_register.py:134
#: hrms/public/js/salary_slip_deductions_report_filters.js:48
msgid "Branch"
msgstr "Görev Bölümü"
@@ -2116,7 +2117,7 @@ msgstr "Kapalı bir İş İlanı için İş Başvurusu oluşturulamaz"
msgid "Cannot create or change transactions against an Appraisal Cycle with status {0}."
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:561
+#: hrms/hr/doctype/leave_application/leave_application.py:563
msgid "Cannot find active Leave Period"
msgstr "Etkin İzin Dönemi bulunamadı"
@@ -2491,7 +2492,7 @@ msgstr "Komisyon"
#: hrms/payroll/report/bank_remittance/bank_remittance.js:9
#: hrms/payroll/report/income_tax_computation/income_tax_computation.js:9
#: hrms/payroll/report/salary_register/salary_register.js:39
-#: hrms/payroll/report/salary_register/salary_register.py:154
+#: hrms/payroll/report/salary_register/salary_register.py:155
#: hrms/public/js/salary_slip_deductions_report_filters.js:7
msgid "Company"
msgstr "Şirket"
@@ -2938,7 +2939,7 @@ msgstr "Personelin Performans Geri Bildirimi ve Öz Değerlendirme sırasında d
#: hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
#: hrms/payroll/report/bank_remittance/bank_remittance.py:51
#: hrms/payroll/report/salary_register/salary_register.js:26
-#: hrms/payroll/report/salary_register/salary_register.py:242
+#: hrms/payroll/report/salary_register/salary_register.py:249
#: hrms/payroll/workspace/salary_payout/salary_payout.json
msgid "Currency"
msgstr "Para Birimi"
@@ -3163,7 +3164,7 @@ msgstr "Doğum Tarihi"
#: hrms/payroll/doctype/retention_bonus/retention_bonus.json
#: hrms/payroll/doctype/salary_withholding/salary_withholding.json
#: hrms/payroll/report/income_tax_computation/income_tax_computation.py:528
-#: hrms/payroll/report/salary_register/salary_register.py:127 hrms/setup.py:396
+#: hrms/payroll/report/salary_register/salary_register.py:128 hrms/setup.py:396
msgid "Date of Joining"
msgstr "İşe Başlama Tarihi"
@@ -3239,8 +3240,8 @@ msgstr "Gönderilmeyen Vergi Muafiyeti Kanıtı için Vergi İndirimi"
#. Option for the 'Type' (Select) field in DocType 'Salary Component'
#: hrms/payroll/doctype/salary_component/salary_component.json
-#: hrms/payroll/report/salary_register/salary_register.py:84
-#: hrms/payroll/report/salary_register/salary_register.py:90
+#: hrms/payroll/report/salary_register/salary_register.py:85
+#: hrms/payroll/report/salary_register/salary_register.py:91
msgid "Deduction"
msgstr "Kesinti"
@@ -3460,7 +3461,7 @@ msgstr "Teslimat Yolculuğu"
#: hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
#: hrms/payroll/report/income_tax_computation/income_tax_computation.js:33
#: hrms/payroll/report/income_tax_computation/income_tax_computation.py:515
-#: hrms/payroll/report/salary_register/salary_register.py:140
+#: hrms/payroll/report/salary_register/salary_register.py:141
#: hrms/public/js/salary_slip_deductions_report_filters.js:42 hrms/setup.py:402
#: hrms/templates/generators/job_opening.html:87
msgid "Department"
@@ -3631,7 +3632,7 @@ msgstr "İş İlanının Açıklaması"
#: hrms/payroll/doctype/salary_slip/salary_slip.json
#: hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
#: hrms/payroll/report/income_tax_computation/income_tax_computation.py:522
-#: hrms/payroll/report/salary_register/salary_register.py:147
+#: hrms/payroll/report/salary_register/salary_register.py:148
msgid "Designation"
msgstr "Görev Tanımı"
@@ -3868,8 +3869,8 @@ msgstr "Kazanılmış İzinler, bir Personelin şirkette belirli bir süre çal
#. Option for the 'Type' (Select) field in DocType 'Salary Component'
#: hrms/payroll/doctype/salary_component/salary_component.json
-#: hrms/payroll/report/salary_register/salary_register.py:84
-#: hrms/payroll/report/salary_register/salary_register.py:90
+#: hrms/payroll/report/salary_register/salary_register.py:85
+#: hrms/payroll/report/salary_register/salary_register.py:91
msgid "Earning"
msgstr "Kazanma"
@@ -3968,7 +3969,7 @@ msgstr "E-posta Gönderildi"
msgid "Email Template"
msgstr "E-Posta Şablonu"
-#: hrms/hr/doctype/leave_application/leave_application.py:659
+#: hrms/hr/doctype/leave_application/leave_application.py:661
msgid "Email sent to {0}"
msgstr "E-posta {0} adresine gönderildi"
@@ -4135,7 +4136,7 @@ msgstr "Çalışan tarafından tercih edilen e-posta tabanlı çalışana e-post
#: hrms/payroll/report/provident_fund_deductions/provident_fund_deductions.py:20
#: hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py:35
#: hrms/payroll/report/salary_register/salary_register.js:32
-#: hrms/payroll/report/salary_register/salary_register.py:114
+#: hrms/payroll/report/salary_register/salary_register.py:115
#: hrms/public/js/templates/employees_with_unmarked_attendance.html:17
msgid "Employee"
msgstr "Personel"
@@ -4527,7 +4528,7 @@ msgstr ""
#: hrms/payroll/report/professional_tax_deductions/professional_tax_deductions.py:28
#: hrms/payroll/report/provident_fund_deductions/provident_fund_deductions.py:27
#: hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py:28
-#: hrms/payroll/report/salary_register/salary_register.py:121
+#: hrms/payroll/report/salary_register/salary_register.py:122
#: hrms/public/js/templates/employees_with_unmarked_attendance.html:18
msgid "Employee Name"
msgstr "Personel Adı"
@@ -4783,7 +4784,7 @@ msgstr ""
msgid "Employee {0} has already applied for Shift {1}: {2} that overlaps within this period"
msgstr "Personel {0} , bu dönem içinde çakışan {1}: {2} vardiyası için zaten başvuruda bulunmuştur"
-#: hrms/hr/doctype/leave_application/leave_application.py:450
+#: hrms/hr/doctype/leave_application/leave_application.py:452
msgid "Employee {0} has already applied for {1} between {2} and {3} : {4}"
msgstr ""
@@ -4811,7 +4812,7 @@ msgstr "Personel {0}, {1} tarihinde yarım gün çalışacak"
msgid "Employee {0} relieved on {1} must be set as 'Left'"
msgstr ""
-#: hrms/payroll/doctype/gratuity/gratuity.py:166
+#: hrms/payroll/doctype/gratuity/gratuity.py:163
msgid "Employee: {0} have to complete minimum {1} years for gratuity"
msgstr ""
@@ -4978,7 +4979,7 @@ msgstr ""
#: hrms/payroll/doctype/payroll_period/payroll_period.json
#: hrms/payroll/doctype/payroll_period_date/payroll_period_date.json
#: hrms/payroll/doctype/salary_slip/salary_slip.json
-#: hrms/payroll/report/salary_register/salary_register.py:167
+#: hrms/payroll/report/salary_register/salary_register.py:168
msgid "End Date"
msgstr "Bitiş Tarihi"
@@ -5082,7 +5083,7 @@ msgstr "Bazı satırlarda hata var"
#: frontend/src/components/FormView.vue:558
msgid "Error updating {0}"
-msgstr ""
+msgstr "{0} Güncellenirken hata oluştu"
#: hrms/payroll/doctype/salary_slip/salary_slip.py:2270
msgid "Error while evaluating the {doctype} {doclink} at row {row_id}.
Error: {error}
Hint: {description}"
@@ -6264,7 +6265,7 @@ msgstr "Şikayet Türü"
#: hrms/payroll/doctype/salary_slip/salary_slip.json
#: hrms/payroll/report/income_tax_deductions/income_tax_deductions.py:54
#: hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py:42
-#: hrms/payroll/report/salary_register/salary_register.py:199
+#: hrms/payroll/report/salary_register/salary_register.py:206
msgid "Gross Pay"
msgstr "Brüt Ödeme"
@@ -6594,7 +6595,7 @@ msgid "Hold"
msgstr "Beklemede"
#: hrms/hr/doctype/attendance/attendance.py:280
-#: hrms/hr/doctype/leave_application/leave_application.py:1290
+#: hrms/hr/doctype/leave_application/leave_application.py:1292
#: hrms/hr/report/employees_working_on_a_holiday/employees_working_on_a_holiday.py:46
msgid "Holiday"
msgstr "Tatil"
@@ -6995,11 +6996,11 @@ msgstr ""
msgid "Install Frappe HR"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:409
+#: hrms/hr/doctype/leave_application/leave_application.py:411
msgid "Insufficient Balance"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:407
+#: hrms/hr/doctype/leave_application/leave_application.py:409
msgid "Insufficient leave balance for Leave Type {0}"
msgstr ""
@@ -7703,7 +7704,7 @@ msgstr "Tahsisleri Bırak"
msgid "Leave Application"
msgstr "İzin Formu"
-#: hrms/hr/doctype/leave_application/leave_application.py:714
+#: hrms/hr/doctype/leave_application/leave_application.py:716
msgid "Leave Application period cannot be across two non-consecutive leave allocations {0} and {1}."
msgstr "İzin başvuru süresi, ardışık olmayan iki farklı izin tahsisi {0} ve {1} arasında olamaz."
@@ -7781,7 +7782,7 @@ msgstr "izin engel listesi süreleri"
msgid "Leave Block List Name"
msgstr "izin engel listesi adı"
-#: hrms/hr/doctype/leave_application/leave_application.py:1266
+#: hrms/hr/doctype/leave_application/leave_application.py:1268
msgid "Leave Blocked"
msgstr "İzin Engellendi"
@@ -7957,7 +7958,7 @@ msgstr "{0} Türü Ayrılma özelliği değiştirilemez"
#. Label of the leave_without_pay (Float) field in DocType 'Salary Slip'
#: hrms/payroll/doctype/salary_slip/salary_slip.json
-#: hrms/payroll/report/salary_register/salary_register.py:173 hrms/setup.py:374
+#: hrms/payroll/report/salary_register/salary_register.py:174 hrms/setup.py:374
#: hrms/setup.py:375
msgid "Leave Without Pay"
msgstr "Ücretsiz izin"
@@ -7992,7 +7993,7 @@ msgstr "Öncelik tahsis edememek izin {0}, izin özellikleri zaten devredilen ge
msgid "Leave cannot be applied/cancelled before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}"
msgstr "İzin yapısı zaten devredilen gelecek izin tahsisi kayıtlarında olduğu gibi, daha önce {0} iptal / tatbik etmek anlamsız bırakın {1}"
-#: hrms/hr/doctype/leave_application/leave_application.py:477
+#: hrms/hr/doctype/leave_application/leave_application.py:479
msgid "Leave of type {0} cannot be longer than {1}."
msgstr "{0} türündeki izin {1} değerinden daha uzun olamaz."
@@ -8117,7 +8118,7 @@ msgstr "Avans Hesabı"
msgid "Loan Product"
msgstr ""
-#: hrms/payroll/report/salary_register/salary_register.py:221 hrms/setup.py:764
+#: hrms/payroll/report/salary_register/salary_register.py:228 hrms/setup.py:764
msgid "Loan Repayment"
msgstr ""
@@ -8393,7 +8394,7 @@ msgstr "Maksimum Devredilen İzin"
msgid "Maximum Consecutive Leaves Allowed"
msgstr "İzin Verilen Maksimum Ardışık İzinler"
-#: hrms/hr/doctype/leave_application/leave_application.py:487
+#: hrms/hr/doctype/leave_application/leave_application.py:489
msgid "Maximum Consecutive Leaves Exceeded"
msgstr "Maksimum Ardışık İzin Sayısı Aşıldı"
@@ -8678,7 +8679,7 @@ msgstr "Adlandırma Serisi"
#: hrms/payroll/doctype/salary_slip/salary_slip.json
#: hrms/payroll/doctype/salary_structure/salary_structure.json
#: hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py:49
-#: hrms/payroll/report/salary_register/salary_register.py:235
+#: hrms/payroll/report/salary_register/salary_register.py:242
msgid "Net Pay"
msgstr "Net Ödeme"
@@ -8796,7 +8797,7 @@ msgstr "İzin Süresi Bulunamadı"
msgid "No Leaves Allocated to Employee: {0} for Leave Type: {1}"
msgstr "Çalışana Ayrılan Yaprak Yok: {0} İzin Türü için: {1}"
-#: hrms/payroll/doctype/gratuity/gratuity.py:269
+#: hrms/payroll/doctype/gratuity/gratuity.py:266
msgid "No Salary Slip found for Employee: {0}"
msgstr ""
@@ -8832,15 +8833,15 @@ msgstr "veri tarihleri için çalışanlar {0} için bulunma aktif veya varsayı
msgid "No additional expenses has been added"
msgstr "Hiçbir ek masraf eklenmedi"
-#: hrms/payroll/doctype/gratuity/gratuity.py:285
+#: hrms/payroll/doctype/gratuity/gratuity.py:282
msgid "No applicable Earning component found in last salary slip for Gratuity Rule: {0}"
msgstr ""
-#: hrms/payroll/doctype/gratuity/gratuity.py:298
+#: hrms/payroll/doctype/gratuity/gratuity.py:295
msgid "No applicable Earning components found for Gratuity Rule: {0}"
msgstr ""
-#: hrms/payroll/doctype/gratuity/gratuity.py:258
+#: hrms/payroll/doctype/gratuity/gratuity.py:255
msgid "No applicable slab found for the calculation of gratuity amount as per the Gratuity Rule: {0}"
msgstr ""
@@ -9217,7 +9218,7 @@ msgstr ""
msgid "Opening closed."
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:567
+#: hrms/hr/doctype/leave_application/leave_application.py:569
msgid "Optional Holiday List not set for leave period {0}"
msgstr "İsteğe bağlı Tatil Listesi, {0} dönem izin için ayarlanmamış"
@@ -9492,7 +9493,7 @@ msgstr "Ödeme Tarihi"
#. Label of the payment_days (Float) field in DocType 'Salary Slip'
#. Label of the payment_days_tab (Tab Break) field in DocType 'Salary Slip'
#: hrms/payroll/doctype/salary_slip/salary_slip.json
-#: hrms/payroll/report/salary_register/salary_register.py:179
+#: hrms/payroll/report/salary_register/salary_register.py:186
msgid "Payment Days"
msgstr "Ödeme Günleri"
@@ -9927,7 +9928,7 @@ msgstr ""
msgid "Please set Payroll based on in Payroll settings"
msgstr "Lütfen Bordro ayarlarına göre Bordro ayarı"
-#: hrms/payroll/doctype/gratuity/gratuity.py:178
+#: hrms/payroll/doctype/gratuity/gratuity.py:175
msgid "Please set Relieving Date for employee: {0}"
msgstr ""
@@ -9940,11 +9941,11 @@ msgstr "Lütfen Şirket Varsayılanlarında Varsayılan Nakit Hesabı ayarlayın
msgid "Please set account in Salary Component {0}"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:622
+#: hrms/hr/doctype/leave_application/leave_application.py:624
msgid "Please set default template for Leave Approval Notification in HR Settings."
msgstr "Lütfen İK Ayarları'nda Onay Onay Bildirimi için varsayılan şablonu ayarı."
-#: hrms/hr/doctype/leave_application/leave_application.py:597
+#: hrms/hr/doctype/leave_application/leave_application.py:599
msgid "Please set default template for Leave Status Notification in HR Settings."
msgstr "Lütfen İK Ayarları'nda Durum Bildirimi Bırakma için varsayılan şablonu ayarlayın."
@@ -10482,7 +10483,7 @@ msgid "Reference Document Type"
msgstr "Referans Belge Türü"
#: hrms/hr/doctype/leave_allocation/leave_allocation.py:355
-#: hrms/hr/doctype/leave_application/leave_application.py:481
+#: hrms/hr/doctype/leave_application/leave_application.py:483
#: hrms/payroll/doctype/additional_salary/additional_salary.py:137
#: hrms/payroll/doctype/payroll_entry/payroll_entry.py:91
msgid "Reference: {0}"
@@ -10994,7 +10995,7 @@ msgstr ""
msgid "Row {0}# Allocated amount {1} cannot be greater than unclaimed amount {2}"
msgstr "Satır {0} # Tahsis edilen tutarlar {1}, talep edilmeyen tutarlardan {2} daha büyük olamaz"
-#: hrms/payroll/doctype/gratuity/gratuity.py:138
+#: hrms/payroll/doctype/gratuity/gratuity.py:135
msgid "Row {0}# Paid Amount cannot be greater than Total amount"
msgstr ""
@@ -11176,7 +11177,7 @@ msgstr "Maaş Bordrosu"
msgid "Salary Slip Based on Timesheet"
msgstr "Maaş Yapısını Devamlılık Tablosuna Göre Oluştur"
-#: hrms/payroll/report/salary_register/salary_register.py:107
+#: hrms/payroll/report/salary_register/salary_register.py:108
msgid "Salary Slip ID"
msgstr "Maaş kayma kimliği"
@@ -12155,7 +12156,7 @@ msgstr "Başlangıç"
#: hrms/payroll/doctype/payroll_period/payroll_period.json
#: hrms/payroll/doctype/payroll_period_date/payroll_period_date.json
#: hrms/payroll/doctype/salary_slip/salary_slip.json
-#: hrms/payroll/report/salary_register/salary_register.py:161
+#: hrms/payroll/report/salary_register/salary_register.py:162
msgid "Start Date"
msgstr "Başlangıç Tarihi"
@@ -12650,7 +12651,7 @@ msgstr "Maaş Bordrosunda Maaş Bileşeninin Tutar ile Kazanç/Kesintiye katkı
msgid "The day of the month when leaves should be allocated"
msgstr "İzinlerin tahsis edileceği ayın günü"
-#: hrms/hr/doctype/leave_application/leave_application.py:365
+#: hrms/hr/doctype/leave_application/leave_application.py:367
msgid "The day(s) on which you are applying for leave are holidays. You need not apply for leave."
msgstr "Eğer izin için başvuruda bulunulduğu gün (ler) tatildir. İstemenize izin gerekmez."
@@ -13033,7 +13034,7 @@ msgstr "Toplam Tutar"
msgid "Total Amount Reimbursed"
msgstr "Toplam Tutar Geri Çekenler"
-#: hrms/payroll/doctype/gratuity/gratuity.py:105
+#: hrms/payroll/doctype/gratuity/gratuity.py:102
msgid "Total Amount cannot be zero"
msgstr "Toplam Tutar sıfır olamaz"
@@ -13065,7 +13066,7 @@ msgstr "Toplam Beyan Tutarı"
#: hrms/payroll/doctype/salary_slip/salary_slip.json
#: hrms/payroll/doctype/salary_structure/salary_structure.json
#: hrms/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py:148
-#: hrms/payroll/report/salary_register/salary_register.py:228
+#: hrms/payroll/report/salary_register/salary_register.py:235
msgid "Total Deduction"
msgstr "Toplam Kesinti"
@@ -13910,7 +13911,7 @@ msgstr ""
msgid "Walk In"
msgstr "Rezervasyonsuz Müşteri"
-#: hrms/hr/doctype/leave_application/leave_application.py:404
+#: hrms/hr/doctype/leave_application/leave_application.py:406
#: hrms/payroll/doctype/additional_salary/additional_salary.py:153
#: hrms/payroll/doctype/salary_component/salary_component.py:56
#: hrms/payroll/doctype/salary_structure/salary_structure.js:322
@@ -13919,11 +13920,11 @@ msgstr "Rezervasyonsuz Müşteri"
msgid "Warning"
msgstr "Uyarı"
-#: hrms/hr/doctype/leave_application/leave_application.py:392
+#: hrms/hr/doctype/leave_application/leave_application.py:394
msgid "Warning: Insufficient leave balance for Leave Type {0} in this allocation."
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:400
+#: hrms/hr/doctype/leave_application/leave_application.py:402
msgid "Warning: Insufficient leave balance for Leave Type {0}."
msgstr ""
@@ -14368,7 +14369,7 @@ msgstr "{0} tatil değil."
msgid "{0} is not allowed to submit Interview Feedback for the Interview: {1}"
msgstr ""
-#: hrms/hr/doctype/leave_application/leave_application.py:575
+#: hrms/hr/doctype/leave_application/leave_application.py:577
msgid "{0} is not in Optional Holiday List"
msgstr "{0} İsteğe Bağlı Tatil Listesinde değil"
diff --git a/hrms/patches.txt b/hrms/patches.txt
index 8ca18becf8..9f88a81e0f 100644
--- a/hrms/patches.txt
+++ b/hrms/patches.txt
@@ -24,3 +24,4 @@ hrms.patches.v15_0.make_hr_settings_tab_in_company_master
hrms.patches.v15_0.enable_allow_checkin_setting
hrms.patches.v15_0.set_default_asset_action_in_fnf
hrms.patches.v15_0.add_loan_docperms_to_ess #2024-05-14
+hrms.patches.v15_0.migrate_shift_assignment_schedule_to_shift_schedule
diff --git a/hrms/patches/v15_0/migrate_shift_assignment_schedule_to_shift_schedule.py b/hrms/patches/v15_0/migrate_shift_assignment_schedule_to_shift_schedule.py
new file mode 100644
index 0000000000..fe94689a06
--- /dev/null
+++ b/hrms/patches/v15_0/migrate_shift_assignment_schedule_to_shift_schedule.py
@@ -0,0 +1,26 @@
+import frappe
+
+from hrms.hr.doctype.shift_schedule.shift_schedule import get_or_insert_shift_schedule
+
+
+def execute():
+ fields = ["name", "shift_type", "frequency", "employee", "shift_status", "enabled", "create_shifts_after"]
+ for doc in frappe.get_all("Shift Assignment Schedule", fields=fields):
+ repeat_on_days = frappe.get_all(
+ "Assignment Rule Day", {"parent": doc.name}, pluck="day", distinct=True
+ )
+ shift_schedule_name = get_or_insert_shift_schedule(doc.shift_type, doc.frequency, repeat_on_days)
+
+ schedule_assignment = frappe.get_doc(
+ {
+ "doctype": "Shift Schedule Assignment",
+ "shift_schedule": shift_schedule_name,
+ "employee": doc.employee,
+ "shift_status": doc.shift_status,
+ "enabled": doc.enabled,
+ "create_shifts_after": doc.create_shifts_after,
+ }
+ ).insert()
+
+ for d in frappe.get_all("Shift Assignment", filters={"schedule": doc.name}, pluck="name"):
+ frappe.db.set_value("Shift Assignment", d, "shift_schedule_assignment", schedule_assignment.name)
diff --git a/hrms/public/js/utils/index.js b/hrms/public/js/utils/index.js
index b13082b8db..c2626999c4 100644
--- a/hrms/public/js/utils/index.js
+++ b/hrms/public/js/utils/index.js
@@ -34,7 +34,7 @@ $.extend(hrms, {
}
if (missing_fields.length) {
- let message = __("Mandatory fields required for this action");
+ let message = __("Mandatory fields required for this action:");
message += "