feat(Result): ajusted return values to match Result pattern.
This commit is contained in:
parent
a8d53ab0aa
commit
1d9eaeab30
|
|
@ -2,11 +2,11 @@ export type Result<T, E> =
|
||||||
| { success: true; data: T }
|
| { success: true; data: T }
|
||||||
| { success: false; error: E };
|
| { success: false; error: E };
|
||||||
|
|
||||||
const success = <T>(data: T): Result<T, never> => {
|
// const success = <T>(data: T): Result<T, never> => {
|
||||||
return { success: true, data };
|
// return { success: true, data };
|
||||||
}
|
// }
|
||||||
|
|
||||||
const failure = <E>(error: E): Result<never, E> => {
|
// const failure = <E>(error: E): Result<never, E> => {
|
||||||
return { success: false, error };
|
// return { success: false, error };
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,28 @@
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
import { Prisma, PrismaClient } from '@prisma/client';
|
||||||
import { getWeekStart, getWeekEnd, computeHours } from 'src/common/utils/date-utils';
|
import { getWeekStart, getWeekEnd, computeHours } from 'src/common/utils/date-utils';
|
||||||
import { PrismaService } from 'src/prisma/prisma.service';
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
import { DAILY_LIMIT_HOURS, WEEKLY_LIMIT_HOURS } from 'src/time-and-attendance/utils/constants.utils';
|
import { DAILY_LIMIT_HOURS, WEEKLY_LIMIT_HOURS } from 'src/time-and-attendance/utils/constants.utils';
|
||||||
import { Tx, WeekOvertimeSummary } from 'src/time-and-attendance/utils/type.utils';
|
|
||||||
|
|
||||||
|
|
||||||
|
type Tx = Prisma.TransactionClient | PrismaClient;
|
||||||
|
|
||||||
|
type WeekOvertimeSummary = {
|
||||||
|
week_start:string;
|
||||||
|
week_end: string;
|
||||||
|
week_total_hours: number;
|
||||||
|
weekly_overtime: number;
|
||||||
|
daily_overtime_kept: number;
|
||||||
|
total_overtime: number;
|
||||||
|
breakdown: Array<{
|
||||||
|
date:string;
|
||||||
|
day_hours: number;
|
||||||
|
day_overtime: number;
|
||||||
|
daily_kept: number;
|
||||||
|
running_total_before: number;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class OvertimeService {
|
export class OvertimeService {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { Controller, Post, Param, Body, Patch, Delete, Req, UnauthorizedException } from "@nestjs/common";
|
import { Controller, Post, Param, Body, Patch, Delete, Req, UnauthorizedException } from "@nestjs/common";
|
||||||
import { CreateExpenseResult } from "src/time-and-attendance/utils/type.utils";
|
|
||||||
import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service";
|
import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service";
|
||||||
import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto";
|
import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto";
|
||||||
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { toDateFromString, toStringFromDate, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils";
|
import { toDateFromString, toStringFromDate, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils";
|
||||||
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
import { NormalizedExpense } from "src/time-and-attendance/utils/type.utils";
|
// import { NormalizedExpense } from "src/time-and-attendance/utils/type.utils";
|
||||||
import { expense_select } from "src/time-and-attendance/utils/selects.utils";
|
import { expense_select } from "src/time-and-attendance/utils/selects.utils";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto";
|
import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto";
|
||||||
|
|
@ -8,6 +8,7 @@ import { ExpenseEntity } from "src/time-and-attendance/expenses/dtos/expense-ent
|
||||||
import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto";
|
import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto";
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { Result } from "src/common/errors/result-error.factory";
|
import { Result } from "src/common/errors/result-error.factory";
|
||||||
|
import { NormalizedExpense } from "src/time-and-attendance/utils/type.utils";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto";
|
import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto";
|
||||||
import { LeaveRequestRow } from "src/time-and-attendance/utils/type.utils";
|
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils";
|
||||||
|
|
||||||
|
type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>;
|
||||||
|
|
||||||
const toNum = (value?: Prisma.Decimal | null) =>
|
const toNum = (value?: Prisma.Decimal | null) =>
|
||||||
value !== null && value !== undefined ? Number(value) : undefined;
|
value !== null && value !== undefined ? Number(value) : undefined;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
import { LeaveRequestViewDto } from "src/time-and-attendance/leave-requests/dtos/leave-request-view.dto";
|
import { LeaveRequestViewDto } from "src/time-and-attendance/leave-requests/dtos/leave-request-view.dto";
|
||||||
import { mapArchiveRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests-archive.mapper";
|
import { mapArchiveRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests-archive.mapper";
|
||||||
import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper";
|
import { mapRowToView } from "src/time-and-attendance/leave-requests/mappers/leave-requests.mapper";
|
||||||
import { LeaveRequestArchiveRow } from "src/time-and-attendance/leave-requests/utils/leave-requests-archive.select";
|
import { LeaveRequestArchiveRow } from "src/time-and-attendance/leave-requests/utils/leave-requests-archive.select";
|
||||||
import { LeaveRequestRow } from "src/time-and-attendance/utils/type.utils";
|
import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils";
|
||||||
|
|
||||||
|
export type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>;
|
||||||
|
|
||||||
/** Active (table leave_requests) : proxy to base mapper */
|
/** Active (table leave_requests) : proxy to base mapper */
|
||||||
export function mapRowToViewWithDays(row: LeaveRequestRow): LeaveRequestViewDto {
|
export function mapRowToViewWithDays(row: LeaveRequestRow): LeaveRequestViewDto {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { PresetResponse, ShiftResponse } from "src/time-and-attendance/utils/type.utils";
|
import { PresetResponse, ShiftResponse } from "src/time-and-attendance/utils/type.utils";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { Prisma } from "@prisma/client";
|
|
||||||
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
import { Result } from "src/common/errors/result-error.factory";
|
import { Result } from "src/common/errors/result-error.factory";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { Injectable, BadRequestException, NotFoundException, ConflictException } from "@nestjs/common";
|
import { Injectable, BadRequestException, NotFoundException, ConflictException } from "@nestjs/common";
|
||||||
import { CreatePresetResult, DeletePresetResult, UpdatePresetResult } from "src/time-and-attendance/utils/type.utils";
|
|
||||||
import { Prisma, Weekday } from "@prisma/client";
|
import { Prisma, Weekday } from "@prisma/client";
|
||||||
import { toDateFromString, toHHmmFromDate } from "src/time-and-attendance/utils/date-time.utils";
|
import { toDateFromString, toHHmmFromDate } from "src/time-and-attendance/utils/date-time.utils";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
|
@ -21,7 +20,7 @@ export class SchedulePresetsUpsertService {
|
||||||
async createPreset(email: string, dto: SchedulePresetsDto): Promise<Result<SchedulePresetsDto, string>> {
|
async createPreset(email: string, dto: SchedulePresetsDto): Promise<Result<SchedulePresetsDto, string>> {
|
||||||
try {
|
try {
|
||||||
const shifts_data = await this.normalizePresetShifts(dto);
|
const shifts_data = await this.normalizePresetShifts(dto);
|
||||||
if (!shifts_data) return { success: false, error: `Employee with email: ${email} or dto not found` };
|
if (!shifts_data.success) return { success: false, error: `Employee with email: ${email} or dto not found` };
|
||||||
|
|
||||||
const employee_id = await this.emailResolver.findIdByEmail(email);
|
const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||||
if (!employee_id.success) return { success: false, error: employee_id.error };
|
if (!employee_id.success) return { success: false, error: employee_id.error };
|
||||||
|
|
@ -38,7 +37,7 @@ export class SchedulePresetsUpsertService {
|
||||||
employee_id: employee_id.data,
|
employee_id: employee_id.data,
|
||||||
name: dto.name,
|
name: dto.name,
|
||||||
is_default: !!dto.is_default,
|
is_default: !!dto.is_default,
|
||||||
shifts: { create: shifts_data },
|
shifts: { create: shifts_data.data },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return { success: true, data: created }
|
return { success: true, data: created }
|
||||||
|
|
@ -66,6 +65,7 @@ export class SchedulePresetsUpsertService {
|
||||||
if (!existing) return { success: false, error: `Preset "${dto.name}" not found` };
|
if (!existing) return { success: false, error: `Preset "${dto.name}" not found` };
|
||||||
|
|
||||||
const shifts_data = await this.normalizePresetShifts(dto);
|
const shifts_data = await this.normalizePresetShifts(dto);
|
||||||
|
if(!shifts_data.success) return { success: false, error: 'An error occured during normalization'}
|
||||||
|
|
||||||
await this.prisma.$transaction(async (tx) => {
|
await this.prisma.$transaction(async (tx) => {
|
||||||
if (typeof dto.is_default === 'boolean') {
|
if (typeof dto.is_default === 'boolean') {
|
||||||
|
|
@ -87,33 +87,34 @@ export class SchedulePresetsUpsertService {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (shifts_data.length <= 0) return { success: false, error: 'Preset shifts to update not found' };
|
if (shifts_data.data.length <= 0) return { success: false, error: 'Preset shifts to update not found' };
|
||||||
|
|
||||||
await tx.schedulePresetShifts.deleteMany({ where: { preset_id: existing.id } });
|
await tx.schedulePresetShifts.deleteMany({ where: { preset_id: existing.id } });
|
||||||
|
|
||||||
try {
|
// try {
|
||||||
const create_many_data: Prisma.SchedulePresetShiftsCreateManyInput[] =
|
// const create_many_data: Result<Prisma.SchedulePresetShiftsCreateManyInput[], string> =
|
||||||
shifts_data.map((shift) => {
|
// shifts_data.data.map((shift) => {
|
||||||
if (!shift.bank_code || !('connect' in shift.bank_code) || typeof shift.bank_code.connect?.id !== 'number') {
|
// if (!shift.bank_code || !('connect' in shift.bank_code) || typeof shift.bank_code.connect?.id !== 'number') {
|
||||||
throw new NotFoundException(`Bank code is required for updates( ${shift.week_day}, ${shift.sort_order})`);
|
// return { success: false, error: `Bank code is required for updates( ${shift.week_day}, ${shift.sort_order})`}
|
||||||
}
|
// }
|
||||||
const bank_code_id = shift.bank_code.connect.id;
|
// const bank_code_id = shift.bank_code.connect.id;
|
||||||
return {
|
// return {
|
||||||
preset_id: existing.id,
|
// preset_id: existing.id,
|
||||||
week_day: shift.week_day,
|
// week_day: shift.week_day,
|
||||||
sort_order: shift.sort_order,
|
// sort_order: shift.sort_order,
|
||||||
start_time: shift.start_time,
|
// start_time: shift.start_time,
|
||||||
end_time: shift.end_time,
|
// end_time: shift.end_time,
|
||||||
is_remote: shift.is_remote ?? false,
|
// is_remote: shift.is_remote ?? false,
|
||||||
bank_code_id: bank_code_id,
|
// bank_code_id: bank_code_id,
|
||||||
};
|
// };
|
||||||
});
|
// });
|
||||||
await tx.schedulePresetShifts.createMany({ data: create_many_data });
|
// if(!create_many_data.success) return { success: false, error: 'Invalid data'}
|
||||||
|
// await tx.schedulePresetShifts.createMany({ data: create_many_data.data });
|
||||||
|
|
||||||
return { success: true, data: create_many_data }
|
// return { success: true, data: create_many_data }
|
||||||
} catch (error) {
|
// } catch (error) {
|
||||||
return { success: false, error: 'An error occured. Invalid data detected. ' };
|
// return { success: false, error: 'An error occured. Invalid data detected. ' };
|
||||||
}
|
// }
|
||||||
});
|
});
|
||||||
|
|
||||||
const saved = await this.prisma.schedulePresets.findUnique({
|
const saved = await this.prisma.schedulePresets.findUnique({
|
||||||
|
|
@ -175,15 +176,16 @@ export class SchedulePresetsUpsertService {
|
||||||
//resolve bank_code_id using type and convert hours to TIME and valid shifts end/start
|
//resolve bank_code_id using type and convert hours to TIME and valid shifts end/start
|
||||||
private async normalizePresetShifts(
|
private async normalizePresetShifts(
|
||||||
dto: SchedulePresetsDto
|
dto: SchedulePresetsDto
|
||||||
): Promise<Prisma.SchedulePresetShiftsCreateWithoutPresetInput[]> {
|
): Promise<Result<Prisma.SchedulePresetShiftsCreateWithoutPresetInput[], string>> {
|
||||||
if (!dto.preset_shifts?.length) throw new NotFoundException(`Empty or preset shifts not found`);
|
if (!dto.preset_shifts?.length) throw new NotFoundException(`Empty or preset shifts not found`);
|
||||||
|
|
||||||
const types = Array.from(new Set(dto.preset_shifts.map((shift) => shift.type)));
|
const types = Array.from(new Set(dto.preset_shifts.map((shift) => shift.type)));
|
||||||
const bank_code_set = new Map<string, number>();
|
const bank_code_set = new Map<string, number>();
|
||||||
|
|
||||||
for (const type of types) {
|
for (const type of types) {
|
||||||
const { id } = await this.typeResolver.findIdAndModifierByType(type);
|
const bank_code = await this.typeResolver.findIdAndModifierByType(type);
|
||||||
bank_code_set.set(type, id)
|
if (!bank_code.success) return { success: false, error: 'Bank_code not found' }
|
||||||
|
bank_code_set.set(type, bank_code.data.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
const pair_set = new Set<string>();
|
const pair_set = new Set<string>();
|
||||||
|
|
@ -216,6 +218,6 @@ export class SchedulePresetsUpsertService {
|
||||||
is_remote: !!shift.is_remote,
|
is_remote: !!shift.is_remote,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return items;
|
return { success: true, data: items };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { Body, Controller, Delete, Param, Patch, Post, Req } from "@nestjs/common";
|
import { Body, Controller, Delete, Param, Patch, Post, Req } from "@nestjs/common";
|
||||||
import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto";
|
import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto";
|
||||||
import { CreateShiftResult, UpdateShiftResult } from "src/time-and-attendance/utils/type.utils";
|
|
||||||
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
||||||
import { GLOBAL_CONTROLLER_ROLES } from "src/common/shared/role-groupes";
|
import { GLOBAL_CONTROLLER_ROLES } from "src/common/shared/role-groupes";
|
||||||
import { Result } from "src/common/errors/result-error.factory";
|
import { Result } from "src/common/errors/result-error.factory";
|
||||||
|
|
|
||||||
|
|
@ -63,12 +63,13 @@ export class ShiftsCreateService {
|
||||||
try {
|
try {
|
||||||
//transform string format to date and HHmm
|
//transform string format to date and HHmm
|
||||||
const normed_shift = await this.normalizeShiftDto(dto);
|
const normed_shift = await this.normalizeShiftDto(dto);
|
||||||
if (normed_shift.end_time <= normed_shift.start_time) return {
|
if(!normed_shift.success) return { success: false, error: 'An error occured during normalization' }
|
||||||
|
if (normed_shift.data.end_time <= normed_shift.data.start_time) return {
|
||||||
success: false,
|
success: false,
|
||||||
error: `INVALID_SHIFT - `
|
error: `INVALID_SHIFT - `
|
||||||
+ `start_time: ${toStringFromHHmm(normed_shift.start_time)},`
|
+ `start_time: ${toStringFromHHmm(normed_shift.data.start_time)},`
|
||||||
+ `end_time: ${toStringFromHHmm(normed_shift.end_time)},`
|
+ `end_time: ${toStringFromHHmm(normed_shift.data.end_time)},`
|
||||||
+ `date: ${toStringFromDate(normed_shift.date)}.`
|
+ `date: ${toStringFromDate(normed_shift.data.date)}.`
|
||||||
}
|
}
|
||||||
//fetch the right timesheet
|
//fetch the right timesheet
|
||||||
const timesheet = await this.prisma.timesheets.findUnique({
|
const timesheet = await this.prisma.timesheets.findUnique({
|
||||||
|
|
@ -78,16 +79,17 @@ export class ShiftsCreateService {
|
||||||
if (!timesheet) return {
|
if (!timesheet) return {
|
||||||
success: false,
|
success: false,
|
||||||
error: `INVALID_TIMESHEET -`
|
error: `INVALID_TIMESHEET -`
|
||||||
+ `start_time: ${toStringFromHHmm(normed_shift.start_time)},`
|
+ `start_time: ${toStringFromHHmm(normed_shift.data.start_time)},`
|
||||||
+ `end_time: ${toStringFromHHmm(normed_shift.end_time)},`
|
+ `end_time: ${toStringFromHHmm(normed_shift.data.end_time)},`
|
||||||
+ `date: ${toStringFromDate(normed_shift.date)}.`
|
+ `date: ${toStringFromDate(normed_shift.data.date)}.`
|
||||||
}
|
}
|
||||||
//finds bank_code_id using the type
|
//finds bank_code_id using the type
|
||||||
const bank_code = await this.typeResolver.findBankCodeIDByType(dto.type);
|
const bank_code_id = await this.typeResolver.findBankCodeIDByType(dto.type);
|
||||||
|
if (!bank_code_id.success) return { success: false, error: bank_code_id.error };
|
||||||
|
|
||||||
//fetchs existing shifts from DB to check for overlaps
|
//fetchs existing shifts from DB to check for overlaps
|
||||||
const existing_shifts = await this.prisma.shifts.findMany({
|
const existing_shifts = await this.prisma.shifts.findMany({
|
||||||
where: { timesheet_id: timesheet.id, date: normed_shift.date },
|
where: { timesheet_id: timesheet.id, date: normed_shift.data.date },
|
||||||
select: { id: true, date: true, start_time: true, end_time: true },
|
select: { id: true, date: true, start_time: true, end_time: true },
|
||||||
});
|
});
|
||||||
for (const existing of existing_shifts) {
|
for (const existing of existing_shifts) {
|
||||||
|
|
@ -96,16 +98,16 @@ export class ShiftsCreateService {
|
||||||
const existing_date = await toDateFromString(existing.date);
|
const existing_date = await toDateFromString(existing.date);
|
||||||
|
|
||||||
const has_overlap = overlaps(
|
const has_overlap = overlaps(
|
||||||
{ start: normed_shift.start_time, end: normed_shift.end_time, date: normed_shift.date },
|
{ start: normed_shift.data.start_time, end: normed_shift.data.end_time, date: normed_shift.data.date },
|
||||||
{ start: existing_start, end: existing_end, date: existing_date },
|
{ start: existing_start, end: existing_end, date: existing_date },
|
||||||
);
|
);
|
||||||
if (has_overlap) {
|
if (has_overlap) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: `SHIFT_OVERLAP`
|
error: `SHIFT_OVERLAP`
|
||||||
+ `new shift: ${toStringFromHHmm(normed_shift.start_time)}–${toStringFromHHmm(normed_shift.end_time)} `
|
+ `new shift: ${toStringFromHHmm(normed_shift.data.start_time)}–${toStringFromHHmm(normed_shift.data.end_time)} `
|
||||||
+ `existing shift: ${toStringFromHHmm(existing.start_time)}–${toStringFromHHmm(existing.end_time)} `
|
+ `existing shift: ${toStringFromHHmm(existing.start_time)}–${toStringFromHHmm(existing.end_time)} `
|
||||||
+ `date: ${toStringFromDate(normed_shift.date)})`,
|
+ `date: ${toStringFromDate(normed_shift.data.date)})`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -114,10 +116,10 @@ export class ShiftsCreateService {
|
||||||
const created_shift = await this.prisma.shifts.create({
|
const created_shift = await this.prisma.shifts.create({
|
||||||
data: {
|
data: {
|
||||||
timesheet_id: timesheet.id,
|
timesheet_id: timesheet.id,
|
||||||
bank_code_id: bank_code.id,
|
bank_code_id: bank_code_id.data,
|
||||||
date: normed_shift.date,
|
date: normed_shift.data.date,
|
||||||
start_time: normed_shift.start_time,
|
start_time: normed_shift.data.start_time,
|
||||||
end_time: normed_shift.end_time,
|
end_time: normed_shift.data.end_time,
|
||||||
is_approved: dto.is_approved,
|
is_approved: dto.is_approved,
|
||||||
is_remote: dto.is_remote,
|
is_remote: dto.is_remote,
|
||||||
comment: dto.comment ?? '',
|
comment: dto.comment ?? '',
|
||||||
|
|
@ -145,11 +147,14 @@ export class ShiftsCreateService {
|
||||||
// LOCAL HELPERS
|
// LOCAL HELPERS
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
//converts all string hours and date to Date and HHmm formats
|
//converts all string hours and date to Date and HHmm formats
|
||||||
private normalizeShiftDto = async (dto: ShiftDto): Promise<Normalized> => {
|
private normalizeShiftDto = async (dto: ShiftDto): Promise<Result<Normalized, string>> => {
|
||||||
const { id: bank_code_id } = await this.typeResolver.findBankCodeIDByType(dto.type);
|
const bank_code_id = await this.typeResolver.findBankCodeIDByType(dto.type);
|
||||||
|
if(!bank_code_id.success) return { success: false, error: 'Bank_code not found'}
|
||||||
|
|
||||||
const date = toDateFromString(dto.date);
|
const date = toDateFromString(dto.date);
|
||||||
const start_time = toHHmmFromString(dto.start_time);
|
const start_time = toHHmmFromString(dto.start_time);
|
||||||
const end_time = toHHmmFromString(dto.end_time);
|
const end_time = toHHmmFromString(dto.end_time);
|
||||||
return { date, start_time, end_time, bank_code_id: bank_code_id };
|
|
||||||
|
return { success: true, data: {date, start_time, end_time, bank_code_id: bank_code_id.data} };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,10 @@
|
||||||
import { BadRequestException, NotFoundException, ConflictException } from "@nestjs/common";
|
|
||||||
import { Result } from "src/common/errors/result-error.factory";
|
import { Result } from "src/common/errors/result-error.factory";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto";
|
import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto";
|
||||||
import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-entity.dto";
|
import { toDateFromString, toHHmmFromString, toStringFromHHmm, toStringFromDate, overlaps } from "src/time-and-attendance/utils/date-time.utils";
|
||||||
import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto";
|
|
||||||
import { ShiftsCreateService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-create.service";
|
|
||||||
import { toDateFromString, toHHmmFromString, overlaps, toStringFromHHmm, toStringFromDate } from "src/time-and-attendance/utils/date-time.utils";
|
|
||||||
import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils";
|
import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils";
|
||||||
import { shift_select } from "src/time-and-attendance/utils/selects.utils";
|
import { shift_select } from "src/time-and-attendance/utils/selects.utils";
|
||||||
import { UpdateShiftResult, Normalized } from "src/time-and-attendance/utils/type.utils";
|
import { Normalized } from "src/time-and-attendance/utils/type.utils";
|
||||||
|
|
||||||
export class ShiftsUpdateDeleteService {
|
export class ShiftsUpdateDeleteService {
|
||||||
constructor(
|
constructor(
|
||||||
|
|
@ -22,7 +18,7 @@ export class ShiftsUpdateDeleteService {
|
||||||
if (!Array.isArray(shifts) || shifts.length === 0) return { success: false, error: 'No data received' };
|
if (!Array.isArray(shifts) || shifts.length === 0) return { success: false, error: 'No data received' };
|
||||||
|
|
||||||
//calls the update functions and await the return of successfull result or not
|
//calls the update functions and await the return of successfull result or not
|
||||||
const results = await Promise.allSettled(shifts.map(shift => this.updateShifts(shift)));
|
const results = await Promise.allSettled(shifts.map(shift => this.updateShift(shift)));
|
||||||
|
|
||||||
//return arrays of updated shifts or errors
|
//return arrays of updated shifts or errors
|
||||||
const updated_shifts: ShiftDto[] = [];
|
const updated_shifts: ShiftDto[] = [];
|
||||||
|
|
@ -52,7 +48,7 @@ export class ShiftsUpdateDeleteService {
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
// UPDATE
|
// UPDATE
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
async updateShifts(dto: ShiftDto): Promise<Result<ShiftDto, string>> {
|
async updateShift(dto: ShiftDto): Promise<Result<ShiftDto, string>> {
|
||||||
try {
|
try {
|
||||||
//finds original shift
|
//finds original shift
|
||||||
const original = await this.prisma.shifts.findFirst({
|
const original = await this.prisma.shifts.findFirst({
|
||||||
|
|
@ -63,32 +59,36 @@ export class ShiftsUpdateDeleteService {
|
||||||
|
|
||||||
//transform string format to date and HHmm
|
//transform string format to date and HHmm
|
||||||
const normed_shift = await this.normalizeShiftDto(dto);
|
const normed_shift = await this.normalizeShiftDto(dto);
|
||||||
if (normed_shift.end_time <= normed_shift.start_time) return {
|
if (!normed_shift.success) return { success: false, error: 'An error occured during normalization' }
|
||||||
|
if (normed_shift.data.end_time <= normed_shift.data.start_time) return {
|
||||||
success: false,
|
success: false,
|
||||||
error: `INVALID_SHIFT - `
|
error: `INVALID_SHIFT - `
|
||||||
+ `start_time: ${toStringFromHHmm(normed_shift.start_time)},`
|
+ `start_time: ${toStringFromHHmm(normed_shift.data.start_time)},`
|
||||||
+ `end_time: ${toStringFromHHmm(normed_shift.end_time)},`
|
+ `end_time: ${toStringFromHHmm(normed_shift.data.end_time)},`
|
||||||
+ `date: ${toStringFromDate(normed_shift.date)}.`
|
+ `date: ${toStringFromDate(normed_shift.data.date)}.`
|
||||||
};
|
};
|
||||||
|
const overlap_check = await this.overlapChecker(normed_shift.data);
|
||||||
|
if(!overlap_check.success) return { success: false, error: 'Invalid shift, overlaps with existing shifts'}
|
||||||
|
|
||||||
//finds bank_code_id using the type
|
//finds bank_code_id using the type
|
||||||
const bank_code = await this.typeResolver.findBankCodeIDByType(dto.type);
|
const bank_code = await this.typeResolver.findBankCodeIDByType(dto.type);
|
||||||
|
if (!bank_code.success) return { success: false, error: 'No bank_code_id found' };
|
||||||
|
|
||||||
//updates sent to DB
|
//updates sent to DB
|
||||||
const updated = await this.prisma.shifts.update({
|
const updated = await this.prisma.shifts.update({
|
||||||
where: { id: original.id },
|
where: { id: original.id },
|
||||||
data: {
|
data: {
|
||||||
date: normed_shift.date,
|
date: normed_shift.data.date,
|
||||||
start_time: normed_shift.start_time,
|
start_time: normed_shift.data.start_time,
|
||||||
end_time: normed_shift.end_time,
|
end_time: normed_shift.data.end_time,
|
||||||
bank_code_id: bank_code.id,
|
bank_code_id: bank_code.data,
|
||||||
comment: dto.comment,
|
comment: dto.comment,
|
||||||
is_approved: dto.is_approved,
|
is_approved: dto.is_approved,
|
||||||
is_remote: dto.is_remote,
|
is_remote: dto.is_remote,
|
||||||
},
|
},
|
||||||
select: shift_select,
|
select: shift_select,
|
||||||
});
|
});
|
||||||
if(!updated) return {success: false, error: ' An error occured during update, Invalid Datas'};
|
if (!updated) return { success: false, error: ' An error occured during update, Invalid Datas' };
|
||||||
|
|
||||||
// builds an object to return for display in the frontend
|
// builds an object to return for display in the frontend
|
||||||
const shift: ShiftDto = {
|
const shift: ShiftDto = {
|
||||||
|
|
@ -134,11 +134,42 @@ export class ShiftsUpdateDeleteService {
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
// helpers
|
// helpers
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
private normalizeShiftDto = async (dto: ShiftDto): Promise<Normalized> => {
|
private normalizeShiftDto = async (dto: ShiftDto): Promise<Result<Normalized, string>> => {
|
||||||
const { id: bank_code_id } = await this.typeResolver.findBankCodeIDByType(dto.type);
|
const bank_code_id = await this.typeResolver.findBankCodeIDByType(dto.type);
|
||||||
|
if (!bank_code_id.success) return { success: false, error: 'Bank_code not found' }
|
||||||
|
|
||||||
const date = toDateFromString(dto.date);
|
const date = toDateFromString(dto.date);
|
||||||
const start_time = toHHmmFromString(dto.start_time);
|
const start_time = toHHmmFromString(dto.start_time);
|
||||||
const end_time = toHHmmFromString(dto.end_time);
|
const end_time = toHHmmFromString(dto.end_time);
|
||||||
return { date, start_time, end_time, bank_code_id: bank_code_id };
|
|
||||||
|
return { success: true, data: { date, start_time, end_time, bank_code_id: bank_code_id.data } };
|
||||||
|
}
|
||||||
|
|
||||||
|
private overlapChecker = async (dto: Normalized): Promise<Result<void, string>> => {
|
||||||
|
|
||||||
|
const existing_shifts = await this.prisma.shifts.findMany({
|
||||||
|
where: { date: dto.date },
|
||||||
|
select: { id: true, date: true, start_time: true, end_time: true },
|
||||||
|
});
|
||||||
|
for (const existing of existing_shifts) {
|
||||||
|
const existing_start = toDateFromString(existing.start_time);
|
||||||
|
const existing_end = toDateFromString(existing.end_time);
|
||||||
|
const existing_date = toDateFromString(existing.date);
|
||||||
|
|
||||||
|
const has_overlap = overlaps(
|
||||||
|
{ start: dto.start_time, end: dto.end_time, date: dto.date },
|
||||||
|
{ start: existing_start, end: existing_end, date: existing_date },
|
||||||
|
);
|
||||||
|
if (has_overlap) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: `SHIFT_OVERLAP`
|
||||||
|
+ `new shift: ${toStringFromHHmm(dto.start_time)}–${toStringFromHHmm(dto.end_time)} `
|
||||||
|
+ `existing shift: ${toStringFromHHmm(existing.start_time)}–${toStringFromHHmm(existing.end_time)} `
|
||||||
|
+ `date: ${toStringFromDate(dto.date)})`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { success: true, data: undefined }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,12 +1,28 @@
|
||||||
import { sevenDaysFrom, toStringFromDate, toHHmmFromDate, toDateFromString } from "src/time-and-attendance/utils/date-time.utils";
|
import { sevenDaysFrom, toStringFromDate, toHHmmFromDate, toDateFromString } from "src/time-and-attendance/utils/date-time.utils";
|
||||||
import { NUMBER_OF_TIMESHEETS_TO_RETURN } from "src/time-and-attendance/utils/constants.utils";
|
import { NUMBER_OF_TIMESHEETS_TO_RETURN } from "src/time-and-attendance/utils/constants.utils";
|
||||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { TotalExpenses, TotalHours } from "src/time-and-attendance/utils/type.utils";
|
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
import { Timesheets } from "src/time-and-attendance/time-tracker/timesheets/dtos/timesheet.dto";
|
import { Timesheets } from "src/time-and-attendance/time-tracker/timesheets/dtos/timesheet.dto";
|
||||||
import { Result } from "src/common/errors/result-error.factory";
|
import { Result } from "src/common/errors/result-error.factory";
|
||||||
|
|
||||||
|
export type TotalHours = {
|
||||||
|
regular: number;
|
||||||
|
evening: number;
|
||||||
|
emergency: number;
|
||||||
|
overtime: number;
|
||||||
|
vacation: number;
|
||||||
|
holiday: number;
|
||||||
|
sick: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TotalExpenses = {
|
||||||
|
expenses: number;
|
||||||
|
per_diem: number;
|
||||||
|
on_call: number;
|
||||||
|
mileage: number;
|
||||||
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class GetTimesheetsOverviewService {
|
export class GetTimesheetsOverviewService {
|
||||||
constructor(
|
constructor(
|
||||||
|
|
@ -61,14 +77,14 @@ export class GetTimesheetsOverviewService {
|
||||||
const employee_fullname = `${user.first_name} ${user.last_name}`.trim();
|
const employee_fullname = `${user.first_name} ${user.last_name}`.trim();
|
||||||
|
|
||||||
//maps all timesheet's infos
|
//maps all timesheet's infos
|
||||||
const timesheets = rows.map((timesheet) => this.mapOneTimesheet(timesheet));
|
const timesheets = await Promise.all(rows.map((timesheet) => this.mapOneTimesheet(timesheet)));
|
||||||
return { success: true, data: { employee_fullname, timesheets } };
|
|
||||||
|
return { success: true, data:{ employee_fullname, timesheets} };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { success: false, error}
|
return { success: false, error}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------
|
||||||
// MAPPERS & HELPERS
|
// MAPPERS & HELPERS
|
||||||
//-----------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------
|
||||||
|
|
@ -111,11 +127,14 @@ export class GetTimesheetsOverviewService {
|
||||||
const weekly_hours: TotalHours[] = [emptyHours()];
|
const weekly_hours: TotalHours[] = [emptyHours()];
|
||||||
const weekly_expenses: TotalExpenses[] = [emptyExpenses()];
|
const weekly_expenses: TotalExpenses[] = [emptyExpenses()];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//map of days
|
//map of days
|
||||||
const days = day_dates.map((date) => {
|
const days = day_dates.map((date) => {
|
||||||
const date_iso = toStringFromDate(date);
|
const date_iso = toStringFromDate(date);
|
||||||
const shifts_source = shifts_by_date.get(date_iso) ?? [];
|
const shifts_source = shifts_by_date.get(date_iso) ?? [];
|
||||||
const expenses_source = expenses_by_date.get(date_iso) ?? [];
|
const expenses_source = expenses_by_date.get(date_iso) ?? [];
|
||||||
|
|
||||||
//inner map of shifts
|
//inner map of shifts
|
||||||
const shifts = shifts_source.map((shift) => ({
|
const shifts = shifts_source.map((shift) => ({
|
||||||
timesheet_id: shift.timesheet_id,
|
timesheet_id: shift.timesheet_id,
|
||||||
|
|
@ -139,6 +158,7 @@ export class GetTimesheetsOverviewService {
|
||||||
is_approved: expense.is_approved ?? false,
|
is_approved: expense.is_approved ?? false,
|
||||||
comment: expense.comment ?? '',
|
comment: expense.comment ?? '',
|
||||||
supervisor_comment: expense.supervisor_comment,
|
supervisor_comment: expense.supervisor_comment,
|
||||||
|
type: expense.type,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
//daily totals
|
//daily totals
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,47 @@
|
||||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
import { Prisma, PrismaClient } from "@prisma/client";
|
import { Prisma, PrismaClient } from "@prisma/client";
|
||||||
|
import { Result } from "src/common/errors/result-error.factory";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
|
||||||
type Tx = Prisma.TransactionClient | PrismaClient;
|
type Tx = Prisma.TransactionClient | PrismaClient;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class BankCodesResolver {
|
export class BankCodesResolver {
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(private readonly prisma: PrismaService) { }
|
||||||
|
|
||||||
//find id and modifier by type
|
//find id and modifier by type
|
||||||
readonly findIdAndModifierByType = async ( type: string, client?: Tx
|
readonly findIdAndModifierByType = async (type: string, client?: Tx
|
||||||
): Promise<{id:number; modifier: number }> => {
|
): Promise<Result<{ id: number; modifier: number }, string>> => {
|
||||||
const db = client ?? this.prisma;
|
const db = client ?? this.prisma;
|
||||||
const bank = await db.bankCodes.findFirst({
|
const bank = await db.bankCodes.findFirst({
|
||||||
where: { type },
|
where: { type },
|
||||||
select: { id: true, modifier: true },
|
select: { id: true, modifier: true },
|
||||||
});
|
});
|
||||||
|
if (!bank) return { success: false, error: `Unknown bank code type: ${type}` };
|
||||||
|
|
||||||
if(!bank) throw new NotFoundException(`Unknown bank code type: ${type}`);
|
return { success: true, data: { id: bank.id, modifier: bank.modifier } };
|
||||||
return { id: bank.id, modifier: bank.modifier };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//finds only id by type
|
//finds only id by type
|
||||||
readonly findBankCodeIDByType = async (type: string, client?: Tx) => {
|
readonly findBankCodeIDByType = async (type: string, client?: Tx): Promise<Result<number, string>> => {
|
||||||
const db = client ?? this.prisma;
|
const db = client ?? this.prisma;
|
||||||
const bank_code_id = await db.bankCodes.findFirst({
|
const bank_code = await db.bankCodes.findFirst({
|
||||||
where: { type },
|
where: { type },
|
||||||
select: {id: true},
|
select: { id: true },
|
||||||
});
|
});
|
||||||
if(!bank_code_id) throw new NotFoundException(`Unkown bank type: ${type}`);
|
if (!bank_code) return { success: false, error:`Unkown bank type: ${type}`};
|
||||||
return bank_code_id;
|
|
||||||
|
return { success: true, data: bank_code.id};
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly findTypeByBankCodeId = async (bank_code_id: number, client?: Tx) => {
|
readonly findTypeByBankCodeId = async (bank_code_id: number, client?: Tx): Promise<Result<string, string>> => {
|
||||||
const db = client ?? this.prisma;
|
const db = client ?? this.prisma;
|
||||||
const type = await db.bankCodes.findFirst({
|
const bank_code = await db.bankCodes.findFirst({
|
||||||
where: { id: bank_code_id },
|
where: { id: bank_code_id },
|
||||||
select: { type: true },
|
select: { type: true },
|
||||||
});
|
});
|
||||||
if(!type) throw new NotFoundException(`Type with id : ${bank_code_id} not found`);
|
if (!bank_code) return {success: false, error: `Type with id : ${bank_code_id} not found` }
|
||||||
return type;
|
|
||||||
|
return {success: true, data: bank_code.type};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { Prisma, PrismaClient } from "@prisma/client";
|
import { Prisma, PrismaClient } from "@prisma/client";
|
||||||
|
import { Result } from "src/common/errors/result-error.factory";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
|
||||||
type Tx = Prisma.TransactionClient | PrismaClient;
|
type Tx = Prisma.TransactionClient | PrismaClient;
|
||||||
|
|
@ -8,15 +9,15 @@ type Tx = Prisma.TransactionClient | PrismaClient;
|
||||||
export class FullNameResolver {
|
export class FullNameResolver {
|
||||||
constructor(private readonly prisma: PrismaService){}
|
constructor(private readonly prisma: PrismaService){}
|
||||||
|
|
||||||
readonly resolveFullName = async (employee_id: number, client?: Tx): Promise<string> =>{
|
readonly resolveFullName = async (employee_id: number, client?: Tx): Promise<Result<string, string>> =>{
|
||||||
const db = client ?? this.prisma;
|
const db = client ?? this.prisma;
|
||||||
const employee = await db.employees.findUnique({
|
const employee = await db.employees.findUnique({
|
||||||
where: { id: employee_id },
|
where: { id: employee_id },
|
||||||
select: { user: { select: {first_name: true, last_name: true} } },
|
select: { user: { select: {first_name: true, last_name: true} } },
|
||||||
});
|
});
|
||||||
if(!employee) throw new NotFoundException(`Unknown user with name: ${employee_id}`)
|
if(!employee) return { success: false, error: `Unknown user with id ${employee_id}`}
|
||||||
|
|
||||||
const full_name = ( employee.user.first_name + " " + employee.user.last_name ) || " ";
|
const full_name = ( employee.user.first_name + " " + employee.user.last_name ) || " ";
|
||||||
return full_name;
|
return {success: true, data: full_name };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,14 +1,23 @@
|
||||||
import { Prisma, PrismaClient } from "@prisma/client";
|
import { Prisma, PrismaClient } from "@prisma/client";
|
||||||
import { NotFoundException } from "@nestjs/common";
|
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { ShiftKey } from "src/time-and-attendance/utils/type.utils";
|
import { Result } from "src/common/errors/result-error.factory";
|
||||||
|
|
||||||
type Tx = Prisma.TransactionClient | PrismaClient;
|
type Tx = Prisma.TransactionClient | PrismaClient;
|
||||||
|
|
||||||
|
interface ShiftKey {
|
||||||
|
timesheet_id: number;
|
||||||
|
date: Date;
|
||||||
|
start_time: Date;
|
||||||
|
end_time: Date;
|
||||||
|
bank_code_id: number;
|
||||||
|
is_remote: boolean;
|
||||||
|
comment?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export class ShiftIdResolver {
|
export class ShiftIdResolver {
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
readonly findShiftIdByData = async ( key: ShiftKey, client?: Tx ): Promise<{id:number}> => {
|
readonly findShiftIdByData = async ( key: ShiftKey, client?: Tx ): Promise<Result<number, string>> => {
|
||||||
const db = client ?? this.prisma;
|
const db = client ?? this.prisma;
|
||||||
const shift = await db.shifts.findFirst({
|
const shift = await db.shifts.findFirst({
|
||||||
where: {
|
where: {
|
||||||
|
|
@ -23,7 +32,8 @@ export class ShiftIdResolver {
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!shift) throw new NotFoundException(`shift not found`);
|
if(!shift) return { success: false, error: `shift not found`}
|
||||||
return { id: shift.id };
|
|
||||||
|
return { success: true, data: shift.id };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -51,36 +51,6 @@ export const leaveRequestsSelect = {
|
||||||
},
|
},
|
||||||
} satisfies Prisma.LeaveRequestsSelect;
|
} satisfies Prisma.LeaveRequestsSelect;
|
||||||
|
|
||||||
|
|
||||||
export const EXPENSE_SELECT = {
|
|
||||||
date: true,
|
|
||||||
amount: true,
|
|
||||||
mileage: true,
|
|
||||||
comment: true,
|
|
||||||
is_approved: true,
|
|
||||||
supervisor_comment: true,
|
|
||||||
bank_code: { select: { type: true } },
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const EXPENSE_ASC_ORDER = { date: 'asc' as const };
|
|
||||||
|
|
||||||
export const PAY_PERIOD_SELECT = {
|
|
||||||
period_start: true,
|
|
||||||
period_end: true,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const SHIFT_SELECT = {
|
|
||||||
date: true,
|
|
||||||
start_time: true,
|
|
||||||
end_time: true,
|
|
||||||
comment: true,
|
|
||||||
is_approved: true,
|
|
||||||
is_remote: true,
|
|
||||||
bank_code: {select: { type: true } },
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const SHIFT_ASC_ORDER = [{date: 'asc' as const}, {start_time: 'asc' as const}];
|
|
||||||
|
|
||||||
export const timesheet_select = {
|
export const timesheet_select = {
|
||||||
id: true,
|
id: true,
|
||||||
employee_id: true,
|
employee_id: true,
|
||||||
|
|
|
||||||
|
|
@ -1,47 +1,9 @@
|
||||||
import { Prisma, PrismaClient } from "@prisma/client";
|
|
||||||
import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto";
|
|
||||||
import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto";
|
|
||||||
import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto";
|
|
||||||
import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-entity.dto";
|
|
||||||
import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils";
|
|
||||||
|
|
||||||
|
|
||||||
export type TotalHours = {
|
|
||||||
regular: number;
|
|
||||||
evening: number;
|
|
||||||
emergency: number;
|
|
||||||
overtime: number;
|
|
||||||
vacation: number;
|
|
||||||
holiday: number;
|
|
||||||
sick: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TotalExpenses = {
|
|
||||||
expenses: number;
|
|
||||||
per_diem: number;
|
|
||||||
on_call: number;
|
|
||||||
mileage: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Normalized = {
|
export type Normalized = {
|
||||||
date: Date;
|
date: Date;
|
||||||
start_time: Date;
|
start_time: Date;
|
||||||
end_time: Date;
|
end_time: Date;
|
||||||
bank_code_id: number;
|
bank_code_id: number;
|
||||||
};
|
};
|
||||||
export type CreateShiftResult = { ok: true; data: GetShiftDto } | { ok: false; error: any };
|
|
||||||
export type UpdateShiftResult = { ok: true; id: number; data: GetShiftDto } | { ok: false; id: number; error: any };
|
|
||||||
export type NormedOk = {
|
|
||||||
index: number;
|
|
||||||
dto: ShiftEntity;
|
|
||||||
normed: Normalized;
|
|
||||||
timesheet_id: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type DeletePresetResult = { ok: true; id: number; } | { ok: false; id: number; error: any };
|
|
||||||
export type CreatePresetResult = { ok: true; } | { ok: false; error: any };
|
|
||||||
export type UpdatePresetResult = { ok: true; id: number; data: SchedulePresetsDto } | { ok: false; id: number; error: any };
|
|
||||||
|
|
||||||
|
|
||||||
export type NormalizedExpense = {
|
export type NormalizedExpense = {
|
||||||
date: Date;
|
date: Date;
|
||||||
|
|
@ -51,9 +13,6 @@ export type NormalizedExpense = {
|
||||||
parsed_mileage?: number;
|
parsed_mileage?: number;
|
||||||
parsed_attachment?: number;
|
parsed_attachment?: number;
|
||||||
};
|
};
|
||||||
export type CreateExpenseResult = { ok: true; data: GetExpenseDto } | { ok: false; error: any };
|
|
||||||
export type DeleteExpenseResult = { ok: true; id: number; } | { ok: false; id: number; error: any };
|
|
||||||
|
|
||||||
|
|
||||||
export type ShiftResponse = {
|
export type ShiftResponse = {
|
||||||
week_day: string;
|
week_day: string;
|
||||||
|
|
@ -76,33 +35,3 @@ export type ApplyResult = {
|
||||||
created: number;
|
created: number;
|
||||||
skipped: number;
|
skipped: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>;
|
|
||||||
|
|
||||||
export type Tx = Prisma.TransactionClient | PrismaClient;
|
|
||||||
|
|
||||||
export type WeekOvertimeSummary = {
|
|
||||||
week_start:string;
|
|
||||||
week_end: string;
|
|
||||||
week_total_hours: number;
|
|
||||||
weekly_overtime: number;
|
|
||||||
daily_overtime_kept: number;
|
|
||||||
total_overtime: number;
|
|
||||||
breakdown: Array<{
|
|
||||||
date:string;
|
|
||||||
day_hours: number;
|
|
||||||
day_overtime: number;
|
|
||||||
daily_kept: number;
|
|
||||||
running_total_before: number;
|
|
||||||
}>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface ShiftKey {
|
|
||||||
timesheet_id: number;
|
|
||||||
date: Date;
|
|
||||||
start_time: Date;
|
|
||||||
end_time: Date;
|
|
||||||
bank_code_id: number;
|
|
||||||
is_remote: boolean;
|
|
||||||
comment?: string | null;
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user