refactor(helpers): moved helpers to a shared file
This commit is contained in:
parent
78a335a47c
commit
71d86f7fed
|
|
@ -437,7 +437,7 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/shifts/upsert/{email}/{date}": {
|
"/shifts/upsert/{email}": {
|
||||||
"put": {
|
"put": {
|
||||||
"operationId": "ShiftsController_upsert_by_date",
|
"operationId": "ShiftsController_upsert_by_date",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|
@ -448,14 +448,6 @@
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "date",
|
|
||||||
"required": true,
|
|
||||||
"in": "path",
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ import { HolidayService } from 'src/modules/business-logics/services/holid
|
||||||
import { PrismaService } from 'src/prisma/prisma.service';
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
import { mapRowToView } from '../mappers/leave-requests.mapper';
|
import { mapRowToView } from '../mappers/leave-requests.mapper';
|
||||||
import { leaveRequestsSelect } from '../utils/leave-requests.select';
|
import { leaveRequestsSelect } from '../utils/leave-requests.select';
|
||||||
import { LeaveRequestsUtils, normalizeDates, toDateOnly } from '../utils/leave-request.util';
|
import { LeaveRequestsUtils} from '../utils/leave-request.util';
|
||||||
|
import { normalizeDates, toDateOnly } from 'src/modules/shared/helpers/date-time.helpers';
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ import { HolidayService } from "src/modules/business-logics/services/holiday.ser
|
||||||
import { SickLeaveService } from "src/modules/business-logics/services/sick-leave.service";
|
import { SickLeaveService } from "src/modules/business-logics/services/sick-leave.service";
|
||||||
import { VacationService } from "src/modules/business-logics/services/vacation.service";
|
import { VacationService } from "src/modules/business-logics/services/vacation.service";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { LeaveRequestsUtils, normalizeDates, toDateOnly, toISODateKey } from "../utils/leave-request.util";
|
import { LeaveRequestsUtils } from "../utils/leave-request.util";
|
||||||
|
import { normalizeDates, toDateOnly, toISODateKey } from "src/modules/shared/helpers/date-time.helpers";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LeaveRequestsService {
|
export class LeaveRequestsService {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ import { mapRowToView } from "../mappers/leave-requests.mapper";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { SickLeaveService } from "src/modules/business-logics/services/sick-leave.service";
|
import { SickLeaveService } from "src/modules/business-logics/services/sick-leave.service";
|
||||||
import { roundToQuarterHour } from "src/common/utils/date-utils";
|
import { roundToQuarterHour } from "src/common/utils/date-utils";
|
||||||
import { LeaveRequestsUtils, normalizeDates, toDateOnly } from "../utils/leave-request.util";
|
import { LeaveRequestsUtils } from "../utils/leave-request.util";
|
||||||
|
import { normalizeDates, toDateOnly } from "src/modules/shared/helpers/date-time.helpers";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SickLeaveRequestsService {
|
export class SickLeaveRequestsService {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { mapRowToView } from "../mappers/leave-requests.mapper";
|
import { mapRowToView } from "../mappers/leave-requests.mapper";
|
||||||
import { leaveRequestsSelect } from "../utils/leave-requests.select";
|
import { leaveRequestsSelect } from "../utils/leave-requests.select";
|
||||||
import { roundToQuarterHour } from "src/common/utils/date-utils";
|
import { roundToQuarterHour } from "src/common/utils/date-utils";
|
||||||
import { LeaveRequestsUtils, normalizeDates, toDateOnly } from "../utils/leave-request.util";
|
import { LeaveRequestsUtils } from "../utils/leave-request.util";
|
||||||
|
import { normalizeDates, toDateOnly } from "src/modules/shared/helpers/date-time.helpers";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class VacationLeaveRequestsService {
|
export class VacationLeaveRequestsService {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||||
import { LeaveTypes } from "@prisma/client";
|
import { LeaveTypes } from "@prisma/client";
|
||||||
|
import { hhmmFromLocal, toDateOnly, toStringFromDate } from "src/modules/shared/helpers/date-time.helpers";
|
||||||
import { ShiftsCommandService } from "src/modules/shifts/services/shifts-command.service";
|
import { ShiftsCommandService } from "src/modules/shifts/services/shifts-command.service";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
|
||||||
|
|
@ -33,7 +34,7 @@ export class LeaveRequestsUtils {
|
||||||
async syncShift(
|
async syncShift(
|
||||||
email: string,
|
email: string,
|
||||||
employee_id: number,
|
employee_id: number,
|
||||||
iso_date: string,
|
date: string,
|
||||||
hours: number,
|
hours: number,
|
||||||
type: LeaveTypes,
|
type: LeaveTypes,
|
||||||
comment?: string,
|
comment?: string,
|
||||||
|
|
@ -44,6 +45,10 @@ export class LeaveRequestsUtils {
|
||||||
if (duration_minutes > 8 * 60) {
|
if (duration_minutes > 8 * 60) {
|
||||||
throw new BadRequestException("Amount of hours cannot exceed 8 hours per day.");
|
throw new BadRequestException("Amount of hours cannot exceed 8 hours per day.");
|
||||||
}
|
}
|
||||||
|
const date_only = toDateOnly(date);
|
||||||
|
const yyyy_mm_dd = toStringFromDate(date_only);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const start_minutes = 8 * 60;
|
const start_minutes = 8 * 60;
|
||||||
const end_minutes = start_minutes + duration_minutes;
|
const end_minutes = start_minutes + duration_minutes;
|
||||||
|
|
@ -52,16 +57,17 @@ export class LeaveRequestsUtils {
|
||||||
|
|
||||||
const existing = await this.prisma.shifts.findFirst({
|
const existing = await this.prisma.shifts.findFirst({
|
||||||
where: {
|
where: {
|
||||||
date: new Date(iso_date),
|
date: date_only,
|
||||||
bank_code: { type },
|
bank_code: { type },
|
||||||
timesheet: { employee_id: employee_id },
|
timesheet: { employee_id: employee_id },
|
||||||
},
|
},
|
||||||
include: { bank_code: true },
|
include: { bank_code: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.shiftsCommand.upsertShiftsByDate(email, iso_date, {
|
await this.shiftsCommand.upsertShiftsByDate(email, {
|
||||||
old_shift: existing
|
old_shift: existing
|
||||||
? {
|
? {
|
||||||
|
date: yyyy_mm_dd,
|
||||||
start_time: existing.start_time.toISOString().slice(11, 16),
|
start_time: existing.start_time.toISOString().slice(11, 16),
|
||||||
end_time: existing.end_time.toISOString().slice(11, 16),
|
end_time: existing.end_time.toISOString().slice(11, 16),
|
||||||
type: existing.bank_code?.type ?? type,
|
type: existing.bank_code?.type ?? type,
|
||||||
|
|
@ -71,6 +77,7 @@ export class LeaveRequestsUtils {
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
new_shift: {
|
new_shift: {
|
||||||
|
date: yyyy_mm_dd,
|
||||||
start_time: toHHmm(start_minutes),
|
start_time: toHHmm(start_minutes),
|
||||||
end_time: toHHmm(end_minutes),
|
end_time: toHHmm(end_minutes),
|
||||||
is_remote: existing?.is_remote ?? false,
|
is_remote: existing?.is_remote ?? false,
|
||||||
|
|
@ -87,9 +94,11 @@ export class LeaveRequestsUtils {
|
||||||
iso_date: string,
|
iso_date: string,
|
||||||
type: LeaveTypes,
|
type: LeaveTypes,
|
||||||
) {
|
) {
|
||||||
|
const date_only = toDateOnly(iso_date);
|
||||||
|
const yyyy_mm_dd = toStringFromDate(date_only);
|
||||||
const existing = await this.prisma.shifts.findFirst({
|
const existing = await this.prisma.shifts.findFirst({
|
||||||
where: {
|
where: {
|
||||||
date: new Date(iso_date),
|
date: date_only,
|
||||||
bank_code: { type },
|
bank_code: { type },
|
||||||
timesheet: { employee_id: employee_id },
|
timesheet: { employee_id: employee_id },
|
||||||
},
|
},
|
||||||
|
|
@ -97,10 +106,11 @@ export class LeaveRequestsUtils {
|
||||||
});
|
});
|
||||||
if (!existing) return;
|
if (!existing) return;
|
||||||
|
|
||||||
await this.shiftsCommand.upsertShiftsByDate(email, iso_date, {
|
await this.shiftsCommand.upsertShiftsByDate(email, {
|
||||||
old_shift: {
|
old_shift: {
|
||||||
start_time: existing.start_time.toISOString().slice(11, 16),
|
date: yyyy_mm_dd,
|
||||||
end_time: existing.end_time.toISOString().slice(11, 16),
|
start_time: hhmmFromLocal(existing.start_time),
|
||||||
|
end_time: hhmmFromLocal(existing.end_time),
|
||||||
type: existing.bank_code?.type ?? type,
|
type: existing.bank_code?.type ?? type,
|
||||||
is_remote: existing.is_remote,
|
is_remote: existing.is_remote,
|
||||||
is_approved:existing.is_approved,
|
is_approved:existing.is_approved,
|
||||||
|
|
@ -110,18 +120,3 @@ export class LeaveRequestsUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const toDateOnly = (iso: string): Date => {
|
|
||||||
const date = new Date(iso);
|
|
||||||
if (Number.isNaN(date.getTime())) {
|
|
||||||
throw new BadRequestException(`Invalid date: ${iso}`);
|
|
||||||
}
|
|
||||||
date.setHours(0, 0, 0, 0);
|
|
||||||
return date;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const toISODateKey = (date: Date): string => date.toISOString().slice(0, 10);
|
|
||||||
|
|
||||||
export const normalizeDates = (dates: string[]): string[] =>
|
|
||||||
Array.from(new Set(dates.map((iso) => toISODateKey(toDateOnly(iso)))));
|
|
||||||
34
src/modules/shared/helpers/date-time.helpers.ts
Normal file
34
src/modules/shared/helpers/date-time.helpers.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { BadRequestException } from "@nestjs/common";
|
||||||
|
|
||||||
|
export const hhmmFromLocal = (d: Date) =>
|
||||||
|
`${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`;
|
||||||
|
|
||||||
|
export const toDateOnly = (s: string): Date => {
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}$/.test(s)) {
|
||||||
|
const y = Number(s.slice(0,4));
|
||||||
|
const m = Number(s.slice(5,7)) - 1;
|
||||||
|
const d = Number(s.slice(8,10));
|
||||||
|
return new Date(y, m, d, 0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
const dt = new Date(s);
|
||||||
|
if (Number.isNaN(dt.getTime())) throw new BadRequestException(`Invalid date: ${s}`);
|
||||||
|
return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate(), 0,0,0,0);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const toStringFromDate = (d: Date) =>
|
||||||
|
`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
|
||||||
|
|
||||||
|
|
||||||
|
export const toISOtoDateOnly = (iso: string): Date => {
|
||||||
|
const date = new Date(iso);
|
||||||
|
if (Number.isNaN(date.getTime())) {
|
||||||
|
throw new BadRequestException(`Invalid date: ${iso}`);
|
||||||
|
}
|
||||||
|
date.setHours(0, 0, 0, 0);
|
||||||
|
return date;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const toISODateKey = (date: Date): string => date.toISOString().slice(0, 10);
|
||||||
|
|
||||||
|
export const normalizeDates = (dates: string[]): string[] =>
|
||||||
|
Array.from(new Set(dates.map((iso) => toISODateKey(toISOtoDateOnly(iso)))));
|
||||||
|
|
@ -18,13 +18,12 @@ export class ShiftsController {
|
||||||
private readonly shiftsCommandService: ShiftsCommandService,
|
private readonly shiftsCommandService: ShiftsCommandService,
|
||||||
){}
|
){}
|
||||||
|
|
||||||
@Put('upsert/:email/:date')
|
@Put('upsert/:email')
|
||||||
async upsert_by_date(
|
async upsert_by_date(
|
||||||
@Param('email') email_param: string,
|
@Param('email') email_param: string,
|
||||||
@Param('date') date_param: string,
|
|
||||||
@Body() payload: UpsertShiftDto,
|
@Body() payload: UpsertShiftDto,
|
||||||
) {
|
) {
|
||||||
return this.shiftsCommandService.upsertShiftsByDate(email_param, date_param, payload);
|
return this.shiftsCommandService.upsertShiftsByDate(email_param, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch('approval/:id')
|
@Patch('approval/:id')
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ export const COMMENT_MAX_LENGTH = 280;
|
||||||
|
|
||||||
export class ShiftPayloadDto {
|
export class ShiftPayloadDto {
|
||||||
|
|
||||||
|
@Matches(/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/)
|
||||||
|
date!: string;
|
||||||
|
|
||||||
@Matches(/^([01]\d|2[0-3]):([0-5]\d)$/)
|
@Matches(/^([01]\d|2[0-3]):([0-5]\d)$/)
|
||||||
start_time!: string;
|
start_time!: string;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,25 @@
|
||||||
export function timeFromHHMM(hhmm: string): Date {
|
export function timeFromHHMM(hhmm: string): Date {
|
||||||
const [hour, min] = hhmm.split(':').map(Number);
|
const [h, m] = hhmm.split(':').map(Number);
|
||||||
return new Date(1970, 0, 1, hour, min, 0, 0);
|
return new Date(1970, 0, 1, h, m, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function weekStartSunday(d: Date): Date {
|
export function toDateOnly(ymd: string): Date {
|
||||||
const start = new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
const y = Number(ymd.slice(0, 4));
|
||||||
const day = start.getDay(); // 0 = dimanche
|
const m = Number(ymd.slice(5, 7)) - 1;
|
||||||
start.setDate(start.getDate() - day);
|
const d = Number(ymd.slice(8, 10));
|
||||||
|
return new Date(y, m, d, 0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function weekStartSunday(dateLocal: Date): Date {
|
||||||
|
const start = new Date(dateLocal.getFullYear(), dateLocal.getMonth(), dateLocal.getDate());
|
||||||
|
const dow = start.getDay(); // 0 = dimanche
|
||||||
|
start.setDate(start.getDate() - dow);
|
||||||
start.setHours(0, 0, 0, 0);
|
start.setHours(0, 0, 0, 0);
|
||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toDateOnly(input: string | Date): Date {
|
export function formatHHmm(t: Date): string {
|
||||||
const base = (typeof input === 'string') ? new Date(input) : new Date(input);
|
const hh = String(t.getHours()).padStart(2, '0');
|
||||||
const y = (typeof input === 'string') ? Number(input.slice(0,4)) : base.getFullYear();
|
const mm = String(t.getMinutes()).padStart(2, '0');
|
||||||
const m = (typeof input === 'string') ? Number(input.slice(5,7)) - 1 : base.getMonth();
|
|
||||||
const d = (typeof input === 'string') ? Number(input.slice(8,10)) : base.getDate();
|
|
||||||
return new Date(y, m, d, 0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatHHmm(time: Date): string {
|
|
||||||
const hh = String(time.getHours()).padStart(2,'0');
|
|
||||||
const mm = String(time.getMinutes()).padStart(2,'0');
|
|
||||||
return `${hh}:${mm}`;
|
return `${hh}:${mm}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ export class ShiftsCommandService extends BaseApprovalService<Shifts> {
|
||||||
//_____________________________________________________________________________________________
|
//_____________________________________________________________________________________________
|
||||||
// MASTER CRUD METHOD
|
// MASTER CRUD METHOD
|
||||||
//_____________________________________________________________________________________________
|
//_____________________________________________________________________________________________
|
||||||
async upsertShiftsByDate(email:string, date_string: string, dto: UpsertShiftDto):
|
async upsertShiftsByDate(email:string, dto: UpsertShiftDto):
|
||||||
Promise<{ action: UpsertAction; day: DayShiftResponse[] }> {
|
Promise<{ action: UpsertAction; day: DayShiftResponse[] }> {
|
||||||
const { old_shift, new_shift } = dto;
|
const { old_shift, new_shift } = dto;
|
||||||
|
|
||||||
|
|
@ -49,7 +49,14 @@ export class ShiftsCommandService extends BaseApprovalService<Shifts> {
|
||||||
throw new BadRequestException('At least one of old or new shift must be provided');
|
throw new BadRequestException('At least one of old or new shift must be provided');
|
||||||
}
|
}
|
||||||
|
|
||||||
const date_only = toDateOnly(date_string);
|
const date = new_shift?.date ?? old_shift?.date;
|
||||||
|
if (!date) throw new BadRequestException("A date (YYYY-MM-DD) must be provided in old_shift or new_shift");
|
||||||
|
if (old_shift?.date
|
||||||
|
&& new_shift?.date
|
||||||
|
&& old_shift.date
|
||||||
|
!== new_shift.date) throw new BadRequestException("old_shift.date and new_shift.date must be identical");
|
||||||
|
|
||||||
|
const date_only = toDateOnly(date);
|
||||||
const employee_id = await this.emailResolver.findIdByEmail(email);
|
const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||||
|
|
||||||
return this.prisma.$transaction(async (tx) => {
|
return this.prisma.$transaction(async (tx) => {
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ export function resolveBankCodeByType(type: string): Promise<number> {
|
||||||
|
|
||||||
export function normalizeShiftPayload(payload: ShiftPayloadDto) {
|
export function normalizeShiftPayload(payload: ShiftPayloadDto) {
|
||||||
//normalize shift's infos
|
//normalize shift's infos
|
||||||
|
const date = payload.date;
|
||||||
const start_time = timeFromHHMM(payload.start_time);
|
const start_time = timeFromHHMM(payload.start_time);
|
||||||
const end_time = timeFromHHMM(payload.end_time );
|
const end_time = timeFromHHMM(payload.end_time );
|
||||||
const type = (payload.type || '').trim().toUpperCase();
|
const type = (payload.type || '').trim().toUpperCase();
|
||||||
|
|
@ -34,5 +35,5 @@ export function resolveBankCodeByType(type: string): Promise<number> {
|
||||||
const trimmed = typeof raw_comment === 'string' ? raw_comment.trim() : null;
|
const trimmed = typeof raw_comment === 'string' ? raw_comment.trim() : null;
|
||||||
const comment = trimmed && trimmed.length > 0 ? trimmed: null;
|
const comment = trimmed && trimmed.length > 0 ? trimmed: null;
|
||||||
|
|
||||||
return { start_time, end_time, type, is_remote, is_approved, comment };
|
return { date, start_time, end_time, type, is_remote, is_approved, comment };
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user