clean(folder): cleaning imports
This commit is contained in:
parent
c59b50a829
commit
f1f765b350
|
|
@ -91,30 +91,20 @@
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"name": "date",
|
"name": "date",
|
||||||
"required": false,
|
"required": true,
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"description": "Override for resolving the current period",
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"example": "2025-08-11",
|
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Find current and all pay periods",
|
"description": ""
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/PayPeriodBundleDto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"summary": "Return current pay period and the full list",
|
|
||||||
"tags": [
|
"tags": [
|
||||||
"pay-periods"
|
"PayPeriods"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -133,22 +123,11 @@
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Pay period found for the selected date",
|
"description": ""
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/PayPeriodDto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"404": {
|
|
||||||
"description": "Pay period not found for the selected date"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"summary": "Resolve a period by a date within it",
|
|
||||||
"tags": [
|
"tags": [
|
||||||
"pay-periods"
|
"PayPeriods"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -161,7 +140,6 @@
|
||||||
"required": true,
|
"required": true,
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"schema": {
|
"schema": {
|
||||||
"example": 2024,
|
|
||||||
"type": "number"
|
"type": "number"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -169,31 +147,18 @@
|
||||||
"name": "periodNumber",
|
"name": "periodNumber",
|
||||||
"required": true,
|
"required": true,
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"description": "1..26",
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"example": 1,
|
|
||||||
"type": "number"
|
"type": "number"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Pay period found",
|
"description": ""
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/PayPeriodDto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"404": {
|
|
||||||
"description": "Pay period not found"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"summary": "Find pay period by year and period number",
|
|
||||||
"tags": [
|
"tags": [
|
||||||
"pay-periods"
|
"PayPeriods"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -206,7 +171,6 @@
|
||||||
"required": true,
|
"required": true,
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"schema": {
|
"schema": {
|
||||||
"example": 2024,
|
|
||||||
"type": "number"
|
"type": "number"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -214,49 +178,18 @@
|
||||||
"name": "periodNumber",
|
"name": "periodNumber",
|
||||||
"required": true,
|
"required": true,
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"description": "1..26",
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"example": 1,
|
|
||||||
"type": "number"
|
"type": "number"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "email",
|
|
||||||
"required": true,
|
|
||||||
"in": "path",
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "includeSubtree",
|
|
||||||
"required": false,
|
|
||||||
"in": "query",
|
|
||||||
"description": "Include indirect reports",
|
|
||||||
"schema": {
|
|
||||||
"example": false,
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Crew overview",
|
"description": ""
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/PayPeriodOverviewDto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"404": {
|
|
||||||
"description": "Pay period not found"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"summary": "Supervisor crew overview for a given pay period",
|
|
||||||
"tags": [
|
"tags": [
|
||||||
"pay-periods"
|
"PayPeriods"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -269,7 +202,6 @@
|
||||||
"required": true,
|
"required": true,
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"schema": {
|
"schema": {
|
||||||
"example": 2024,
|
|
||||||
"type": "number"
|
"type": "number"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -277,31 +209,18 @@
|
||||||
"name": "periodNumber",
|
"name": "periodNumber",
|
||||||
"required": true,
|
"required": true,
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"description": "1..26",
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"example": 1,
|
|
||||||
"type": "number"
|
"type": "number"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Pay period overview found",
|
"description": ""
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/PayPeriodOverviewDto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"404": {
|
|
||||||
"description": "Pay period not found"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"summary": "Detailed view of a pay period by year + number",
|
|
||||||
"tags": [
|
"tags": [
|
||||||
"pay-periods"
|
"PayPeriods"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -690,168 +609,6 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schemas": {
|
"schemas": {
|
||||||
"PayPeriodDto": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"pay_period_no": {
|
|
||||||
"type": "number",
|
|
||||||
"example": 1,
|
|
||||||
"description": "numéro cyclique de la période entre 1 et 26"
|
|
||||||
},
|
|
||||||
"period_start": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "2023-12-17",
|
|
||||||
"format": "date"
|
|
||||||
},
|
|
||||||
"period_end": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "2023-12-30",
|
|
||||||
"format": "date"
|
|
||||||
},
|
|
||||||
"payday": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "2023-01-04",
|
|
||||||
"format": "date"
|
|
||||||
},
|
|
||||||
"pay_year": {
|
|
||||||
"type": "number",
|
|
||||||
"example": 2023
|
|
||||||
},
|
|
||||||
"label": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "2023-12-17 → 2023-12-30"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"pay_period_no",
|
|
||||||
"period_start",
|
|
||||||
"period_end",
|
|
||||||
"payday",
|
|
||||||
"pay_year",
|
|
||||||
"label"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"PayPeriodBundleDto": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"current": {
|
|
||||||
"description": "Current pay period (resolved from date)",
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/PayPeriodDto"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"periods": {
|
|
||||||
"description": "All pay periods",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/components/schemas/PayPeriodDto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"current",
|
|
||||||
"periods"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"EmployeePeriodOverviewDto": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"employee_name": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "Alex Dupont",
|
|
||||||
"description": "Nom complet de lemployé"
|
|
||||||
},
|
|
||||||
"regular_hours": {
|
|
||||||
"type": "number",
|
|
||||||
"example": 40,
|
|
||||||
"description": "pay-period`s regular hours"
|
|
||||||
},
|
|
||||||
"other_hours": {
|
|
||||||
"type": "object",
|
|
||||||
"example": 0,
|
|
||||||
"description": "pay-period`s other hours"
|
|
||||||
},
|
|
||||||
"expenses": {
|
|
||||||
"type": "number",
|
|
||||||
"example": 420.69,
|
|
||||||
"description": "pay-period`s total expenses ($)"
|
|
||||||
},
|
|
||||||
"mileage": {
|
|
||||||
"type": "number",
|
|
||||||
"example": 40,
|
|
||||||
"description": "pay-period total mileages (km)"
|
|
||||||
},
|
|
||||||
"is_approved": {
|
|
||||||
"type": "boolean",
|
|
||||||
"example": true,
|
|
||||||
"description": "Tous les timesheets de la période sont approuvés pour cet employé"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"employee_name",
|
|
||||||
"regular_hours",
|
|
||||||
"other_hours",
|
|
||||||
"expenses",
|
|
||||||
"mileage",
|
|
||||||
"is_approved"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"PayPeriodOverviewDto": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"pay_period_no": {
|
|
||||||
"type": "number",
|
|
||||||
"example": 1,
|
|
||||||
"description": "Period number (1–26)"
|
|
||||||
},
|
|
||||||
"pay_year": {
|
|
||||||
"type": "number",
|
|
||||||
"example": 2023,
|
|
||||||
"description": "Calendar year of the period"
|
|
||||||
},
|
|
||||||
"period_start": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "2023-12-17",
|
|
||||||
"format": "date",
|
|
||||||
"description": "Period start date (YYYY-MM-DD)"
|
|
||||||
},
|
|
||||||
"period_end": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "2023-12-30",
|
|
||||||
"format": "date",
|
|
||||||
"description": "Period end date (YYYY-MM-DD)"
|
|
||||||
},
|
|
||||||
"payday": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "2023-12-30",
|
|
||||||
"format": "date",
|
|
||||||
"description": "Period pay day(YYYY-MM-DD)"
|
|
||||||
},
|
|
||||||
"label": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "2023-12-17 → 2023-12-30",
|
|
||||||
"description": "Human-readable label"
|
|
||||||
},
|
|
||||||
"employees_overview": {
|
|
||||||
"description": "Per-employee overview for the period",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/components/schemas/EmployeePeriodOverviewDto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"pay_period_no",
|
|
||||||
"pay_year",
|
|
||||||
"period_start",
|
|
||||||
"period_end",
|
|
||||||
"payday",
|
|
||||||
"label",
|
|
||||||
"employees_overview"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"PreferencesDto": {
|
"PreferencesDto": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {}
|
"properties": {}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { CsvExportController } from "./controllers/csv-exports.controller";
|
import { CsvExportController } from "./controllers/csv-exports.controller";
|
||||||
import { CsvExportService } from "./services/csv-exports.service";
|
import { CsvExportService } from "./services/csv-exports.service";
|
||||||
import { SharedModule } from "src/time-and-attendance/shared/shared.module";
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
providers:[CsvExportService, SharedModule],
|
providers:[CsvExportService],
|
||||||
controllers: [CsvExportController],
|
controllers: [CsvExportController],
|
||||||
})
|
})
|
||||||
export class CsvExportModule {}
|
export class CsvExportModule {}
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,18 @@ import { VacationService } from "./services/vacation.service";
|
||||||
import { HolidayService } from "./services/holiday.service";
|
import { HolidayService } from "./services/holiday.service";
|
||||||
import { MileageService } from "./services/mileage.service";
|
import { MileageService } from "./services/mileage.service";
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
|
imports:[],
|
||||||
providers: [
|
providers: [
|
||||||
HolidayService,
|
HolidayService,
|
||||||
MileageService,
|
MileageService,
|
||||||
OvertimeService,
|
OvertimeService,
|
||||||
SickLeaveService,
|
SickLeaveService,
|
||||||
VacationService
|
VacationService,
|
||||||
|
EmailToIdResolver,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
HolidayService,
|
HolidayService,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import { Injectable, Logger, NotFoundException } from "@nestjs/common";
|
import { Injectable, Logger, NotFoundException } from "@nestjs/common";
|
||||||
import { computeHours, getWeekStart } from "src/common/utils/date-utils";
|
import { computeHours, getWeekStart } from "src/common/utils/date-utils";
|
||||||
import { PrismaService } from "../../../prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
const WEEK_IN_MS = 7 * 24 * 60 * 60 * 1000;
|
import { MS_PER_WEEK } from "src/time-and-attendance/utils/constants.utils";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
le calcul est 1/20 des 4 dernières semaines, précédent la semaine incluant le férier.
|
le calcul est 1/20 des 4 dernières semaines, précédent la semaine incluant le férier.
|
||||||
Un maximum de 08h00 est allouable pour le férier
|
Un maximum de 08h00 est allouable pour le férier
|
||||||
|
|
@ -15,28 +14,19 @@ const WEEK_IN_MS = 7 * 24 * 60 * 60 * 1000;
|
||||||
export class HolidayService {
|
export class HolidayService {
|
||||||
private readonly logger = new Logger(HolidayService.name);
|
private readonly logger = new Logger(HolidayService.name);
|
||||||
|
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
//fetch employee_id by email
|
private readonly emailResolver: EmailToIdResolver,
|
||||||
private async resolveEmployeeByEmail(email: string): Promise<number> {
|
) {}
|
||||||
const employee = await this.prisma.employees.findFirst({
|
|
||||||
where: {
|
|
||||||
user: { email }
|
|
||||||
},
|
|
||||||
select: { id: true },
|
|
||||||
});
|
|
||||||
if(!employee) throw new NotFoundException(`Employee with email : ${email} not found`);
|
|
||||||
return employee.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async computeHoursPrevious4WeeksByEmail(email: string, holiday_date: Date): Promise<number> {
|
private async computeHoursPrevious4WeeksByEmail(email: string, holiday_date: Date): Promise<number> {
|
||||||
const employee_id = await this.resolveEmployeeByEmail(email);
|
const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||||
return this.computeHoursPrevious4Weeks(employee_id, holiday_date);
|
return this.computeHoursPrevious4Weeks(employee_id, holiday_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async computeHoursPrevious4Weeks(employee_id: number, holiday_date: Date): Promise<number> {
|
private async computeHoursPrevious4Weeks(employee_id: number, holiday_date: Date): Promise<number> {
|
||||||
const holiday_week_start = getWeekStart(holiday_date);
|
const holiday_week_start = getWeekStart(holiday_date);
|
||||||
const window_start = new Date(holiday_week_start.getTime() - 4 * WEEK_IN_MS);
|
const window_start = new Date(holiday_week_start.getTime() - 4 * MS_PER_WEEK);
|
||||||
const window_end = new Date(holiday_week_start.getTime() - 1);
|
const window_end = new Date(holiday_week_start.getTime() - 1);
|
||||||
|
|
||||||
const valid_codes = ['G1', 'G43', 'G56', 'G104', 'G105', 'G700'];
|
const valid_codes = ['G1', 'G43', 'G56', 'G104', 'G105', 'G700'];
|
||||||
|
|
@ -60,7 +50,7 @@ export class HolidayService {
|
||||||
|
|
||||||
let capped_total = 0;
|
let capped_total = 0;
|
||||||
for(let offset = 1; offset <= 4; offset++) {
|
for(let offset = 1; offset <= 4; offset++) {
|
||||||
const week_start = new Date(holiday_week_start.getTime() - offset * WEEK_IN_MS);
|
const week_start = new Date(holiday_week_start.getTime() - offset * MS_PER_WEEK);
|
||||||
const key = week_start.getTime();
|
const key = week_start.getTime();
|
||||||
const weekly_hours = hours_by_week.get(key) ?? 0;
|
const weekly_hours = hours_by_week.get(key) ?? 0;
|
||||||
capped_total += Math.min(weekly_hours, 40);
|
capped_total += Math.min(weekly_hours, 40);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
|
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
|
||||||
import { PrismaService } from '../../../prisma/prisma.service';
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MileageService {
|
export class MileageService {
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,15 @@
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { PrismaService } from '../../../prisma/prisma.service';
|
|
||||||
import { getWeekStart, getWeekEnd, computeHours } from 'src/common/utils/date-utils';
|
import { getWeekStart, getWeekEnd, computeHours } from 'src/common/utils/date-utils';
|
||||||
import { Prisma, PrismaClient } from '@prisma/client';
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
|
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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}>;
|
|
||||||
};
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class OvertimeService {
|
export class OvertimeService {
|
||||||
|
|
||||||
private logger = new Logger(OvertimeService.name);
|
private logger = new Logger(OvertimeService.name);
|
||||||
private daily_max = 8; // maximum for regular hours per day
|
|
||||||
private weekly_max = 40; // maximum for regular hours per week
|
|
||||||
private INCLUDED_TYPES = ['EMERGENCY', 'EVENING','OVERTIME','REGULAR'] as const; // included types for weekly overtime calculation
|
private INCLUDED_TYPES = ['EMERGENCY', 'EVENING','OVERTIME','REGULAR'] as const; // included types for weekly overtime calculation
|
||||||
|
|
||||||
constructor(private prisma: PrismaService) {}
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
@ -61,7 +44,7 @@ export class OvertimeService {
|
||||||
}
|
}
|
||||||
|
|
||||||
const week_total_hours = [ ...day_totals.values()].reduce((a,b) => a + b, 0);
|
const week_total_hours = [ ...day_totals.values()].reduce((a,b) => a + b, 0);
|
||||||
const weekly_overtime = Math.max(0, week_total_hours - this.weekly_max);
|
const weekly_overtime = Math.max(0, week_total_hours - WEEKLY_LIMIT_HOURS);
|
||||||
|
|
||||||
let running = 0;
|
let running = 0;
|
||||||
let daily_kept_sum = 0;
|
let daily_kept_sum = 0;
|
||||||
|
|
@ -69,9 +52,9 @@ export class OvertimeService {
|
||||||
|
|
||||||
for (const key of days) {
|
for (const key of days) {
|
||||||
const day_hours = day_totals.get(key) ?? 0;
|
const day_hours = day_totals.get(key) ?? 0;
|
||||||
const day_overtime = Math.max(0, day_hours - this.daily_max);
|
const day_overtime = Math.max(0, day_hours - DAILY_LIMIT_HOURS);
|
||||||
|
|
||||||
const cap_before_40 = Math.max(0, this.weekly_max - running);
|
const cap_before_40 = Math.max(0, WEEKLY_LIMIT_HOURS - running);
|
||||||
const daily_kept = Math.min(day_overtime, cap_before_40);
|
const daily_kept = Math.min(day_overtime, cap_before_40);
|
||||||
|
|
||||||
breakdown.push({
|
breakdown.push({
|
||||||
|
|
@ -104,144 +87,4 @@ export class OvertimeService {
|
||||||
breakdown,
|
breakdown,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// //calculate daily overtime
|
|
||||||
// async getDailyOvertimeHours(timesheet_id: number, date: Date): Promise<number> {
|
|
||||||
// const shifts = await this.prisma.shifts.findMany({
|
|
||||||
// where: {
|
|
||||||
// timesheet_id,
|
|
||||||
// date: date,
|
|
||||||
// bank_code: { type: { in: this.INCLUDED_TYPES as unknown as string[] } },
|
|
||||||
// },
|
|
||||||
// select: { start_time: true, end_time: true },
|
|
||||||
// orderBy: [{ start_time: 'asc' }],
|
|
||||||
// });
|
|
||||||
|
|
||||||
// const total = shifts.map((shift)=>
|
|
||||||
// computeHours(shift.start_time, shift.end_time, 5)).
|
|
||||||
// reduce((sum, hours)=> sum + hours, 0);
|
|
||||||
|
|
||||||
// const overtime = Math.max(0, total - this.daily_max);
|
|
||||||
|
|
||||||
// this.logger.debug(`[OVERTIME]-[DAILY] total=${total.toFixed(2)}h, overtime= ${overtime.toFixed(2)}h`);
|
|
||||||
// return overtime;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //calculate Weekly overtime
|
|
||||||
// async getWeeklyOvertimeHours(timesheet_id: number, ref_date: Date): Promise<number> {
|
|
||||||
// const week_start = getWeekStart(ref_date);
|
|
||||||
// const week_end = getWeekEnd(week_start);
|
|
||||||
|
|
||||||
// //fetches all shifts from INCLUDED_TYPES array
|
|
||||||
// const included_shifts = await this.prisma.shifts.findMany({
|
|
||||||
// where: {
|
|
||||||
// timesheet_id,
|
|
||||||
// date: { gte:week_start, lte: week_end },
|
|
||||||
// bank_code: { type: { in: this.INCLUDED_TYPES as unknown as string[] } },
|
|
||||||
// },
|
|
||||||
// select: { start_time: true, end_time: true },
|
|
||||||
// orderBy: [{date: 'asc'}, {start_time:'asc'}],
|
|
||||||
// });
|
|
||||||
|
|
||||||
// //calculate total hours of those shifts minus weekly Max to find total overtime hours
|
|
||||||
// const total = included_shifts.map(shift =>
|
|
||||||
// computeHours(shift.start_time, shift.end_time, 5)).
|
|
||||||
// reduce((sum, hours)=> sum+hours, 0);
|
|
||||||
|
|
||||||
// const overtime = Math.max(0, total - this.weekly_max);
|
|
||||||
|
|
||||||
// this.logger.debug(`[OVERTIME]-[WEEKLY] total=${total.toFixed(2)}h, overtime= ${overtime.toFixed(2)}h`);
|
|
||||||
// return overtime;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// //transform REGULAR shifts to OVERTIME when exceed 40hrs of included_types of shift
|
|
||||||
// async transformRegularHoursToWeeklyOvertime(
|
|
||||||
// employee_id: number,
|
|
||||||
// ref_date: Date,
|
|
||||||
// tx?: Prisma.TransactionClient,
|
|
||||||
// ): Promise<void> {
|
|
||||||
// //ensures the use of the transaction if needed. fallback to this.prisma if no transaction is detected.
|
|
||||||
// const db = tx ?? this.prisma;
|
|
||||||
|
|
||||||
// //calculate weekly overtime
|
|
||||||
// const overtime_hours = await this.getWeeklyOvertimeHours(employee_id, ref_date);
|
|
||||||
// if(overtime_hours <= 0) return;
|
|
||||||
|
|
||||||
// const convert_to_minutes = Math.round(overtime_hours * 60);
|
|
||||||
|
|
||||||
// const [regular, overtime] = await Promise.all([
|
|
||||||
// db.bankCodes.findFirst({where: { type: 'REGULAR' }, select: { id: true } }),
|
|
||||||
// db.bankCodes.findFirst({where: { type: 'OVERTIME'}, select: { id: true } }),
|
|
||||||
// ]);
|
|
||||||
// if(!regular || !overtime) return;
|
|
||||||
|
|
||||||
// const week_start = getWeekStart(ref_date);
|
|
||||||
// const week_end = getWeekEnd(week_start);
|
|
||||||
|
|
||||||
// //gets all regular shifts and order them by desc
|
|
||||||
// const regular_shifts_desc = await db.shifts.findMany({
|
|
||||||
// where: {
|
|
||||||
// date: { gte:week_start, lte: week_end },
|
|
||||||
// timesheet: { employee_id },
|
|
||||||
// bank_code_id: regular.id,
|
|
||||||
// },
|
|
||||||
// select: {
|
|
||||||
// id: true,
|
|
||||||
// timesheet_id: true,
|
|
||||||
// date: true,
|
|
||||||
// start_time: true,
|
|
||||||
// end_time: true,
|
|
||||||
// is_remote: true,
|
|
||||||
// comment: true,
|
|
||||||
// },
|
|
||||||
// orderBy: [{date: 'desc'}, {start_time:'desc'}],
|
|
||||||
// });
|
|
||||||
|
|
||||||
// let remaining_minutes = convert_to_minutes;
|
|
||||||
|
|
||||||
// for(const shift of regular_shifts_desc) {
|
|
||||||
// if(remaining_minutes <= 0) break;
|
|
||||||
|
|
||||||
// const start = shift.start_time;
|
|
||||||
// const end = shift.end_time;
|
|
||||||
// const duration_in_minutes = Math.max(0, Math.round((end.getTime() - start.getTime())/60000));
|
|
||||||
// if(duration_in_minutes === 0) continue;
|
|
||||||
|
|
||||||
// if(duration_in_minutes <= remaining_minutes) {
|
|
||||||
// await db.shifts.update({
|
|
||||||
// where: { id: shift.id },
|
|
||||||
// data: { bank_code_id: overtime.id },
|
|
||||||
// });
|
|
||||||
// remaining_minutes -= duration_in_minutes;
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
// //sets the start_time of the new overtime shift
|
|
||||||
// const new_overtime_start = new Date(end.getTime() - remaining_minutes * 60000);
|
|
||||||
|
|
||||||
// //shorten the regular shift
|
|
||||||
// await db.shifts.update({
|
|
||||||
// where: { id: shift.id },
|
|
||||||
// data: { end_time: new_overtime_start },
|
|
||||||
// });
|
|
||||||
|
|
||||||
// //creates the new overtime shift to replace the shorten regular shift
|
|
||||||
// await db.shifts.create({
|
|
||||||
// data: {
|
|
||||||
// timesheet_id: shift.timesheet_id,
|
|
||||||
// date: shift.date,
|
|
||||||
// start_time: new_overtime_start,
|
|
||||||
// end_time: end,
|
|
||||||
// is_remote: shift.is_remote,
|
|
||||||
// comment: shift.comment,
|
|
||||||
// bank_code_id: overtime.id,
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// remaining_minutes = 0;
|
|
||||||
// }
|
|
||||||
// this.logger.debug(`[OVERTIME]-[WEEKLY]-[TRANSFORM] emp=${employee_id}
|
|
||||||
// week: ${week_start.toISOString().slice(0,10)}..${week_end.toISOString().slice(0,10)}
|
|
||||||
// converted= ${(convert_to_minutes-remaining_minutes)/60}h`);
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { getYearStart, roundToQuarterHour } from "src/common/utils/date-utils";
|
import { getYearStart, roundToQuarterHour } from "src/common/utils/date-utils";
|
||||||
import { Injectable, Logger } from "@nestjs/common";
|
import { Injectable, Logger } from "@nestjs/common";
|
||||||
import { PrismaService } from "../../../prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SickLeaveService {
|
export class SickLeaveService {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Injectable, Logger, NotFoundException } from "@nestjs/common";
|
import { Injectable, Logger, NotFoundException } from "@nestjs/common";
|
||||||
import { PrismaService } from "../../../prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class VacationService {
|
export class VacationService {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ 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 { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto";
|
import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto";
|
||||||
import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto";
|
import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto";
|
||||||
import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils";
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { LeaveRequestController } from "src/time-and-attendance/leave-requests/controllers/leave-requests.controller";
|
import { LeaveRequestController } from "src/time-and-attendance/leave-requests/controllers/leave-requests.controller";
|
||||||
import { LeaveRequestsService } from "src/time-and-attendance/leave-requests/services/leave-request.service";
|
import { LeaveRequestsService } from "src/time-and-attendance/leave-requests/services/leave-request.service";
|
||||||
import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module";
|
import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-logics.module";
|
||||||
import { SharedModule } from "src/time-and-attendance/shared/shared.module";
|
|
||||||
import { ShiftsModule } from "src/time-and-attendance/time-tracker/shifts/shifts.module";
|
import { ShiftsModule } from "src/time-and-attendance/time-tracker/shifts/shifts.module";
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
|
|
||||||
|
|
@ -9,7 +8,6 @@ import { Module } from "@nestjs/common";
|
||||||
imports: [
|
imports: [
|
||||||
BusinessLogicsModule,
|
BusinessLogicsModule,
|
||||||
ShiftsModule,
|
ShiftsModule,
|
||||||
SharedModule
|
|
||||||
],
|
],
|
||||||
controllers: [LeaveRequestController],
|
controllers: [LeaveRequestController],
|
||||||
providers: [LeaveRequestsService],
|
providers: [LeaveRequestsService],
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common";
|
import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common";
|
||||||
import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto";
|
import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto";
|
||||||
import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client";
|
import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client";
|
||||||
import { normalizeDates, toDateOnly } from "src/time-and-attendance/shared/helpers/date-time.helpers";
|
|
||||||
import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils";
|
import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils";
|
||||||
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 { LeaveRequestsUtils } from "src/time-and-attendance/leave-requests/utils/leave-request.util";
|
import { LeaveRequestsUtils } from "src/time-and-attendance/leave-requests/utils/leave-request.util";
|
||||||
import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils";
|
import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils";
|
||||||
import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils";
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service";
|
import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
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 { normalizeDates, toDateOnly } from "src/time-and-attendance/utils/date-time.utils";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||||
import { normalizeDates, toDateOnly, toISODateKey } from "src/time-and-attendance/shared/helpers/date-time.helpers";
|
|
||||||
import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto";
|
import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto";
|
||||||
import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client";
|
import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client";
|
||||||
import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils";
|
import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils";
|
||||||
import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto";
|
import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto";
|
||||||
import { roundToQuarterHour } from "src/common/utils/date-utils";
|
import { roundToQuarterHour } from "src/common/utils/date-utils";
|
||||||
import { LeaveRequestsUtils } from "src/time-and-attendance/leave-requests/utils/leave-request.util";
|
import { LeaveRequestsUtils } from "src/time-and-attendance/leave-requests/utils/leave-request.util";
|
||||||
import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils";
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils";
|
import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils";
|
||||||
import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service";
|
import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service";
|
||||||
import { VacationService } from "src/time-and-attendance/domains/services/vacation.service";
|
import { VacationService } from "src/time-and-attendance/domains/services/vacation.service";
|
||||||
import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service";
|
import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service";
|
||||||
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 { normalizeDates, toDateOnly, toISODateKey } from "src/time-and-attendance/utils/date-time.utils";
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LeaveRequestsService {
|
export class LeaveRequestsService {
|
||||||
constructor(
|
constructor(
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common";
|
import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common";
|
||||||
import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto";
|
import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto";
|
||||||
import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client";
|
import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client";
|
||||||
import { normalizeDates, toDateOnly } from "src/time-and-attendance/shared/helpers/date-time.helpers";
|
|
||||||
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 { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils";
|
import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils";
|
||||||
import { roundToQuarterHour } from "src/common/utils/date-utils";
|
import { roundToQuarterHour } from "src/common/utils/date-utils";
|
||||||
import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils";
|
import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils";
|
||||||
import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils";
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service";
|
import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
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 { normalizeDates, toDateOnly } from "src/time-and-attendance/utils/date-time.utils";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common";
|
import { Injectable, NotFoundException, BadRequestException } from "@nestjs/common";
|
||||||
import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto";
|
import { UpsertLeaveRequestDto, UpsertResult } from "src/time-and-attendance/leave-requests/dtos/upsert-leave-request.dto";
|
||||||
import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client";
|
import { LeaveTypes, LeaveApprovalStatus } from "@prisma/client";
|
||||||
import { normalizeDates, toDateOnly } from "src/time-and-attendance/shared/helpers/date-time.helpers";
|
|
||||||
import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils";
|
import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils";
|
||||||
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 { roundToQuarterHour } from "src/common/utils/date-utils";
|
import { roundToQuarterHour } from "src/common/utils/date-utils";
|
||||||
import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils";
|
import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils";
|
||||||
import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils";
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
import { VacationService } from "src/time-and-attendance/domains/services/vacation.service";
|
import { VacationService } from "src/time-and-attendance/domains/services/vacation.service";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
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 { normalizeDates, toDateOnly } from "src/time-and-attendance/utils/date-time.utils";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Query } from "@nestjs/common";
|
import { Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Query, Req } from "@nestjs/common";
|
||||||
import { ApiNotFoundResponse, ApiOperation, ApiParam, ApiQuery, ApiResponse, ApiTags } from "@nestjs/swagger";
|
|
||||||
import { PayPeriodDto } from "../dtos/pay-period.dto";
|
import { PayPeriodDto } from "../dtos/pay-period.dto";
|
||||||
import { PayPeriodOverviewDto } from "../dtos/overview-pay-period.dto";
|
import { PayPeriodOverviewDto } from "../dtos/overview-pay-period.dto";
|
||||||
import { PayPeriodsQueryService } from "../services/pay-periods-query.service";
|
import { PayPeriodsQueryService } from "../services/pay-periods-query.service";
|
||||||
|
|
@ -9,7 +8,7 @@ import { Roles as RoleEnum } from '.prisma/client';
|
||||||
import { PayPeriodBundleDto } from "../dtos/bundle-pay-period.dto";
|
import { PayPeriodBundleDto } from "../dtos/bundle-pay-period.dto";
|
||||||
import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto";
|
import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto";
|
||||||
|
|
||||||
@ApiTags('pay-periods')
|
|
||||||
@Controller('pay-periods')
|
@Controller('pay-periods')
|
||||||
export class PayPeriodsController {
|
export class PayPeriodsController {
|
||||||
|
|
||||||
|
|
@ -19,9 +18,6 @@ export class PayPeriodsController {
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Get('current-and-all')
|
@Get('current-and-all')
|
||||||
@ApiOperation({summary: 'Return current pay period and the full list'})
|
|
||||||
@ApiQuery({name: 'date', required:false, example: '2025-08-11', description:'Override for resolving the current period'})
|
|
||||||
@ApiResponse({status: 200, description:'Find current and all pay periods', type: PayPeriodBundleDto})
|
|
||||||
async getCurrentAndAll(@Query('date') date?: string): Promise<PayPeriodBundleDto> {
|
async getCurrentAndAll(@Query('date') date?: string): Promise<PayPeriodBundleDto> {
|
||||||
const [current, periods] = await Promise.all([
|
const [current, periods] = await Promise.all([
|
||||||
this.queryService.findCurrent(date),
|
this.queryService.findCurrent(date),
|
||||||
|
|
@ -31,19 +27,11 @@ export class PayPeriodsController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get("date/:date")
|
@Get("date/:date")
|
||||||
@ApiOperation({ summary: "Resolve a period by a date within it" })
|
|
||||||
@ApiResponse({ status: 200, description: "Pay period found for the selected date", type: PayPeriodDto })
|
|
||||||
@ApiNotFoundResponse({ description: "Pay period not found for the selected date" })
|
|
||||||
async findByDate(@Param("date") date: string) {
|
async findByDate(@Param("date") date: string) {
|
||||||
return this.queryService.findByDate(date);
|
return this.queryService.findByDate(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(":year/:periodNumber")
|
@Get(":year/:periodNumber")
|
||||||
@ApiOperation({ summary: "Find pay period by year and period number" })
|
|
||||||
@ApiParam({ name: "year", type: Number, example: 2024 })
|
|
||||||
@ApiParam({ name: "periodNumber", type: Number, example: 1, description: "1..26" })
|
|
||||||
@ApiResponse({ status: 200, description: "Pay period found", type: PayPeriodDto })
|
|
||||||
@ApiNotFoundResponse({ description: "Pay period not found" })
|
|
||||||
async findOneByYear(
|
async findOneByYear(
|
||||||
@Param("year", ParseIntPipe) year: number,
|
@Param("year", ParseIntPipe) year: number,
|
||||||
@Param("periodNumber", ParseIntPipe) period_no: number,
|
@Param("periodNumber", ParseIntPipe) period_no: number,
|
||||||
|
|
@ -61,27 +49,16 @@ export class PayPeriodsController {
|
||||||
|
|
||||||
@Get(':year/:periodNumber/:email')
|
@Get(':year/:periodNumber/:email')
|
||||||
//@RolesAllowed(RoleEnum.SUPERVISOR)
|
//@RolesAllowed(RoleEnum.SUPERVISOR)
|
||||||
@ApiOperation({ summary: 'Supervisor crew overview for a given pay period' })
|
async getCrewOverview( @Req() req,
|
||||||
@ApiParam({ name: 'year', type: Number, example: 2024 })
|
|
||||||
@ApiParam({ name: 'periodNumber', type: Number, example: 1, description: '1..26' })
|
|
||||||
@ApiQuery({ name: 'includeSubtree', required: false, type: Boolean, example: false, description: 'Include indirect reports' })
|
|
||||||
@ApiResponse({ status: 200, description: 'Crew overview', type: PayPeriodOverviewDto })
|
|
||||||
@ApiNotFoundResponse({ description: 'Pay period not found' })
|
|
||||||
async getCrewOverview(
|
|
||||||
@Param('year', ParseIntPipe) year: number,
|
@Param('year', ParseIntPipe) year: number,
|
||||||
@Param('periodNumber', ParseIntPipe) period_no: number,
|
@Param('periodNumber', ParseIntPipe) period_no: number,
|
||||||
@Param('email') email: string,
|
|
||||||
@Query('includeSubtree', new ParseBoolPipe({ optional: true })) include_subtree = false,
|
@Query('includeSubtree', new ParseBoolPipe({ optional: true })) include_subtree = false,
|
||||||
): Promise<PayPeriodOverviewDto> {
|
): Promise<PayPeriodOverviewDto> {
|
||||||
|
const email = req.user?.email;
|
||||||
return this.queryService.getCrewOverview(year, period_no, email, include_subtree);
|
return this.queryService.getCrewOverview(year, period_no, email, include_subtree);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('overview/:year/:periodNumber')
|
@Get('overview/:year/:periodNumber')
|
||||||
@ApiOperation({ summary: 'Detailed view of a pay period by year + number' })
|
|
||||||
@ApiParam({ name: 'year', type: Number, example: 2024 })
|
|
||||||
@ApiParam({ name: 'periodNumber', type: Number, example: 1, description: '1..26' })
|
|
||||||
@ApiResponse({ status: 200, description: 'Pay period overview found', type: PayPeriodOverviewDto })
|
|
||||||
@ApiNotFoundResponse({ description: 'Pay period not found' })
|
|
||||||
async getOverviewByYear(
|
async getOverviewByYear(
|
||||||
@Param('year', ParseIntPipe) year: number,
|
@Param('year', ParseIntPipe) year: number,
|
||||||
@Param('periodNumber', ParseIntPipe) period_no: number,
|
@Param('periodNumber', ParseIntPipe) period_no: number,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Type } from "class-transformer";
|
import { Type } from "class-transformer";
|
||||||
import { IsArray, IsBoolean, IsEmail, IsInt, IsOptional, ValidateNested } from "class-validator";
|
import { IsArray, IsBoolean, IsEmail, IsInt, ValidateNested } from "class-validator";
|
||||||
|
|
||||||
export class BulkCrewApprovalItemDto {
|
export class BulkCrewApprovalItemDto {
|
||||||
@IsInt()
|
@IsInt()
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
import { ApiProperty } from "@nestjs/swagger";
|
|
||||||
import { PayPeriodDto } from "./pay-period.dto";
|
import { PayPeriodDto } from "./pay-period.dto";
|
||||||
|
|
||||||
export class PayPeriodBundleDto {
|
export class PayPeriodBundleDto {
|
||||||
|
|
||||||
@ApiProperty({ type: PayPeriodDto, description: 'Current pay period (resolved from date)' })
|
|
||||||
current: PayPeriodDto;
|
current: PayPeriodDto;
|
||||||
|
|
||||||
@ApiProperty({ type: [PayPeriodDto], description: 'All pay periods' })
|
|
||||||
periods: PayPeriodDto[];
|
periods: PayPeriodDto[];
|
||||||
}
|
}
|
||||||
|
|
@ -1,27 +1,7 @@
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
|
||||||
|
|
||||||
export class EmployeePeriodOverviewDto {
|
export class EmployeePeriodOverviewDto {
|
||||||
// @ApiProperty({
|
|
||||||
// example: 42,
|
|
||||||
// description: "Employees.id (clé primaire num.)",
|
|
||||||
// })
|
|
||||||
// @Allow()
|
|
||||||
// @IsOptional()
|
|
||||||
// employee_id: number;
|
|
||||||
|
|
||||||
|
|
||||||
email: string;
|
email: string;
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: 'Alex Dupont',
|
|
||||||
description: 'Nom complet de lemployé',
|
|
||||||
})
|
|
||||||
employee_name: string;
|
employee_name: string;
|
||||||
|
|
||||||
@ApiProperty({ example: 40, description: 'pay-period`s regular hours' })
|
|
||||||
regular_hours: number;
|
regular_hours: number;
|
||||||
|
|
||||||
@ApiProperty({ example: 0, description: 'pay-period`s other hours' })
|
|
||||||
other_hours: {
|
other_hours: {
|
||||||
evening_hours: number;
|
evening_hours: number;
|
||||||
|
|
||||||
|
|
@ -35,20 +15,9 @@ export class EmployeePeriodOverviewDto {
|
||||||
|
|
||||||
vacation_hours: number;
|
vacation_hours: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
total_hours: number;
|
total_hours: number;
|
||||||
|
|
||||||
@ApiProperty({ example: 420.69, description: 'pay-period`s total expenses ($)' })
|
|
||||||
expenses: number;
|
expenses: number;
|
||||||
|
|
||||||
@ApiProperty({ example: 40, description: 'pay-period total mileages (km)' })
|
|
||||||
mileage: number;
|
mileage: number;
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: true,
|
|
||||||
description: 'Tous les timesheets de la période sont approuvés pour cet employé',
|
|
||||||
})
|
|
||||||
is_approved: boolean;
|
is_approved: boolean;
|
||||||
|
|
||||||
is_remote: boolean;
|
is_remote: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,11 @@
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
|
||||||
import { EmployeePeriodOverviewDto } from './overview-employee-period.dto';
|
import { EmployeePeriodOverviewDto } from './overview-employee-period.dto';
|
||||||
|
|
||||||
export class PayPeriodOverviewDto {
|
export class PayPeriodOverviewDto {
|
||||||
@ApiProperty({ example: 1, description: 'Period number (1–26)' })
|
|
||||||
pay_period_no: number;
|
pay_period_no: number;
|
||||||
|
|
||||||
@ApiProperty({ example: 2023, description: 'Calendar year of the period' })
|
|
||||||
pay_year: number;
|
pay_year: number;
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: '2023-12-17',
|
|
||||||
type: String,
|
|
||||||
format: 'date',
|
|
||||||
description: "Period start date (YYYY-MM-DD)",
|
|
||||||
})
|
|
||||||
period_start: string;
|
period_start: string;
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: '2023-12-30',
|
|
||||||
type: String,
|
|
||||||
format: 'date',
|
|
||||||
description: "Period end date (YYYY-MM-DD)",
|
|
||||||
})
|
|
||||||
period_end: string;
|
period_end: string;
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: '2023-12-30',
|
|
||||||
type: String,
|
|
||||||
format: 'date',
|
|
||||||
description: "Period pay day(YYYY-MM-DD)",
|
|
||||||
})
|
|
||||||
payday: string;
|
payday: string;
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: '2023-12-17 → 2023-12-30',
|
|
||||||
description: 'Human-readable label',
|
|
||||||
})
|
|
||||||
label: string;
|
label: string;
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
type: [EmployeePeriodOverviewDto],
|
|
||||||
description: 'Per-employee overview for the period',
|
|
||||||
})
|
|
||||||
employees_overview: EmployeePeriodOverviewDto[];
|
employees_overview: EmployeePeriodOverviewDto[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,8 @@
|
||||||
import { ApiProperty } from "@nestjs/swagger";
|
|
||||||
|
|
||||||
export class PayPeriodDto {
|
export class PayPeriodDto {
|
||||||
@ApiProperty({ example: 1,
|
|
||||||
description: 'numéro cyclique de la période entre 1 et 26' })
|
|
||||||
pay_period_no: number;
|
pay_period_no: number;
|
||||||
|
|
||||||
@ApiProperty({ example: '2023-12-17',
|
|
||||||
type: String, format: 'date' })
|
|
||||||
period_start: string;
|
period_start: string;
|
||||||
|
|
||||||
@ApiProperty({ example: '2023-12-30',
|
|
||||||
type: String, format: 'date' })
|
|
||||||
period_end: string;
|
period_end: string;
|
||||||
|
|
||||||
@ApiProperty({ example: '2023-01-04',
|
|
||||||
type: String, format: 'date' })
|
|
||||||
payday: string;
|
payday: string;
|
||||||
|
|
||||||
@ApiProperty({ example: 2023 })
|
|
||||||
pay_year: number;
|
pay_year: number;
|
||||||
|
|
||||||
@ApiProperty({ example: '2023-12-17 → 2023-12-30' })
|
|
||||||
label: string;
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
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)))));
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
export interface ShiftKey {
|
|
||||||
timesheet_id: number;
|
|
||||||
date: Date;
|
|
||||||
start_time: Date;
|
|
||||||
end_time: Date;
|
|
||||||
bank_code_id: number;
|
|
||||||
is_remote: boolean;
|
|
||||||
comment?: string | null;
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
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 };
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
export const PAY_PERIOD_SELECT = {
|
|
||||||
period_start: true,
|
|
||||||
period_end: true,
|
|
||||||
} as const;
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
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}];
|
|
||||||
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
import { EmployeeTimesheetResolver } from "./utils/resolve-timesheet.utils";
|
|
||||||
import { EmailToIdResolver } from "./utils/resolve-email-id.utils";
|
|
||||||
import { BankCodesResolver } from "./utils/resolve-bank-type-id.utils";
|
|
||||||
import { FullNameResolver } from "./utils/resolve-full-name.utils";
|
|
||||||
import { PrismaModule } from "src/prisma/prisma.module";
|
|
||||||
import { Module } from "@nestjs/common";
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
imports: [PrismaModule],
|
|
||||||
providers: [
|
|
||||||
FullNameResolver,
|
|
||||||
EmailToIdResolver,
|
|
||||||
BankCodesResolver,
|
|
||||||
EmployeeTimesheetResolver,
|
|
||||||
],
|
|
||||||
exports: [
|
|
||||||
FullNameResolver,
|
|
||||||
EmailToIdResolver,
|
|
||||||
BankCodesResolver,
|
|
||||||
EmployeeTimesheetResolver,
|
|
||||||
],
|
|
||||||
}) export class SharedModule {}
|
|
||||||
|
|
@ -4,7 +4,6 @@ import { BusinessLogicsModule } from "src/time-and-attendance/domains/business-l
|
||||||
import { ExpenseController } from "src/time-and-attendance/expenses/controllers/expense.controller";
|
import { ExpenseController } from "src/time-and-attendance/expenses/controllers/expense.controller";
|
||||||
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 { PayperiodsModule } from "src/time-and-attendance/pay-period/pay-periods.module";
|
import { PayperiodsModule } from "src/time-and-attendance/pay-period/pay-periods.module";
|
||||||
import { SharedModule } from "src/time-and-attendance/shared/shared.module";
|
|
||||||
import { SchedulePresetsController } from "src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller";
|
import { SchedulePresetsController } from "src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller";
|
||||||
import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service";
|
import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service";
|
||||||
import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service";
|
import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service";
|
||||||
|
|
@ -14,12 +13,13 @@ import { ShiftsGetService } from "src/time-and-attendance/time-tracker/shifts/se
|
||||||
import { ShiftsUpsertService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service";
|
import { ShiftsUpsertService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service";
|
||||||
import { TimesheetController } from "src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller";
|
import { TimesheetController } from "src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller";
|
||||||
import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service";
|
import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service";
|
||||||
|
import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils";
|
||||||
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
BusinessLogicsModule,
|
BusinessLogicsModule,
|
||||||
PayperiodsModule,
|
PayperiodsModule,
|
||||||
SharedModule,
|
|
||||||
],
|
],
|
||||||
controllers: [
|
controllers: [
|
||||||
TimesheetController,
|
TimesheetController,
|
||||||
|
|
@ -35,6 +35,8 @@ import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-track
|
||||||
SchedulePresetsUpsertService,
|
SchedulePresetsUpsertService,
|
||||||
SchedulePresetsGetService,
|
SchedulePresetsGetService,
|
||||||
SchedulePresetsApplyService,
|
SchedulePresetsApplyService,
|
||||||
|
EmailToIdResolver,
|
||||||
|
BankCodesResolver,
|
||||||
],
|
],
|
||||||
exports: [],
|
exports: [],
|
||||||
}) export class TimeAndAttendanceModule { };
|
}) export class TimeAndAttendanceModule { };
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
|
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { SharedModule } from "src/time-and-attendance/shared/shared.module";
|
|
||||||
import { SchedulePresetsController } from "src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller";
|
import { SchedulePresetsController } from "src/time-and-attendance/time-tracker/schedule-presets/controller/schedule-presets.controller";
|
||||||
import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service";
|
import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service";
|
||||||
import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service";
|
import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service";
|
||||||
|
|
@ -8,7 +7,6 @@ import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-track
|
||||||
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [SharedModule],
|
|
||||||
controllers: [SchedulePresetsController],
|
controllers: [SchedulePresetsController],
|
||||||
providers: [
|
providers: [
|
||||||
SchedulePresetsUpsertService,
|
SchedulePresetsUpsertService,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { DATE_ISO_FORMAT } from "src/time-and-attendance/utils/constants.utils";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { ApplyResult } from "src/time-and-attendance/utils/type.utils";
|
import { ApplyResult } from "src/time-and-attendance/utils/type.utils";
|
||||||
import { WEEKDAY } from "src/time-and-attendance/utils/mappers.utils";
|
import { WEEKDAY } from "src/time-and-attendance/utils/mappers.utils";
|
||||||
import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils";
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { PresetResponse, ShiftResponse } from "src/time-and-attendance/utils/typ
|
||||||
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 { Prisma } from "@prisma/client";
|
||||||
import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils";
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SchedulePresetsGetService {
|
export class SchedulePresetsGetService {
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ import { CreatePresetResult, DeletePresetResult, UpdatePresetResult } from "src/
|
||||||
import { Prisma, Weekday } from "@prisma/client";
|
import { Prisma, Weekday } from "@prisma/client";
|
||||||
import { toHHmmFromDate } from "src/time-and-attendance/utils/date-time.utils";
|
import { toHHmmFromDate } from "src/time-and-attendance/utils/date-time.utils";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { BankCodesResolver } from "src/time-and-attendance/shared/utils/resolve-bank-type-id.utils";
|
import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-type-id.utils";
|
||||||
import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto";
|
import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto";
|
||||||
import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils";
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SchedulePresetsUpsertService {
|
export class SchedulePresetsUpsertService {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { overlaps, toStringFromHHmm, toStringFromDate, toDateFromString, toHHmmF
|
||||||
import { Injectable, BadRequestException, ConflictException, NotFoundException } from "@nestjs/common";
|
import { Injectable, BadRequestException, ConflictException, NotFoundException } from "@nestjs/common";
|
||||||
import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service";
|
import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { EmailToIdResolver } from "src/time-and-attendance/shared/utils/resolve-email-id.utils";
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
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 { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto";
|
import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto";
|
||||||
import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto";
|
import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto";
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { NUMBER_OF_TIMESHEETS_TO_RETURN } from "src/time-and-attendance/utils/co
|
||||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
import { TotalExpenses, TotalHours } from "src/time-and-attendance/utils/type.utils";
|
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/shared/utils/resolve-email-id.utils";
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class GetTimesheetsOverviewService {
|
export class GetTimesheetsOverviewService {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
|
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { SharedModule } from 'src/time-and-attendance/shared/shared.module';
|
|
||||||
import { TimesheetController } from 'src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller';
|
import { TimesheetController } from 'src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller';
|
||||||
import { TimesheetArchiveService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service';
|
import { TimesheetArchiveService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service';
|
||||||
import { GetTimesheetsOverviewService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service';
|
import { GetTimesheetsOverviewService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [SharedModule],
|
|
||||||
controllers: [TimesheetController],
|
controllers: [TimesheetController],
|
||||||
providers: [
|
providers: [
|
||||||
TimesheetArchiveService,
|
TimesheetArchiveService,
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ export const ANCHOR_ISO = '2023-12-17';
|
||||||
export const PERIOD_DAYS = 14;
|
export const PERIOD_DAYS = 14;
|
||||||
export const PERIODS_PER_YEAR = 26;
|
export const PERIODS_PER_YEAR = 26;
|
||||||
export const MS_PER_DAY = 86_400_000;
|
export const MS_PER_DAY = 86_400_000;
|
||||||
|
export const MS_PER_WEEK = 7 * 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
|
|
||||||
//REGEX CONSTANTS
|
//REGEX CONSTANTS
|
||||||
export const DATE_ISO_FORMAT = /^\d{4}-\d{2}-\d{2}$/;
|
export const DATE_ISO_FORMAT = /^\d{4}-\d{2}-\d{2}$/;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { BadRequestException } from "@nestjs/common";
|
||||||
import { ANCHOR_ISO, MS_PER_DAY, PERIODS_PER_YEAR, PERIOD_DAYS } from "src/time-and-attendance/utils/constants.utils";
|
import { ANCHOR_ISO, MS_PER_DAY, PERIODS_PER_YEAR, PERIOD_DAYS } from "src/time-and-attendance/utils/constants.utils";
|
||||||
|
|
||||||
//ensures the week starts from sunday
|
//ensures the week starts from sunday
|
||||||
|
|
@ -89,3 +90,37 @@ export function listPayYear(pay_year: number, anchorISO = ANCHOR_ISO) {
|
||||||
|
|
||||||
export const overlaps = (a: { start: Date; end: Date }, b: { start: Date; end: Date }) =>
|
export const overlaps = (a: { start: Date; end: Date }, b: { start: Date; end: Date }) =>
|
||||||
!(a.end <= b.start || a.start >= b.end);
|
!(a.end <= b.start || a.start >= b.end);
|
||||||
|
|
||||||
|
|
||||||
|
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)))));
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Prisma, PrismaClient } from "@prisma/client";
|
import { Prisma, PrismaClient } from "@prisma/client";
|
||||||
import { NotFoundException } from "@nestjs/common";
|
import { NotFoundException } from "@nestjs/common";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { ShiftKey } from "../interfaces/shifts.interface";
|
import { ShiftKey } from "src/time-and-attendance/utils/type.utils";
|
||||||
|
|
||||||
type Tx = Prisma.TransactionClient | PrismaClient;
|
type Tx = Prisma.TransactionClient | PrismaClient;
|
||||||
|
|
||||||
|
|
@ -47,3 +47,34 @@ 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}];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma, PrismaClient } from "@prisma/client";
|
||||||
import { WeekOvertimeSummary } from "src/time-and-attendance/domains/services/overtime.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";
|
||||||
import { updateExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-update.dto";
|
import { updateExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-update.dto";
|
||||||
import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto";
|
import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto";
|
||||||
|
|
@ -81,3 +80,31 @@ export type ApplyResult = {
|
||||||
export type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>;
|
export type LeaveRequestRow = Prisma.LeaveRequestsGetPayload<{ select: typeof leaveRequestsSelect}>;
|
||||||
|
|
||||||
export type UpsertAction = 'create' | 'update' | 'delete';
|
export type UpsertAction = 'create' | 'update' | 'delete';
|
||||||
|
|
||||||
|
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