Skip to content

Commit

Permalink
Merge pull request #2140 from frappe/version-15-hotfix
Browse files Browse the repository at this point in the history
chore: release v15
  • Loading branch information
ruchamahabal authored Aug 31, 2024
2 parents a75dc65 + e2f8d8a commit 90b7aba
Show file tree
Hide file tree
Showing 86 changed files with 5,686 additions and 1,330 deletions.
4 changes: 2 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ trim_trailing_whitespace = true
charset = utf-8

# js indentation settings
[{*.js,*.vue,*.css,*.scss,*.html}]
[{*.js,*.ts,*.vue,*.css,*.scss,*.html}]
indent_style = tab
indent_size = 4
max_line_length = 99
max_line_length = 99
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ node_modules
hrms/docs/current
hrms/public/frontend
hrms/www/hrms.html
hrms/public/roster
hrms/www/roster.html
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ repos:
rev: v3.1.0
hooks:
- id: prettier
types_or: [javascript, vue, css, scss]
# Ignore fronetend folder and any files that might contain jinja / bundles
types_or: [javascript, ts, vue, css, scss]
# Ignore frontend folder and any files that might contain jinja / bundles
exclude: |
(?x)^(
frontend/.*|
Expand All @@ -46,4 +46,4 @@ repos:
ci:
autoupdate_schedule: weekly
skip: []
submodules: false
submodules: false
2 changes: 1 addition & 1 deletion frappe-ui
Submodule frappe-ui updated 53 files
+3 −2 package.json
+4 −2 src/components/Autocomplete.vue
+11 −2 src/components/Avatar.vue
+1 −0 src/components/Breadcrumbs.vue
+247 −0 src/components/Calendar.story.md
+138 −0 src/components/Calendar.story.vue
+432 −0 src/components/Calendar/Calendar.vue
+113 −0 src/components/Calendar/CalendarDaily.vue
+512 −0 src/components/Calendar/CalendarEvent.vue
+143 −0 src/components/Calendar/CalendarMonthly.vue
+49 −0 src/components/Calendar/CalendarTimeMarker.vue
+252 −0 src/components/Calendar/CalendarWeekly.vue
+63 −0 src/components/Calendar/EventModalContent.vue
+46 −0 src/components/Calendar/FloatingPopover.vue
+210 −0 src/components/Calendar/NewEventModal.vue
+37 −0 src/components/Calendar/ShowMoreCalendarEvent.vue
+277 −0 src/components/Calendar/calendarUtils.js
+54 −0 src/components/Calendar/composables/useCalendarData.js
+41 −0 src/components/Calendar/composables/useEventModal.js
+7 −4 src/components/Checkbox.vue
+36 −0 src/components/DatePicker.story.vue
+108 −72 src/components/DatePicker.vue
+333 −0 src/components/DateRangePicker.vue
+405 −0 src/components/DateTimePicker.vue
+11 −3 src/components/Dialog.vue
+3 −3 src/components/FeatherIcon.vue
+6 −6 src/components/FileUploader.vue
+4 −4 src/components/ListFilter/ListFilter.vue
+2 −2 src/components/ListFilter/SearchComplete.vue
+1 −1 src/components/ListView/ListHeader.vue
+19 −7 src/components/ListView/ListRow.vue
+2 −2 src/components/ListView/ListView.vue
+19 −10 src/components/Popover.vue
+4 −4 src/components/Switch.vue
+17 −3 src/components/TabButtons.vue
+2 −2 src/components/TextEditor/InsertVideo.vue
+1 −1 src/components/TextEditor/TextEditor.vue
+1 −1 src/components/TextEditor/image-extension.js
+1 −1 src/components/TextEditor/mention.js
+1 −1 src/components/Tooltip/Tooltip.vue
+2 −2 src/components/toast.js
+4 −2 src/fonts/Inter/inter.css
+4 −0 src/index.js
+6 −6 src/resources/documentResource.js
+8 −8 src/resources/listResource.js
+1 −1 src/resources/plugin.js
+1 −1 src/utils/call.js
+1 −1 src/utils/confirmDialog.js
+1 −1 src/utils/debounce.ts
+1 −1 src/utils/frappeRequest.js
+1 −1 src/utils/markdown.js
+1 −1 src/utils/pageMeta.js
+4 −0 src/utils/tailwind.config.js
12 changes: 6 additions & 6 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@
"@ionic/vue": "^7.4.3",
"@ionic/vue-router": "^7.4.3",
"@vitejs/plugin-vue": "^4.4.0",
"autoprefixer": "^10.4.2",
"dayjs": "^1.11.7",
"feather-icons": "^4.28.0",
"frappe-ui": "^0.1.59",
"vue": "^3.2.25",
"vue-router": "^4.0.12",
"autoprefixer": "^10.4.2",
"firebase": "^10.8.0",
"frappe-ui": "^0.1.67",
"postcss": "^8.4.5",
"tailwindcss": "^3.0.15",
"vite": "^5.1.4",
"vite-plugin-pwa": "^0.19.0",
"workbox-precaching": "^7.0.0",
"vue": "^3.2.25",
"vue-router": "^4.0.12",
"workbox-core": "^7.0.0",
"firebase": "^10.8.0"
"workbox-precaching": "^7.0.0"
},
"devDependencies": {
"eslint": "^8.39.0",
Expand Down
251 changes: 251 additions & 0 deletions hrms/api/roster.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
import frappe
from frappe import _
from frappe.utils import add_days, date_diff

from erpnext.setup.doctype.employee.employee import get_holiday_list_for_employee

from hrms.hr.doctype.shift_assignment.shift_assignment import ShiftAssignment
from hrms.hr.doctype.shift_assignment_tool.shift_assignment_tool import create_shift_assignment


@frappe.whitelist()
def get_values(doctype: str, name: str, fields: list) -> dict[str, str]:
return frappe.db.get_value(doctype, name, fields, as_dict=True)


@frappe.whitelist()
def get_events(
month_start: str, month_end: str, employee_filters: dict[str, str], shift_filters: dict[str, str]
) -> dict[str, list[dict]]:
holidays = get_holidays(month_start, month_end, employee_filters)
leaves = get_leaves(month_start, month_end, employee_filters)
shifts = get_shifts(month_start, month_end, employee_filters, shift_filters)

events = {}
for event in [holidays, leaves, shifts]:
for key, value in event.items():
if key in events:
events[key].extend(value)
else:
events[key] = value
return events


@frappe.whitelist()
def create_shift_assignment_schedule(
employee: str,
company: str,
shift_type: str,
status: str,
start_date: str,
end_date: str | None,
repeat_on_days: list[str],
frequency: str,
) -> None:
schedule = 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,
"employee": employee,
"company": company,
"shift_type": shift_type,
"shift_status": status,
}
).insert()

