|
- import { getPublicHolidaysForNYears } from "@/app/utils/holidayUtils";
- import { HolidaysResult } from "../holidays";
- import { LeaveEntry, RecordTimeLeaveInput, TimeEntry } from "./actions";
- import { convertDateArrayToString } from "@/app/utils/formatUtil";
- import compact from "lodash/compact";
- import dayjs from "dayjs";
-
- export type TimeEntryError = {
- [field in keyof TimeEntry]?: string;
- };
-
- /**
- * @param entry - the time entry
- * @returns an object where the keys are the error fields and the values the error message, and undefined if there are no errors
- */
- export const validateTimeEntry = (
- entry: Partial<TimeEntry>,
- isHoliday: boolean,
- ): TimeEntryError | undefined => {
- // Test for errors
- const error: TimeEntryError = {};
-
- // Either normal or other hours need to be inputted
- if (!entry.inputHours && !entry.otHours) {
- error[isHoliday ? "otHours" : "inputHours"] = "Required";
- } else if (entry.inputHours && isHoliday) {
- error.inputHours = "Cannot input normal hours on holidays";
- } else if (entry.inputHours && entry.inputHours <= 0) {
- error.inputHours =
- "Input hours should be between 0 and {{DAILY_NORMAL_MAX_HOURS}}";
- } else if (entry.inputHours && entry.inputHours > DAILY_NORMAL_MAX_HOURS) {
- error.inputHours =
- "Input hours should be between 0 and {{DAILY_NORMAL_MAX_HOURS}}";
- } else if (entry.otHours && entry.otHours <= 0) {
- error.otHours = "Hours should be bigger than 0";
- }
-
- // If there is a project id, there should also be taskGroupId, taskId, inputHours
- if (entry.projectId) {
- if (!entry.taskGroupId) {
- error.taskGroupId = "Required";
- } else if (!entry.taskId) {
- error.taskId = "Required";
- }
- } else {
- if (entry.taskGroupId && !entry.taskId) {
- error.taskId = "Required";
- } else if (!entry.remark) {
- error.remark = "Required for non-billable tasks";
- }
- }
-
- return Object.keys(error).length > 0 ? error : undefined;
- };
-
- export type LeaveEntryError = {
- [field in keyof LeaveEntry]?: string;
- };
-
- export const validateLeaveEntry = (
- entry: Partial<LeaveEntry>,
- isHoliday: boolean,
- ): LeaveEntryError | undefined => {
- // Test for errrors
- const error: LeaveEntryError = {};
- if (!entry.leaveTypeId) {
- error.leaveTypeId = "Required";
- } else if (entry.inputHours && isHoliday) {
- error.inputHours = "Cannot input normal hours on holidays";
- } else if (!entry.inputHours) {
- error.inputHours = "Required";
- } else if (
- entry.inputHours &&
- (entry.inputHours <= 0 || entry.inputHours > DAILY_NORMAL_MAX_HOURS)
- ) {
- error.inputHours =
- "Input hours should be between 0 and {{DAILY_NORMAL_MAX_HOURS}}";
- }
-
- return Object.keys(error).length > 0 ? error : undefined;
- };
-
- export const validateTimeLeaveRecord = (
- records: RecordTimeLeaveInput,
- companyHolidays: HolidaysResult[],
- isFullTime?: boolean,
- ): { [date: string]: string } | undefined => {
- const errors: { [date: string]: string } = {};
-
- const holidays = new Set(
- compact([
- ...getPublicHolidaysForNYears(2).map((h) => h.date),
- ...companyHolidays.map((h) => convertDateArrayToString(h.date)),
- ]),
- );
-
- Object.keys(records).forEach((date) => {
- const dayJsObj = dayjs(date);
- const isHoliday =
- holidays.has(date) || dayJsObj.day() === 0 || dayJsObj.day() === 6;
-
- const entries = records[date];
- // Check each entry
- for (const entry of entries) {
- let entryError;
- if (entry.type === "leaveEntry") {
- entryError = validateLeaveEntry(entry, isHoliday);
- } else {
- entryError = validateTimeEntry(entry, isHoliday);
- }
-
- if (entryError) {
- errors[date] = "There are errors in the entries";
- return;
- }
- }
-
- // Check total hours
- const totalHourError = checkTotalHours(
- entries.filter((e) => e.type === "timeEntry") as TimeEntry[],
- entries.filter((e) => e.type === "leaveEntry") as LeaveEntry[],
- isHoliday,
- isFullTime,
- );
-
- if (totalHourError) {
- errors[date] = totalHourError;
- }
- });
-
- return Object.keys(errors).length > 0 ? errors : undefined;
- };
-
- export const checkTotalHours = (
- timeEntries: TimeEntry[],
- leaves: LeaveEntry[],
- isHoliday?: boolean,
- isFullTime?: boolean,
- ): string | undefined => {
- const leaveHours = leaves.reduce((acc, entry) => acc + entry.inputHours, 0);
-
- const totalInputHours = timeEntries.reduce((acc, entry) => {
- return acc + (entry.inputHours || 0);
- }, 0);
-
- const totalOtHours = timeEntries.reduce((acc, entry) => {
- return acc + (entry.otHours || 0);
- }, 0);
-
- if (totalInputHours + leaveHours > DAILY_NORMAL_MAX_HOURS) {
- return "The daily normal hours (timesheet hours + leave hours) cannot be more than {{DAILY_NORMAL_MAX_HOURS}}. Please use other hours for exceeding hours or decrease the leave hours.";
- } else if (
- isFullTime &&
- !isHoliday &&
- totalInputHours + leaveHours !== DAILY_NORMAL_MAX_HOURS
- ) {
- return "The daily normal hours (timesheet hours + leave hours) for full-time staffs should be {{DAILY_NORMAL_MAX_HOURS}}.";
- } else if (
- totalInputHours + totalOtHours + leaveHours >
- TIMESHEET_DAILY_MAX_HOURS
- ) {
- return "The daily total hours cannot be more than {{TIMESHEET_DAILY_MAX_HOURS}}";
- }
- };
-
- export const DAILY_NORMAL_MAX_HOURS = 8;
- export const TIMESHEET_DAILY_MAX_HOURS = 20;
|