if not end_date or date_diff(end_date, start_date) <= 90:
return schedule.create_shifts(start_date, end_date)

frappe.enqueue(schedule.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"):
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.whitelist()
def swap_shift(
src_shift: str, src_date: str, tgt_employee: str, tgt_date: str, tgt_shift: str | None
) -> None:
if src_shift == tgt_shift:
frappe.throw(_("Source and target shifts cannot be the same"))

if tgt_shift:
tgt_shift_doc = frappe.get_doc("Shift Assignment", tgt_shift)
tgt_company = tgt_shift_doc.company
break_shift(tgt_shift_doc, tgt_date)
else:
tgt_company = frappe.db.get_value("Employee", tgt_employee, "company")

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
)

if tgt_shift:
insert_shift(
src_shift_doc.employee,
src_shift_doc.company,
tgt_shift_doc.shift_type,
src_date,
src_date,
tgt_shift_doc.status,
)


@frappe.whitelist()
def break_shift(assignment: str | ShiftAssignment, date: str) -> None:
if isinstance(assignment, str):
assignment = frappe.get_doc("Shift Assignment", assignment)

if assignment.end_date and date_diff(assignment.end_date, date) < 0:
frappe.throw(_("Cannot break shift after end date"))
if date_diff(assignment.start_date, date) > 0:
frappe.throw(_("Cannot break shift before start date"))

employee = assignment.employee
company = assignment.company
shift_type = assignment.shift_type
status = assignment.status
end_date = assignment.end_date

if date_diff(date, assignment.start_date) == 0:
assignment.cancel()
assignment.delete()
else:
assignment.end_date = add_days(date, -1)
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)


@frappe.whitelist()
def insert_shift(
employee: str, company: str, shift_type: str, start_date: str, end_date: str | None, status: str
) -> None:
filters = {
"doctype": "Shift Assignment",
"employee": employee,
"company": company,
"shift_type": shift_type,
"status": status,
}
prev_shift = frappe.db.exists(dict({"end_date": add_days(start_date, -1)}, **filters))
next_shift = (
frappe.db.exists(dict({"start_date": add_days(end_date, 1)}, **filters)) if end_date else None
)

if prev_shift:
if next_shift:
end_date = frappe.db.get_value("Shift Assignment", next_shift, "end_date")
frappe.db.set_value("Shift Assignment", next_shift, "docstatus", 2)
frappe.delete_doc("Shift Assignment", next_shift)
frappe.db.set_value("Shift Assignment", prev_shift, "end_date", end_date or None)

elif next_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)


def get_holidays(month_start: str, month_end: str, employee_filters: dict[str, str]) -> dict[str, list[dict]]:
holidays = {}

for employee in frappe.get_list("Employee", filters=employee_filters, pluck="name"):
if holiday_list := get_holiday_list_for_employee(employee, raise_exception=False):
holidays[employee] = frappe.get_all(
"Holiday",
filters={"parent": holiday_list, "holiday_date": ["between", [month_start, month_end]]},
fields=["name as holiday", "holiday_date", "description", "weekly_off"],
)

return holidays


def get_leaves(month_start: str, month_end: str, employee_filters: dict[str, str]) -> dict[str, list[dict]]:
LeaveApplication = frappe.qb.DocType("Leave Application")
Employee = frappe.qb.DocType("Employee")

query = (
frappe.qb.select(
LeaveApplication.name.as_("leave"),
LeaveApplication.employee,
LeaveApplication.leave_type,
LeaveApplication.from_date,
LeaveApplication.to_date,
)
.from_(LeaveApplication)
.left_join(Employee)
.on(LeaveApplication.employee == Employee.name)
.where(
(LeaveApplication.docstatus == 1)
& (LeaveApplication.status == "Approved")
& (LeaveApplication.from_date <= month_end)
& (LeaveApplication.to_date >= month_start)
)
)

for filter in employee_filters:
query = query.where(Employee[filter] == employee_filters[filter])

return group_by_employee(query.run(as_dict=True))


def get_shifts(
month_start: str, month_end: str, employee_filters: dict[str, str], shift_filters: dict[str, str]
) -> dict[str, list[dict]]:
ShiftAssignment = frappe.qb.DocType("Shift Assignment")
ShiftType = frappe.qb.DocType("Shift Type")
Employee = frappe.qb.DocType("Employee")

query = (
frappe.qb.select(
ShiftAssignment.name,
ShiftAssignment.employee,
ShiftAssignment.shift_type,
ShiftAssignment.start_date,
ShiftAssignment.end_date,
ShiftAssignment.status,
ShiftType.start_time,
ShiftType.end_time,
ShiftType.color,
)
.from_(ShiftAssignment)
.left_join(ShiftType)
.on(ShiftAssignment.shift_type == ShiftType.name)
.left_join(Employee)
.on(ShiftAssignment.employee == Employee.name)
.where(
(ShiftAssignment.docstatus == 1)
& (ShiftAssignment.start_date <= month_end)
& ((ShiftAssignment.end_date >= month_start) | (ShiftAssignment.end_date.isnull()))
)
)

for filter in employee_filters:
query = query.where(Employee[filter] == employee_filters[filter])

for filter in shift_filters:
query = query.where(ShiftAssignment[filter] == shift_filters[filter])

return group_by_employee(query.run(as_dict=True))


def group_by_employee(events: list[dict]) -> dict[str, list[dict]]:
grouped_events = {}
for event in events:
grouped_events.setdefault(event["employee"], []).append(
{k: v for k, v in event.items() if k != "employee"}
)
return grouped_events
2 changes: 2 additions & 0 deletions hrms/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@

website_route_rules = [
{"from_route": "/hrms/<path:app_path>", "to_route": "hrms"},
{"from_route": "/hr/<path:app_path>", "to_route": "roster"},
]
# Jinja
# ----------
Expand Down Expand Up @@ -220,6 +221,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",
],
"daily": [
"hrms.controllers.employee_reminders.send_birthday_reminders",
Expand Down
10 changes: 9 additions & 1 deletion hrms/hr/doctype/shift_assignment/shift_assignment.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"employee_name",
"shift_type",
"status",
"schedule",
"column_break_3",
"company",
"start_date",
Expand Down Expand Up @@ -101,11 +102,18 @@
"fieldtype": "Select",
"label": "Status",
"options": "Active\nInactive"
},
{
"fieldname": "schedule",
"fieldtype": "Link",
"label": "Schedule",
"options": "Shift Assignment Schedule",
"read_only": 1
}
],
"is_submittable": 1,
"links": [],
"modified": "2024-04-04 17:13:13.137431",
"modified": "2024-05-31 16:41:32.869130",
"modified_by": "Administrator",
"module": "HR",
"name": "Shift Assignment",
Expand Down
Loading

0 comments on commit 90b7aba

Please sign in to comment.