refactor(expenses): commented module expenses et leave-request. needs refactor to match new timesheets and shifts module.
This commit is contained in:
parent
11f6cf2049
commit
6aeaf16993
File diff suppressed because it is too large
Load Diff
|
|
@ -1,21 +1,21 @@
|
|||
import { BadRequestException, Module, ValidationPipe } from '@nestjs/common';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { ArchivalModule } from './modules/archival/archival.module';
|
||||
// import { ArchivalModule } from './modules/archival/archival.module';
|
||||
import { AuthenticationModule } from './modules/authentication/auth.module';
|
||||
import { BankCodesModule } from './modules/bank-codes/bank-codes.module';
|
||||
import { BusinessLogicsModule } from './modules/business-logics/business-logics.module';
|
||||
// import { CsvExportModule } from './modules/exports/csv-exports.module';
|
||||
import { CustomersModule } from './modules/customers/customers.module';
|
||||
import { EmployeesModule } from './modules/employees/employees.module';
|
||||
import { ExpensesModule } from './modules/expenses/expenses.module';
|
||||
// import { ExpensesModule } from './modules/expenses/expenses.module';
|
||||
import { HealthModule } from './health/health.module';
|
||||
import { HealthController } from './health/health.controller';
|
||||
import { LeaveRequestsModule } from './modules/leave-requests/leave-requests.module';
|
||||
// import { LeaveRequestsModule } from './modules/leave-requests/leave-requests.module';
|
||||
import { NotificationsModule } from './modules/notifications/notifications.module';
|
||||
import { OauthSessionsModule } from './modules/oauth-sessions/oauth-sessions.module';
|
||||
import { OvertimeService } from './modules/business-logics/services/overtime.service';
|
||||
import { PayperiodsModule } from './modules/pay-periods/pay-periods.module';
|
||||
// import { PayperiodsModule } from './modules/pay-periods/pay-periods.module';
|
||||
import { PreferencesModule } from './modules/preferences/preferences.module';
|
||||
import { PrismaModule } from './prisma/prisma.module';
|
||||
import { ScheduleModule } from '@nestjs/schedule';
|
||||
|
|
@ -30,7 +30,7 @@ import { SchedulePresetsModule } from './modules/schedule-presets/schedule-prese
|
|||
|
||||
@Module({
|
||||
imports: [
|
||||
ArchivalModule,
|
||||
// ArchivalModule,
|
||||
AuthenticationModule,
|
||||
BankCodesModule,
|
||||
BusinessLogicsModule,
|
||||
|
|
@ -38,12 +38,12 @@ import { SchedulePresetsModule } from './modules/schedule-presets/schedule-prese
|
|||
// CsvExportModule,
|
||||
CustomersModule,
|
||||
EmployeesModule,
|
||||
ExpensesModule,
|
||||
// ExpensesModule,
|
||||
HealthModule,
|
||||
LeaveRequestsModule,
|
||||
// LeaveRequestsModule,
|
||||
NotificationsModule,
|
||||
OauthSessionsModule,
|
||||
PayperiodsModule,
|
||||
// PayperiodsModule,
|
||||
PreferencesModule,
|
||||
PrismaModule,
|
||||
ScheduleModule.forRoot(), //cronjobs
|
||||
|
|
|
|||
|
|
@ -1,34 +1,34 @@
|
|||
import { Module } from "@nestjs/common";
|
||||
import { ScheduleModule } from "@nestjs/schedule";
|
||||
import { TimesheetsModule } from "../timesheets/timesheets.module";
|
||||
import { ExpensesModule } from "../expenses/expenses.module";
|
||||
import { ShiftsModule } from "../shifts/shifts.module";
|
||||
import { LeaveRequestsModule } from "../leave-requests/leave-requests.module";
|
||||
import { ArchivalService } from "./services/archival.service";
|
||||
import { EmployeesArchiveController } from "./controllers/employees-archive.controller";
|
||||
import { ExpensesArchiveController } from "./controllers/expenses-archive.controller";
|
||||
import { LeaveRequestsArchiveController } from "./controllers/leave-requests-archive.controller";
|
||||
import { ShiftsArchiveController } from "./controllers/shifts-archive.controller";
|
||||
import { TimesheetsArchiveController } from "./controllers/timesheets-archive.controller";
|
||||
import { EmployeesModule } from "../employees/employees.module";
|
||||
// import { Module } from "@nestjs/common";
|
||||
// import { ScheduleModule } from "@nestjs/schedule";
|
||||
// import { TimesheetsModule } from "../timesheets/timesheets.module";
|
||||
// import { ExpensesModule } from "../expenses/expenses.module";
|
||||
// import { ShiftsModule } from "../shifts/shifts.module";
|
||||
// import { LeaveRequestsModule } from "../leave-requests/leave-requests.module";
|
||||
// import { ArchivalService } from "./services/archival.service";
|
||||
// import { EmployeesArchiveController } from "./controllers/employees-archive.controller";
|
||||
// import { ExpensesArchiveController } from "./controllers/expenses-archive.controller";
|
||||
// import { LeaveRequestsArchiveController } from "./controllers/leave-requests-archive.controller";
|
||||
// import { ShiftsArchiveController } from "./controllers/shifts-archive.controller";
|
||||
// import { TimesheetsArchiveController } from "./controllers/timesheets-archive.controller";
|
||||
// import { EmployeesModule } from "../employees/employees.module";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
EmployeesModule,
|
||||
ScheduleModule,
|
||||
TimesheetsModule,
|
||||
ExpensesModule,
|
||||
ShiftsModule,
|
||||
LeaveRequestsModule,
|
||||
],
|
||||
providers: [ArchivalService],
|
||||
controllers: [
|
||||
EmployeesArchiveController,
|
||||
ExpensesArchiveController,
|
||||
LeaveRequestsArchiveController,
|
||||
ShiftsArchiveController,
|
||||
TimesheetsArchiveController,
|
||||
],
|
||||
})
|
||||
// @Module({
|
||||
// imports: [
|
||||
// EmployeesModule,
|
||||
// ScheduleModule,
|
||||
// TimesheetsModule,
|
||||
// ExpensesModule,
|
||||
// ShiftsModule,
|
||||
// LeaveRequestsModule,
|
||||
// ],
|
||||
// providers: [ArchivalService],
|
||||
// controllers: [
|
||||
// EmployeesArchiveController,
|
||||
// ExpensesArchiveController,
|
||||
// LeaveRequestsArchiveController,
|
||||
// ShiftsArchiveController,
|
||||
// TimesheetsArchiveController,
|
||||
// ],
|
||||
// })
|
||||
|
||||
export class ArchivalModule {}
|
||||
// export class ArchivalModule {}
|
||||
|
|
@ -1,95 +1,95 @@
|
|||
import { Body, Controller, Get, Param, Put, } from "@nestjs/common";
|
||||
import { Roles as RoleEnum } from '.prisma/client';
|
||||
import { ApiBearerAuth, ApiTags } from "@nestjs/swagger";
|
||||
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
||||
import { ExpensesCommandService } from "../services/expenses-command.service";
|
||||
import { UpsertExpenseDto } from "../dtos/upsert-expense.dto";
|
||||
import { UpsertExpenseResult } from "../types and interfaces/expenses.types.interfaces";
|
||||
import { DayExpensesDto } from "src/modules/timesheets/~misc_deprecated-files/timesheet-period.dto";
|
||||
import { ExpensesQueryService } from "../services/expenses-query.service";
|
||||
// import { Body, Controller, Get, Param, Put, } from "@nestjs/common";
|
||||
// import { Roles as RoleEnum } from '.prisma/client';
|
||||
// import { ApiBearerAuth, ApiTags } from "@nestjs/swagger";
|
||||
// import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
||||
// import { ExpensesCommandService } from "../services/expenses-command.service";
|
||||
// import { UpsertExpenseDto } from "../dtos/upsert-expense.dto";
|
||||
// import { UpsertExpenseResult } from "../types and interfaces/expenses.types.interfaces";
|
||||
// import { DayExpensesDto } from "src/modules/timesheets/~misc_deprecated-files/timesheet-period.dto";
|
||||
// import { ExpensesQueryService } from "../services/expenses-query.service";
|
||||
|
||||
@ApiTags('Expenses')
|
||||
@ApiBearerAuth('access-token')
|
||||
// @UseGuards()
|
||||
@Controller('Expenses')
|
||||
export class ExpensesController {
|
||||
constructor(
|
||||
private readonly query: ExpensesQueryService,
|
||||
private readonly command: ExpensesCommandService,
|
||||
) {}
|
||||
// @ApiTags('Expenses')
|
||||
// @ApiBearerAuth('access-token')
|
||||
// // @UseGuards()
|
||||
// @Controller('Expenses')
|
||||
// export class ExpensesController {
|
||||
// constructor(
|
||||
// private readonly query: ExpensesQueryService,
|
||||
// private readonly command: ExpensesCommandService,
|
||||
// ) {}
|
||||
|
||||
@Put('upsert/:email/:date')
|
||||
async upsert_by_date(
|
||||
@Param('email') email: string,
|
||||
@Param('date') date: string,
|
||||
@Body() dto: UpsertExpenseDto,
|
||||
): Promise<UpsertExpenseResult> {
|
||||
return this.command.upsertExpensesByDate(email, date, dto);
|
||||
}
|
||||
|
||||
@Get('list/:email/:year/:period_no')
|
||||
async findExpenseListByPayPeriodAndEmail(
|
||||
@Param('email') email:string,
|
||||
@Param('year') year: number,
|
||||
@Param('period_no') period_no: number,
|
||||
): Promise<DayExpensesDto> {
|
||||
return this.query.findExpenseListByPayPeriodAndEmail(email, year, period_no);
|
||||
}
|
||||
|
||||
//_____________________________________________________________________________________________
|
||||
// Deprecated or unused methods
|
||||
//_____________________________________________________________________________________________
|
||||
|
||||
// @Post()
|
||||
// //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
// @ApiOperation({ summary: 'Create expense' })
|
||||
// @ApiResponse({ status: 201, description: 'Expense created',type: CreateExpenseDto })
|
||||
// @ApiResponse({ status: 400, description: 'Incomplete task or invalid data' })
|
||||
// create(@Body() dto: CreateExpenseDto): Promise<Expenses> {
|
||||
// return this.query.create(dto);
|
||||
// @Put('upsert/:email/:date')
|
||||
// async upsert_by_date(
|
||||
// @Param('email') email: string,
|
||||
// @Param('date') date: string,
|
||||
// @Body() dto: UpsertExpenseDto,
|
||||
// ): Promise<UpsertExpenseResult> {
|
||||
// return this.command.upsertExpensesByDate(email, date, dto);
|
||||
// }
|
||||
|
||||
// @Get()
|
||||
// //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
// @ApiOperation({ summary: 'Find all expenses' })
|
||||
// @ApiResponse({ status: 201, description: 'List of expenses found',type: CreateExpenseDto, isArray: true })
|
||||
// @ApiResponse({ status: 400, description: 'List of expenses not found' })
|
||||
// @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
|
||||
// findAll(@Query() filters: SearchExpensesDto): Promise<Expenses[]> {
|
||||
// return this.query.findAll(filters);
|
||||
// @Get('list/:email/:year/:period_no')
|
||||
// async findExpenseListByPayPeriodAndEmail(
|
||||
// @Param('email') email:string,
|
||||
// @Param('year') year: number,
|
||||
// @Param('period_no') period_no: number,
|
||||
// ): Promise<DayExpensesDto> {
|
||||
// return this.query.findExpenseListByPayPeriodAndEmail(email, year, period_no);
|
||||
// }
|
||||
|
||||
// @Get(':id')
|
||||
// //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
// @ApiOperation({ summary: 'Find expense' })
|
||||
// @ApiResponse({ status: 201, description: 'Expense found',type: CreateExpenseDto })
|
||||
// @ApiResponse({ status: 400, description: 'Expense not found' })
|
||||
// findOne(@Param('id', ParseIntPipe) id: number): Promise <Expenses> {
|
||||
// return this.query.findOne(id);
|
||||
// }
|
||||
// //_____________________________________________________________________________________________
|
||||
// // Deprecated or unused methods
|
||||
// //_____________________________________________________________________________________________
|
||||
|
||||
// @Patch(':id')
|
||||
// //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
// @ApiOperation({ summary: 'Expense shift' })
|
||||
// @ApiResponse({ status: 201, description: 'Expense updated',type: CreateExpenseDto })
|
||||
// @ApiResponse({ status: 400, description: 'Expense not found' })
|
||||
// update(@Param('id', ParseIntPipe) id: number, @Body() dto: UpdateExpenseDto) {
|
||||
// return this.query.update(id,dto);
|
||||
// }
|
||||
// // @Post()
|
||||
// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
// // @ApiOperation({ summary: 'Create expense' })
|
||||
// // @ApiResponse({ status: 201, description: 'Expense created',type: CreateExpenseDto })
|
||||
// // @ApiResponse({ status: 400, description: 'Incomplete task or invalid data' })
|
||||
// // create(@Body() dto: CreateExpenseDto): Promise<Expenses> {
|
||||
// // return this.query.create(dto);
|
||||
// // }
|
||||
|
||||
// @Delete(':id')
|
||||
// //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
// @ApiOperation({ summary: 'Delete expense' })
|
||||
// @ApiResponse({ status: 201, description: 'Expense deleted',type: CreateExpenseDto })
|
||||
// @ApiResponse({ status: 400, description: 'Expense not found' })
|
||||
// remove(@Param('id', ParseIntPipe) id: number): Promise<Expenses> {
|
||||
// return this.query.remove(id);
|
||||
// }
|
||||
// // @Get()
|
||||
// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
// // @ApiOperation({ summary: 'Find all expenses' })
|
||||
// // @ApiResponse({ status: 201, description: 'List of expenses found',type: CreateExpenseDto, isArray: true })
|
||||
// // @ApiResponse({ status: 400, description: 'List of expenses not found' })
|
||||
// // @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
|
||||
// // findAll(@Query() filters: SearchExpensesDto): Promise<Expenses[]> {
|
||||
// // return this.query.findAll(filters);
|
||||
// // }
|
||||
|
||||
// @Patch('approval/:id')
|
||||
// //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
// async approve(@Param('id', ParseIntPipe) id: number, @Body('is_approved', ParseBoolPipe) isApproved: boolean) {
|
||||
// return this.command.updateApproval(id, isApproved);
|
||||
// }
|
||||
// // @Get(':id')
|
||||
// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
// // @ApiOperation({ summary: 'Find expense' })
|
||||
// // @ApiResponse({ status: 201, description: 'Expense found',type: CreateExpenseDto })
|
||||
// // @ApiResponse({ status: 400, description: 'Expense not found' })
|
||||
// // findOne(@Param('id', ParseIntPipe) id: number): Promise <Expenses> {
|
||||
// // return this.query.findOne(id);
|
||||
// // }
|
||||
|
||||
}
|
||||
// // @Patch(':id')
|
||||
// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
// // @ApiOperation({ summary: 'Expense shift' })
|
||||
// // @ApiResponse({ status: 201, description: 'Expense updated',type: CreateExpenseDto })
|
||||
// // @ApiResponse({ status: 400, description: 'Expense not found' })
|
||||
// // update(@Param('id', ParseIntPipe) id: number, @Body() dto: UpdateExpenseDto) {
|
||||
// // return this.query.update(id,dto);
|
||||
// // }
|
||||
|
||||
// // @Delete(':id')
|
||||
// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
// // @ApiOperation({ summary: 'Delete expense' })
|
||||
// // @ApiResponse({ status: 201, description: 'Expense deleted',type: CreateExpenseDto })
|
||||
// // @ApiResponse({ status: 400, description: 'Expense not found' })
|
||||
// // remove(@Param('id', ParseIntPipe) id: number): Promise<Expenses> {
|
||||
// // return this.query.remove(id);
|
||||
// // }
|
||||
|
||||
// // @Patch('approval/:id')
|
||||
// // //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
// // async approve(@Param('id', ParseIntPipe) id: number, @Body('is_approved', ParseBoolPipe) isApproved: boolean) {
|
||||
// // return this.command.updateApproval(id, isApproved);
|
||||
// // }
|
||||
|
||||
// }
|
||||
|
|
@ -1,23 +1,23 @@
|
|||
import { ExpensesController } from "./controllers/expenses.controller";
|
||||
import { Module } from "@nestjs/common";
|
||||
import { ExpensesQueryService } from "./services/expenses-query.service";
|
||||
import { BusinessLogicsModule } from "src/modules/business-logics/business-logics.module";
|
||||
import { ExpensesCommandService } from "./services/expenses-command.service";
|
||||
import { ExpensesArchivalService } from "./services/expenses-archival.service";
|
||||
import { SharedModule } from "../shared/shared.module";
|
||||
// import { ExpensesController } from "./controllers/expenses.controller";
|
||||
// import { Module } from "@nestjs/common";
|
||||
// import { ExpensesQueryService } from "./services/expenses-query.service";
|
||||
// import { BusinessLogicsModule } from "src/modules/business-logics/business-logics.module";
|
||||
// import { ExpensesCommandService } from "./services/expenses-command.service";
|
||||
// import { ExpensesArchivalService } from "./services/expenses-archival.service";
|
||||
// import { SharedModule } from "../shared/shared.module";
|
||||
|
||||
@Module({
|
||||
imports: [BusinessLogicsModule, SharedModule],
|
||||
controllers: [ExpensesController],
|
||||
providers: [
|
||||
ExpensesQueryService,
|
||||
ExpensesArchivalService,
|
||||
ExpensesCommandService,
|
||||
],
|
||||
exports: [
|
||||
ExpensesQueryService,
|
||||
ExpensesArchivalService,
|
||||
],
|
||||
})
|
||||
// @Module({
|
||||
// imports: [BusinessLogicsModule, SharedModule],
|
||||
// controllers: [ExpensesController],
|
||||
// providers: [
|
||||
// ExpensesQueryService,
|
||||
// ExpensesArchivalService,
|
||||
// ExpensesCommandService,
|
||||
// ],
|
||||
// exports: [
|
||||
// ExpensesQueryService,
|
||||
// ExpensesArchivalService,
|
||||
// ],
|
||||
// })
|
||||
|
||||
export class ExpensesModule {}
|
||||
// export class ExpensesModule {}
|
||||
|
|
@ -1,250 +1,249 @@
|
|||
import { BaseApprovalService } from "src/common/shared/base-approval.service";
|
||||
import { Expenses, Prisma } from "@prisma/client";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { UpsertExpenseDto } from "../dtos/upsert-expense.dto";
|
||||
import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils";
|
||||
import { ExpenseResponse, UpsertAction } from "../types and interfaces/expenses.types.interfaces";
|
||||
import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
|
||||
import { EmployeeTimesheetResolver } from "src/modules/shared/utils/resolve-timesheet.utils";
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
NotFoundException
|
||||
} from "@nestjs/common";
|
||||
import {
|
||||
assertAndTrimComment,
|
||||
computeAmountDecimal,
|
||||
computeMileageAmount,
|
||||
mapDbExpenseToDayResponse,
|
||||
normalizeType,
|
||||
parseAttachmentId
|
||||
} from "../utils/expenses.utils";
|
||||
import { toDateOnly } from "src/modules/shifts/helpers/shifts-date-time-helpers";
|
||||
// import { BaseApprovalService } from "src/common/shared/base-approval.service";
|
||||
// import { Expenses, Prisma } from "@prisma/client";
|
||||
// import { PrismaService } from "src/prisma/prisma.service";
|
||||
// import { UpsertExpenseDto } from "../dtos/upsert-expense.dto";
|
||||
// import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils";
|
||||
// import { ExpenseResponse, UpsertAction } from "../types and interfaces/expenses.types.interfaces";
|
||||
// import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
|
||||
// import { EmployeeTimesheetResolver } from "src/modules/shared/utils/resolve-timesheet.utils";
|
||||
// import {
|
||||
// BadRequestException,
|
||||
// Injectable,
|
||||
// NotFoundException
|
||||
// } from "@nestjs/common";
|
||||
// import {
|
||||
// assertAndTrimComment,
|
||||
// computeAmountDecimal,
|
||||
// computeMileageAmount,
|
||||
// mapDbExpenseToDayResponse,
|
||||
// normalizeType,
|
||||
// parseAttachmentId
|
||||
// } from "../utils/expenses.utils";
|
||||
|
||||
@Injectable()
|
||||
export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
||||
constructor(
|
||||
prisma: PrismaService,
|
||||
private readonly bankCodesResolver: BankCodesResolver,
|
||||
private readonly timesheetsResolver: EmployeeTimesheetResolver,
|
||||
private readonly emailResolver: EmailToIdResolver,
|
||||
) { super(prisma); }
|
||||
// @Injectable()
|
||||
// export class ExpensesCommandService extends BaseApprovalService<Expenses> {
|
||||
// constructor(
|
||||
// prisma: PrismaService,
|
||||
// private readonly bankCodesResolver: BankCodesResolver,
|
||||
// private readonly timesheetsResolver: EmployeeTimesheetResolver,
|
||||
// private readonly emailResolver: EmailToIdResolver,
|
||||
// ) { super(prisma); }
|
||||
|
||||
//_____________________________________________________________________________________________
|
||||
// APPROVAL TX-DELEGATE METHODS
|
||||
//_____________________________________________________________________________________________
|
||||
// //_____________________________________________________________________________________________
|
||||
// // APPROVAL TX-DELEGATE METHODS
|
||||
// //_____________________________________________________________________________________________
|
||||
|
||||
protected get delegate() {
|
||||
return this.prisma.expenses;
|
||||
}
|
||||
// protected get delegate() {
|
||||
// return this.prisma.expenses;
|
||||
// }
|
||||
|
||||
protected delegateFor(transaction: Prisma.TransactionClient){
|
||||
return transaction.expenses;
|
||||
}
|
||||
// protected delegateFor(transaction: Prisma.TransactionClient){
|
||||
// return transaction.expenses;
|
||||
// }
|
||||
|
||||
async updateApproval(id: number, isApproved: boolean): Promise<Expenses> {
|
||||
return this.prisma.$transaction((transaction) =>
|
||||
this.updateApprovalWithTransaction(transaction, id, isApproved),
|
||||
);
|
||||
}
|
||||
// async updateApproval(id: number, isApproved: boolean): Promise<Expenses> {
|
||||
// return this.prisma.$transaction((transaction) =>
|
||||
// this.updateApprovalWithTransaction(transaction, id, isApproved),
|
||||
// );
|
||||
// }
|
||||
|
||||
//_____________________________________________________________________________________________
|
||||
// MASTER CRUD FUNCTION
|
||||
//_____________________________________________________________________________________________
|
||||
readonly upsertExpensesByDate = async (email: string, date: string, dto: UpsertExpenseDto,
|
||||
): Promise<{ action:UpsertAction; day: ExpenseResponse[] }> => {
|
||||
// //_____________________________________________________________________________________________
|
||||
// // MASTER CRUD FUNCTION
|
||||
// //_____________________________________________________________________________________________
|
||||
// readonly upsertExpensesByDate = async (email: string, date: string, dto: UpsertExpenseDto,
|
||||
// ): Promise<{ action:UpsertAction; day: ExpenseResponse[] }> => {
|
||||
|
||||
//validates if there is an existing expense, at least 1 old or new
|
||||
const { old_expense, new_expense } = dto ?? {};
|
||||
if(!old_expense && !new_expense) throw new BadRequestException('At least one expense must be provided');
|
||||
// //validates if there is an existing expense, at least 1 old or new
|
||||
// const { old_expense, new_expense } = dto ?? {};
|
||||
// if(!old_expense && !new_expense) throw new BadRequestException('At least one expense must be provided');
|
||||
|
||||
//validate date format
|
||||
const date_only = toDateOnly(date);
|
||||
if(Number.isNaN(date_only.getTime())) throw new BadRequestException('Invalid date format (expected: YYYY-MM-DD)');
|
||||
// //validate date format
|
||||
// const date_only = toDateOnly(date);
|
||||
// if(Number.isNaN(date_only.getTime())) throw new BadRequestException('Invalid date format (expected: YYYY-MM-DD)');
|
||||
|
||||
//resolve employee_id by email
|
||||
const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||
// //resolve employee_id by email
|
||||
// const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||
|
||||
//make sure a timesheet existes
|
||||
const timesheet_id = await this.timesheetsResolver.findTimesheetIdByEmail(email, date_only);
|
||||
if(!timesheet_id) throw new NotFoundException(`no timesheet found for employee #${employee_id}`)
|
||||
const {id} = timesheet_id;
|
||||
// //make sure a timesheet existes
|
||||
// const timesheet_id = await this.timesheetsResolver.findTimesheetIdByEmail(email, date_only);
|
||||
// if(!timesheet_id) throw new NotFoundException(`no timesheet found for employee #${employee_id}`)
|
||||
// const {id} = timesheet_id;
|
||||
|
||||
return this.prisma.$transaction(async (tx) => {
|
||||
const loadDay = async (): Promise<ExpenseResponse[]> => {
|
||||
const rows = await tx.expenses.findMany({
|
||||
where: {
|
||||
timesheet_id: id,
|
||||
date: date_only,
|
||||
},
|
||||
include: {
|
||||
bank_code: {
|
||||
select: {
|
||||
type: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: [{ date: 'asc' }, { id: 'asc' }],
|
||||
});
|
||||
// return this.prisma.$transaction(async (tx) => {
|
||||
// const loadDay = async (): Promise<ExpenseResponse[]> => {
|
||||
// const rows = await tx.expenses.findMany({
|
||||
// where: {
|
||||
// timesheet_id: id,
|
||||
// date: date_only,
|
||||
// },
|
||||
// include: {
|
||||
// bank_code: {
|
||||
// select: {
|
||||
// type: true,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// orderBy: [{ date: 'asc' }, { id: 'asc' }],
|
||||
// });
|
||||
|
||||
return rows.map((r) =>
|
||||
mapDbExpenseToDayResponse({
|
||||
date: r.date,
|
||||
amount: r.amount ?? 0,
|
||||
mileage: r.mileage ?? 0,
|
||||
comment: r.comment,
|
||||
is_approved: r.is_approved,
|
||||
bank_code: r.bank_code,
|
||||
}));
|
||||
};
|
||||
// return rows.map((r) =>
|
||||
// mapDbExpenseToDayResponse({
|
||||
// date: r.date,
|
||||
// amount: r.amount ?? 0,
|
||||
// mileage: r.mileage ?? 0,
|
||||
// comment: r.comment,
|
||||
// is_approved: r.is_approved,
|
||||
// bank_code: r.bank_code,
|
||||
// }));
|
||||
// };
|
||||
|
||||
const normalizePayload = async (payload: {
|
||||
type: string;
|
||||
amount?: number;
|
||||
mileage?: number;
|
||||
comment: string;
|
||||
attachment?: string | number;
|
||||
}): Promise<{
|
||||
type: string;
|
||||
bank_code_id: number;
|
||||
amount: Prisma.Decimal;
|
||||
mileage: number | null;
|
||||
comment: string;
|
||||
attachment: number | null;
|
||||
}> => {
|
||||
const type = normalizeType(payload.type);
|
||||
const comment = assertAndTrimComment(payload.comment);
|
||||
const attachment = parseAttachmentId(payload.attachment);
|
||||
// const normalizePayload = async (payload: {
|
||||
// type: string;
|
||||
// amount?: number;
|
||||
// mileage?: number;
|
||||
// comment: string;
|
||||
// attachment?: string | number;
|
||||
// }): Promise<{
|
||||
// type: string;
|
||||
// bank_code_id: number;
|
||||
// amount: Prisma.Decimal;
|
||||
// mileage: number | null;
|
||||
// comment: string;
|
||||
// attachment: number | null;
|
||||
// }> => {
|
||||
// const type = normalizeType(payload.type);
|
||||
// const comment = assertAndTrimComment(payload.comment);
|
||||
// const attachment = parseAttachmentId(payload.attachment);
|
||||
|
||||
const { id: bank_code_id, modifier } = await this.bankCodesResolver.findByType(type);
|
||||
let amount = computeAmountDecimal(type, payload, modifier);
|
||||
let mileage: number | null = null;
|
||||
// const { id: bank_code_id, modifier } = await this.bankCodesResolver.findByType(type);
|
||||
// let amount = computeAmountDecimal(type, payload, modifier);
|
||||
// let mileage: number | null = null;
|
||||
|
||||
if (type === 'MILEAGE') {
|
||||
mileage = Number(payload.mileage ?? 0);
|
||||
if (!(mileage > 0)) {
|
||||
throw new BadRequestException('Mileage required and must be > 0 for type MILEAGE');
|
||||
}
|
||||
// if (type === 'MILEAGE') {
|
||||
// mileage = Number(payload.mileage ?? 0);
|
||||
// if (!(mileage > 0)) {
|
||||
// throw new BadRequestException('Mileage required and must be > 0 for type MILEAGE');
|
||||
// }
|
||||
|
||||
const amountNumber = computeMileageAmount(mileage, modifier);
|
||||
amount = new Prisma.Decimal(amountNumber);
|
||||
// const amountNumber = computeMileageAmount(mileage, modifier);
|
||||
// amount = new Prisma.Decimal(amountNumber);
|
||||
|
||||
} else {
|
||||
if (!(typeof payload.amount === 'number' && payload.amount >= 0)) {
|
||||
throw new BadRequestException('Amount required for non-MILEAGE expense');
|
||||
}
|
||||
amount = new Prisma.Decimal(payload.amount);
|
||||
}
|
||||
// } else {
|
||||
// if (!(typeof payload.amount === 'number' && payload.amount >= 0)) {
|
||||
// throw new BadRequestException('Amount required for non-MILEAGE expense');
|
||||
// }
|
||||
// amount = new Prisma.Decimal(payload.amount);
|
||||
// }
|
||||
|
||||
if (attachment !== null) {
|
||||
const attachment_row = await tx.attachments.findUnique({
|
||||
where: { id: attachment },
|
||||
select: { status: true },
|
||||
});
|
||||
if (!attachment_row || attachment_row.status !== 'ACTIVE') {
|
||||
throw new BadRequestException('Attachment not found or inactive');
|
||||
}
|
||||
}
|
||||
// if (attachment !== null) {
|
||||
// const attachment_row = await tx.attachments.findUnique({
|
||||
// where: { id: attachment },
|
||||
// select: { status: true },
|
||||
// });
|
||||
// if (!attachment_row || attachment_row.status !== 'ACTIVE') {
|
||||
// throw new BadRequestException('Attachment not found or inactive');
|
||||
// }
|
||||
// }
|
||||
|
||||
return {
|
||||
type,
|
||||
bank_code_id,
|
||||
amount,
|
||||
mileage,
|
||||
comment,
|
||||
attachment
|
||||
};
|
||||
};
|
||||
// return {
|
||||
// type,
|
||||
// bank_code_id,
|
||||
// amount,
|
||||
// mileage,
|
||||
// comment,
|
||||
// attachment
|
||||
// };
|
||||
// };
|
||||
|
||||
const findExactOld = async (norm: {
|
||||
bank_code_id: number;
|
||||
amount: Prisma.Decimal;
|
||||
mileage: number | null;
|
||||
comment: string;
|
||||
attachment: number | null;
|
||||
}) => {
|
||||
return tx.expenses.findFirst({
|
||||
where: {
|
||||
timesheet_id: id,
|
||||
date: date_only,
|
||||
bank_code_id: norm.bank_code_id,
|
||||
amount: norm.amount,
|
||||
comment: norm.comment,
|
||||
attachment: norm.attachment,
|
||||
...(norm.mileage !== null ? { mileage: norm.mileage } : { mileage: null }),
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
};
|
||||
// const findExactOld = async (norm: {
|
||||
// bank_code_id: number;
|
||||
// amount: Prisma.Decimal;
|
||||
// mileage: number | null;
|
||||
// comment: string;
|
||||
// attachment: number | null;
|
||||
// }) => {
|
||||
// return tx.expenses.findFirst({
|
||||
// where: {
|
||||
// timesheet_id: id,
|
||||
// date: date_only,
|
||||
// bank_code_id: norm.bank_code_id,
|
||||
// amount: norm.amount,
|
||||
// comment: norm.comment,
|
||||
// attachment: norm.attachment,
|
||||
// ...(norm.mileage !== null ? { mileage: norm.mileage } : { mileage: null }),
|
||||
// },
|
||||
// select: { id: true },
|
||||
// });
|
||||
// };
|
||||
|
||||
let action : UpsertAction;
|
||||
//_____________________________________________________________________________________________
|
||||
// DELETE
|
||||
//_____________________________________________________________________________________________
|
||||
if(old_expense && !new_expense) {
|
||||
const old_norm = await normalizePayload(old_expense);
|
||||
const existing = await findExactOld(old_norm);
|
||||
if(!existing) {
|
||||
throw new NotFoundException({
|
||||
error_code: 'EXPENSE_STALE',
|
||||
message: 'The expense was modified or deleted by someone else',
|
||||
});
|
||||
}
|
||||
await tx.expenses.delete({where: { id: existing.id } });
|
||||
action = 'delete';
|
||||
}
|
||||
//_____________________________________________________________________________________________
|
||||
// CREATE
|
||||
//_____________________________________________________________________________________________
|
||||
else if (!old_expense && new_expense) {
|
||||
const new_exp = await normalizePayload(new_expense);
|
||||
await tx.expenses.create({
|
||||
data: {
|
||||
timesheet_id: id,
|
||||
date: date_only,
|
||||
bank_code_id: new_exp.bank_code_id,
|
||||
amount: new_exp.amount,
|
||||
mileage: new_exp.mileage,
|
||||
comment: new_exp.comment,
|
||||
attachment: new_exp.attachment,
|
||||
is_approved: false,
|
||||
},
|
||||
});
|
||||
action = 'create';
|
||||
}
|
||||
//_____________________________________________________________________________________________
|
||||
// UPDATE
|
||||
//_____________________________________________________________________________________________
|
||||
else if(old_expense && new_expense) {
|
||||
const old_norm = await normalizePayload(old_expense);
|
||||
const existing = await findExactOld(old_norm);
|
||||
if(!existing) {
|
||||
throw new NotFoundException({
|
||||
error_code: 'EXPENSE_STALE',
|
||||
message: 'The expense was modified or deleted by someone else',
|
||||
});
|
||||
}
|
||||
// let action : UpsertAction;
|
||||
// //_____________________________________________________________________________________________
|
||||
// // DELETE
|
||||
// //_____________________________________________________________________________________________
|
||||
// if(old_expense && !new_expense) {
|
||||
// const old_norm = await normalizePayload(old_expense);
|
||||
// const existing = await findExactOld(old_norm);
|
||||
// if(!existing) {
|
||||
// throw new NotFoundException({
|
||||
// error_code: 'EXPENSE_STALE',
|
||||
// message: 'The expense was modified or deleted by someone else',
|
||||
// });
|
||||
// }
|
||||
// await tx.expenses.delete({where: { id: existing.id } });
|
||||
// action = 'delete';
|
||||
// }
|
||||
// //_____________________________________________________________________________________________
|
||||
// // CREATE
|
||||
// //_____________________________________________________________________________________________
|
||||
// else if (!old_expense && new_expense) {
|
||||
// const new_exp = await normalizePayload(new_expense);
|
||||
// await tx.expenses.create({
|
||||
// data: {
|
||||
// timesheet_id: id,
|
||||
// date: date_only,
|
||||
// bank_code_id: new_exp.bank_code_id,
|
||||
// amount: new_exp.amount,
|
||||
// mileage: new_exp.mileage,
|
||||
// comment: new_exp.comment,
|
||||
// attachment: new_exp.attachment,
|
||||
// is_approved: false,
|
||||
// },
|
||||
// });
|
||||
// action = 'create';
|
||||
// }
|
||||
// //_____________________________________________________________________________________________
|
||||
// // UPDATE
|
||||
// //_____________________________________________________________________________________________
|
||||
// else if(old_expense && new_expense) {
|
||||
// const old_norm = await normalizePayload(old_expense);
|
||||
// const existing = await findExactOld(old_norm);
|
||||
// if(!existing) {
|
||||
// throw new NotFoundException({
|
||||
// error_code: 'EXPENSE_STALE',
|
||||
// message: 'The expense was modified or deleted by someone else',
|
||||
// });
|
||||
// }
|
||||
|
||||
const new_exp = await normalizePayload(new_expense);
|
||||
await tx.expenses.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
bank_code_id: new_exp.bank_code_id,
|
||||
amount: new_exp.amount,
|
||||
mileage: new_exp.mileage,
|
||||
comment: new_exp.comment,
|
||||
attachment: new_exp.attachment,
|
||||
},
|
||||
});
|
||||
action = 'update';
|
||||
}
|
||||
else {
|
||||
throw new BadRequestException('Invalid upsert combination');
|
||||
}
|
||||
// const new_exp = await normalizePayload(new_expense);
|
||||
// await tx.expenses.update({
|
||||
// where: { id: existing.id },
|
||||
// data: {
|
||||
// bank_code_id: new_exp.bank_code_id,
|
||||
// amount: new_exp.amount,
|
||||
// mileage: new_exp.mileage,
|
||||
// comment: new_exp.comment,
|
||||
// attachment: new_exp.attachment,
|
||||
// },
|
||||
// });
|
||||
// action = 'update';
|
||||
// }
|
||||
// else {
|
||||
// throw new BadRequestException('Invalid upsert combination');
|
||||
// }
|
||||
|
||||
const day = await loadDay();
|
||||
// const day = await loadDay();
|
||||
|
||||
return { action, day };
|
||||
});
|
||||
}
|
||||
}
|
||||
// return { action, day };
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
|
@ -1,174 +1,171 @@
|
|||
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { DayExpensesDto as ExpenseListResponseDto, ExpenseDto } from "src/modules/timesheets/~misc_deprecated-files/timesheet-period.dto";
|
||||
import { round2, toUTCDateOnly } from "src/modules/timesheets/~misc_deprecated-files/utils-helpers-others/timesheet.helpers";
|
||||
import { EXPENSE_TYPES } from "src/modules/timesheets/~misc_deprecated-files/utils-helpers-others/timesheet.types";
|
||||
import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
|
||||
// import { Injectable, NotFoundException } from "@nestjs/common";
|
||||
// import { PrismaService } from "src/prisma/prisma.service";
|
||||
// import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
|
||||
|
||||
@Injectable()
|
||||
export class ExpensesQueryService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly employeeRepo: EmailToIdResolver,
|
||||
) {}
|
||||
// @Injectable()
|
||||
// export class ExpensesQueryService {
|
||||
// constructor(
|
||||
// private readonly prisma: PrismaService,
|
||||
// private readonly employeeRepo: EmailToIdResolver,
|
||||
// ) {}
|
||||
|
||||
|
||||
//fetchs all expenses for a selected employee using email, pay-period-year and number
|
||||
async findExpenseListByPayPeriodAndEmail(
|
||||
email: string,
|
||||
year: number,
|
||||
period_no: number
|
||||
): Promise<ExpenseListResponseDto> {
|
||||
//fetch employe_id using email
|
||||
const employee_id = await this.employeeRepo.findIdByEmail(email);
|
||||
if(!employee_id) throw new NotFoundException(`Employee with email: ${email} not found`);
|
||||
// //fetchs all expenses for a selected employee using email, pay-period-year and number
|
||||
// async findExpenseListByPayPeriodAndEmail(
|
||||
// email: string,
|
||||
// year: number,
|
||||
// period_no: number
|
||||
// ): Promise<ExpenseListResponseDto> {
|
||||
// //fetch employe_id using email
|
||||
// const employee_id = await this.employeeRepo.findIdByEmail(email);
|
||||
// if(!employee_id) throw new NotFoundException(`Employee with email: ${email} not found`);
|
||||
|
||||
//fetch pay-period using year and period_no
|
||||
const pay_period = await this.prisma.payPeriods.findFirst({
|
||||
where: {
|
||||
pay_year: year,
|
||||
pay_period_no: period_no
|
||||
},
|
||||
select: { period_start: true, period_end: true },
|
||||
});
|
||||
if(!pay_period) throw new NotFoundException(`Pay period ${year}- ${period_no} not found`);
|
||||
|
||||
const start = toUTCDateOnly(pay_period.period_start);
|
||||
const end = toUTCDateOnly(pay_period.period_end);
|
||||
|
||||
//sets rows data
|
||||
const rows = await this.prisma.expenses.findMany({
|
||||
where: {
|
||||
date: { gte: start, lte: end },
|
||||
timesheet: { is: { employee_id } },
|
||||
},
|
||||
orderBy: { date: 'asc'},
|
||||
select: {
|
||||
amount: true,
|
||||
mileage: true,
|
||||
comment: true,
|
||||
is_approved: true,
|
||||
supervisor_comment: true,
|
||||
bank_code: {select: { type: true } },
|
||||
},
|
||||
});
|
||||
|
||||
//declare return values
|
||||
const expenses: ExpenseDto[] = [];
|
||||
let total_amount = 0;
|
||||
let total_mileage = 0;
|
||||
|
||||
//set rows
|
||||
for(const row of rows) {
|
||||
const type = (row.bank_code?.type ?? '').toUpperCase();
|
||||
const amount = round2(Number(row.amount ?? 0));
|
||||
const mileage = round2(Number(row.mileage ?? 0));
|
||||
|
||||
if(type === EXPENSE_TYPES.MILEAGE) {
|
||||
total_mileage += mileage;
|
||||
} else {
|
||||
total_amount += amount;
|
||||
}
|
||||
|
||||
//fills rows array
|
||||
expenses.push({
|
||||
type,
|
||||
amount,
|
||||
mileage,
|
||||
comment: row.comment ?? '',
|
||||
is_approved: row.is_approved ?? false,
|
||||
supervisor_comment: row.supervisor_comment ?? '',
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
expenses,
|
||||
total_expense: round2(total_amount),
|
||||
total_mileage: round2(total_mileage),
|
||||
};
|
||||
}
|
||||
|
||||
//_____________________________________________________________________________________________
|
||||
// Deprecated or unused methods
|
||||
//_____________________________________________________________________________________________
|
||||
|
||||
// async create(dto: CreateExpenseDto): Promise<Expenses> {
|
||||
// const { timesheet_id, bank_code_id, date, amount:rawAmount,
|
||||
// comment, is_approved,supervisor_comment} = dto;
|
||||
// //fetches type and modifier
|
||||
// const bank_code = await this.prisma.bankCodes.findUnique({
|
||||
// where: { id: bank_code_id },
|
||||
// select: { type: true, modifier: true },
|
||||
// //fetch pay-period using year and period_no
|
||||
// const pay_period = await this.prisma.payPeriods.findFirst({
|
||||
// where: {
|
||||
// pay_year: year,
|
||||
// pay_period_no: period_no
|
||||
// },
|
||||
// select: { period_start: true, period_end: true },
|
||||
// });
|
||||
// if(!bank_code) throw new NotFoundException(`bank_code #${bank_code_id} not found`);
|
||||
// if(!pay_period) throw new NotFoundException(`Pay period ${year}- ${period_no} not found`);
|
||||
|
||||
// //if mileage -> service, otherwise the ratio is amount:1
|
||||
// let final_amount: number;
|
||||
// if(bank_code.type === 'mileage') {
|
||||
// final_amount = await this.mileageService.calculateReimbursement(rawAmount, bank_code_id);
|
||||
// const start = toUTCDateOnly(pay_period.period_start);
|
||||
// const end = toUTCDateOnly(pay_period.period_end);
|
||||
|
||||
// //sets rows data
|
||||
// const rows = await this.prisma.expenses.findMany({
|
||||
// where: {
|
||||
// date: { gte: start, lte: end },
|
||||
// timesheet: { is: { employee_id } },
|
||||
// },
|
||||
// orderBy: { date: 'asc'},
|
||||
// select: {
|
||||
// amount: true,
|
||||
// mileage: true,
|
||||
// comment: true,
|
||||
// is_approved: true,
|
||||
// supervisor_comment: true,
|
||||
// bank_code: {select: { type: true } },
|
||||
// },
|
||||
// });
|
||||
|
||||
// //declare return values
|
||||
// const expenses: ExpenseDto[] = [];
|
||||
// let total_amount = 0;
|
||||
// let total_mileage = 0;
|
||||
|
||||
// //set rows
|
||||
// for(const row of rows) {
|
||||
// const type = (row.bank_code?.type ?? '').toUpperCase();
|
||||
// const amount = round2(Number(row.amount ?? 0));
|
||||
// const mileage = round2(Number(row.mileage ?? 0));
|
||||
|
||||
// if(type === EXPENSE_TYPES.MILEAGE) {
|
||||
// total_mileage += mileage;
|
||||
// } else {
|
||||
// final_amount = parseFloat( (rawAmount * bank_code.modifier).toFixed(2));
|
||||
// total_amount += amount;
|
||||
// }
|
||||
|
||||
// return this.prisma.expenses.create({
|
||||
// data: {
|
||||
// timesheet_id,
|
||||
// bank_code_id,
|
||||
// date,
|
||||
// amount: final_amount,
|
||||
// comment,
|
||||
// is_approved,
|
||||
// supervisor_comment
|
||||
// },
|
||||
// include: { timesheet: { include: { employee: { include: { user: true }}}},
|
||||
// bank_code: true,
|
||||
// },
|
||||
// })
|
||||
// }
|
||||
|
||||
// async findAll(filters: SearchExpensesDto): Promise<Expenses[]> {
|
||||
// const where = buildPrismaWhere(filters);
|
||||
// const expenses = await this.prisma.expenses.findMany({ where })
|
||||
// return expenses;
|
||||
// }
|
||||
|
||||
// async findOne(id: number): Promise<Expenses> {
|
||||
// const expense = await this.prisma.expenses.findUnique({
|
||||
// where: { id },
|
||||
// include: { timesheet: { include: { employee: { include: { user:true } } } },
|
||||
// bank_code: true,
|
||||
// },
|
||||
// });
|
||||
// if (!expense) {
|
||||
// throw new NotFoundException(`Expense #${id} not found`);
|
||||
// }
|
||||
// return expense;
|
||||
// }
|
||||
|
||||
// async update(id: number, dto: UpdateExpenseDto): Promise<Expenses> {
|
||||
// await this.findOne(id);
|
||||
// const { timesheet_id, bank_code_id, date, amount,
|
||||
// comment, is_approved, supervisor_comment} = dto;
|
||||
// return this.prisma.expenses.update({
|
||||
// where: { id },
|
||||
// data: {
|
||||
// ...(timesheet_id !== undefined && { timesheet_id}),
|
||||
// ...(bank_code_id !== undefined && { bank_code_id }),
|
||||
// ...(date !== undefined && { date }),
|
||||
// ...(amount !== undefined && { amount }),
|
||||
// ...(comment !== undefined && { comment }),
|
||||
// ...(is_approved !== undefined && { is_approved }),
|
||||
// ...(supervisor_comment !== undefined && { supervisor_comment }),
|
||||
// },
|
||||
// include: { timesheet: { include: { employee: { include: { user: true } } } },
|
||||
// bank_code: true,
|
||||
// },
|
||||
// //fills rows array
|
||||
// expenses.push({
|
||||
// type,
|
||||
// amount,
|
||||
// mileage,
|
||||
// comment: row.comment ?? '',
|
||||
// is_approved: row.is_approved ?? false,
|
||||
// supervisor_comment: row.supervisor_comment ?? '',
|
||||
// });
|
||||
// }
|
||||
|
||||
// async remove(id: number): Promise<Expenses> {
|
||||
// await this.findOne(id);
|
||||
// return this.prisma.expenses.delete({ where: { id } });
|
||||
// return {
|
||||
// expenses,
|
||||
// total_expense: round2(total_amount),
|
||||
// total_mileage: round2(total_mileage),
|
||||
// };
|
||||
// }
|
||||
|
||||
}
|
||||
// //_____________________________________________________________________________________________
|
||||
// // Deprecated or unused methods
|
||||
// //_____________________________________________________________________________________________
|
||||
|
||||
// // async create(dto: CreateExpenseDto): Promise<Expenses> {
|
||||
// // const { timesheet_id, bank_code_id, date, amount:rawAmount,
|
||||
// // comment, is_approved,supervisor_comment} = dto;
|
||||
// // //fetches type and modifier
|
||||
// // const bank_code = await this.prisma.bankCodes.findUnique({
|
||||
// // where: { id: bank_code_id },
|
||||
// // select: { type: true, modifier: true },
|
||||
// // });
|
||||
// // if(!bank_code) throw new NotFoundException(`bank_code #${bank_code_id} not found`);
|
||||
|
||||
// // //if mileage -> service, otherwise the ratio is amount:1
|
||||
// // let final_amount: number;
|
||||
// // if(bank_code.type === 'mileage') {
|
||||
// // final_amount = await this.mileageService.calculateReimbursement(rawAmount, bank_code_id);
|
||||
// // }else {
|
||||
// // final_amount = parseFloat( (rawAmount * bank_code.modifier).toFixed(2));
|
||||
// // }
|
||||
|
||||
// // return this.prisma.expenses.create({
|
||||
// // data: {
|
||||
// // timesheet_id,
|
||||
// // bank_code_id,
|
||||
// // date,
|
||||
// // amount: final_amount,
|
||||
// // comment,
|
||||
// // is_approved,
|
||||
// // supervisor_comment
|
||||
// // },
|
||||
// // include: { timesheet: { include: { employee: { include: { user: true }}}},
|
||||
// // bank_code: true,
|
||||
// // },
|
||||
// // })
|
||||
// // }
|
||||
|
||||
// // async findAll(filters: SearchExpensesDto): Promise<Expenses[]> {
|
||||
// // const where = buildPrismaWhere(filters);
|
||||
// // const expenses = await this.prisma.expenses.findMany({ where })
|
||||
// // return expenses;
|
||||
// // }
|
||||
|
||||
// // async findOne(id: number): Promise<Expenses> {
|
||||
// // const expense = await this.prisma.expenses.findUnique({
|
||||
// // where: { id },
|
||||
// // include: { timesheet: { include: { employee: { include: { user:true } } } },
|
||||
// // bank_code: true,
|
||||
// // },
|
||||
// // });
|
||||
// // if (!expense) {
|
||||
// // throw new NotFoundException(`Expense #${id} not found`);
|
||||
// // }
|
||||
// // return expense;
|
||||
// // }
|
||||
|
||||
// // async update(id: number, dto: UpdateExpenseDto): Promise<Expenses> {
|
||||
// // await this.findOne(id);
|
||||
// // const { timesheet_id, bank_code_id, date, amount,
|
||||
// // comment, is_approved, supervisor_comment} = dto;
|
||||
// // return this.prisma.expenses.update({
|
||||
// // where: { id },
|
||||
// // data: {
|
||||
// // ...(timesheet_id !== undefined && { timesheet_id}),
|
||||
// // ...(bank_code_id !== undefined && { bank_code_id }),
|
||||
// // ...(date !== undefined && { date }),
|
||||
// // ...(amount !== undefined && { amount }),
|
||||
// // ...(comment !== undefined && { comment }),
|
||||
// // ...(is_approved !== undefined && { is_approved }),
|
||||
// // ...(supervisor_comment !== undefined && { supervisor_comment }),
|
||||
// // },
|
||||
// // include: { timesheet: { include: { employee: { include: { user: true } } } },
|
||||
// // bank_code: true,
|
||||
// // },
|
||||
// // });
|
||||
// // }
|
||||
|
||||
// // async remove(id: number): Promise<Expenses> {
|
||||
// // await this.findOne(id);
|
||||
// // return this.prisma.expenses.delete({ where: { id } });
|
||||
// // }
|
||||
|
||||
// }
|
||||
|
|
@ -1,30 +1,30 @@
|
|||
import { Body, Controller, Post } from "@nestjs/common";
|
||||
import { ApiBearerAuth, ApiTags } from "@nestjs/swagger";
|
||||
import { LeaveRequestsService } from "../services/leave-request.service";
|
||||
import { UpsertLeaveRequestDto } from "../dtos/upsert-leave-request.dto";
|
||||
import { LeaveTypes } from "@prisma/client";
|
||||
// import { Body, Controller, Post } from "@nestjs/common";
|
||||
// import { ApiBearerAuth, ApiTags } from "@nestjs/swagger";
|
||||
// import { LeaveRequestsService } from "../services/leave-request.service";
|
||||
// import { UpsertLeaveRequestDto } from "../dtos/upsert-leave-request.dto";
|
||||
// import { LeaveTypes } from "@prisma/client";
|
||||
|
||||
@ApiTags('Leave Requests')
|
||||
@ApiBearerAuth('access-token')
|
||||
// @UseGuards()
|
||||
@Controller('leave-requests')
|
||||
export class LeaveRequestController {
|
||||
constructor(private readonly leave_service: LeaveRequestsService){}
|
||||
// @ApiTags('Leave Requests')
|
||||
// @ApiBearerAuth('access-token')
|
||||
// // @UseGuards()
|
||||
// @Controller('leave-requests')
|
||||
// export class LeaveRequestController {
|
||||
// constructor(private readonly leave_service: LeaveRequestsService){}
|
||||
|
||||
@Post('upsert')
|
||||
async upsertLeaveRequest(@Body() dto: UpsertLeaveRequestDto) {
|
||||
const { action, leave_requests } = await this.leave_service.handle(dto);
|
||||
return { action, leave_requests };
|
||||
}q
|
||||
// @Post('upsert')
|
||||
// async upsertLeaveRequest(@Body() dto: UpsertLeaveRequestDto) {
|
||||
// const { action, leave_requests } = await this.leave_service.handle(dto);
|
||||
// return { action, leave_requests };
|
||||
// }q
|
||||
|
||||
//TODO:
|
||||
/*
|
||||
@Get('archive')
|
||||
findAllArchived(){...}
|
||||
// //TODO:
|
||||
// /*
|
||||
// @Get('archive')
|
||||
// findAllArchived(){...}
|
||||
|
||||
@Get('archive/:id')
|
||||
findOneArchived(id){...}
|
||||
*/
|
||||
// @Get('archive/:id')
|
||||
// findOneArchived(id){...}
|
||||
// */
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,29 +1,29 @@
|
|||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { LeaveRequestController } from "./controllers/leave-requests.controller";
|
||||
import { HolidayLeaveRequestsService } from "./services/holiday-leave-requests.service";
|
||||
import { Module } from "@nestjs/common";
|
||||
import { BusinessLogicsModule } from "src/modules/business-logics/business-logics.module";
|
||||
import { VacationLeaveRequestsService } from "./services/vacation-leave-requests.service";
|
||||
import { SickLeaveRequestsService } from "./services/sick-leave-requests.service";
|
||||
import { LeaveRequestsService } from "./services/leave-request.service";
|
||||
import { ShiftsModule } from "../shifts/shifts.module";
|
||||
import { LeaveRequestsUtils } from "./utils/leave-request.util";
|
||||
import { SharedModule } from "../shared/shared.module";
|
||||
// import { PrismaService } from "src/prisma/prisma.service";
|
||||
// import { LeaveRequestController } from "./controllers/leave-requests.controller";
|
||||
// import { HolidayLeaveRequestsService } from "./services/holiday-leave-requests.service";
|
||||
// import { Module } from "@nestjs/common";
|
||||
// import { BusinessLogicsModule } from "src/modules/business-logics/business-logics.module";
|
||||
// import { VacationLeaveRequestsService } from "./services/vacation-leave-requests.service";
|
||||
// import { SickLeaveRequestsService } from "./services/sick-leave-requests.service";
|
||||
// import { LeaveRequestsService } from "./services/leave-request.service";
|
||||
// import { ShiftsModule } from "../shifts/shifts.module";
|
||||
// import { LeaveRequestsUtils } from "./utils/leave-request.util";
|
||||
// import { SharedModule } from "../shared/shared.module";
|
||||
|
||||
@Module({
|
||||
imports: [BusinessLogicsModule, ShiftsModule, SharedModule],
|
||||
controllers: [LeaveRequestController],
|
||||
providers: [
|
||||
VacationLeaveRequestsService,
|
||||
SickLeaveRequestsService,
|
||||
HolidayLeaveRequestsService,
|
||||
LeaveRequestsService,
|
||||
PrismaService,
|
||||
LeaveRequestsUtils,
|
||||
],
|
||||
exports: [
|
||||
LeaveRequestsService,
|
||||
],
|
||||
})
|
||||
// @Module({
|
||||
// imports: [BusinessLogicsModule, ShiftsModule, SharedModule],
|
||||
// controllers: [LeaveRequestController],
|
||||
// providers: [
|
||||
// VacationLeaveRequestsService,
|
||||
// SickLeaveRequestsService,
|
||||
// HolidayLeaveRequestsService,
|
||||
// LeaveRequestsService,
|
||||
// PrismaService,
|
||||
// LeaveRequestsUtils,
|
||||
// ],
|
||||
// exports: [
|
||||
// LeaveRequestsService,
|
||||
// ],
|
||||
// })
|
||||
|
||||
export class LeaveRequestsModule {}
|
||||
// export class LeaveRequestsModule {}
|
||||
|
|
@ -1,78 +1,78 @@
|
|||
import { UpsertLeaveRequestDto, UpsertResult } from '../dtos/upsert-leave-request.dto';
|
||||
import { LeaveRequestViewDto } from '../dtos/leave-request-view.dto';
|
||||
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { LeaveApprovalStatus, LeaveTypes } from '@prisma/client';
|
||||
import { HolidayService } from 'src/modules/business-logics/services/holiday.service';
|
||||
import { PrismaService } from 'src/prisma/prisma.service';
|
||||
import { mapRowToView } from '../mappers/leave-requests.mapper';
|
||||
import { leaveRequestsSelect } from '../utils/leave-requests.select';
|
||||
import { LeaveRequestsUtils} from '../utils/leave-request.util';
|
||||
import { normalizeDates, toDateOnly } from 'src/modules/shared/helpers/date-time.helpers';
|
||||
import { BankCodesResolver } from 'src/modules/shared/utils/resolve-bank-type-id.utils';
|
||||
import { EmailToIdResolver } from 'src/modules/shared/utils/resolve-email-id.utils';
|
||||
// import { UpsertLeaveRequestDto, UpsertResult } from '../dtos/upsert-leave-request.dto';
|
||||
// import { LeaveRequestViewDto } from '../dtos/leave-request-view.dto';
|
||||
// import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
// import { LeaveApprovalStatus, LeaveTypes } from '@prisma/client';
|
||||
// import { HolidayService } from 'src/modules/business-logics/services/holiday.service';
|
||||
// import { PrismaService } from 'src/prisma/prisma.service';
|
||||
// import { mapRowToView } from '../mappers/leave-requests.mapper';
|
||||
// import { leaveRequestsSelect } from '../utils/leave-requests.select';
|
||||
// import { LeaveRequestsUtils} from '../utils/leave-request.util';
|
||||
// import { normalizeDates, toDateOnly } from 'src/modules/shared/helpers/date-time.helpers';
|
||||
// import { BankCodesResolver } from 'src/modules/shared/utils/resolve-bank-type-id.utils';
|
||||
// import { EmailToIdResolver } from 'src/modules/shared/utils/resolve-email-id.utils';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class HolidayLeaveRequestsService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly holidayService: HolidayService,
|
||||
private readonly leaveUtils: LeaveRequestsUtils,
|
||||
private readonly emailResolver: EmailToIdResolver,
|
||||
private readonly typeResolver: BankCodesResolver,
|
||||
) {}
|
||||
// @Injectable()
|
||||
// export class HolidayLeaveRequestsService {
|
||||
// constructor(
|
||||
// private readonly prisma: PrismaService,
|
||||
// private readonly holidayService: HolidayService,
|
||||
// private readonly leaveUtils: LeaveRequestsUtils,
|
||||
// private readonly emailResolver: EmailToIdResolver,
|
||||
// private readonly typeResolver: BankCodesResolver,
|
||||
// ) {}
|
||||
|
||||
async create(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
|
||||
const email = dto.email.trim();
|
||||
const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||
const bank_code = await this.typeResolver.findByType(LeaveTypes.HOLIDAY);
|
||||
if(!bank_code) throw new NotFoundException(`bank_code not found`);
|
||||
const dates = normalizeDates(dto.dates);
|
||||
if (!dates.length) throw new BadRequestException('Dates array must not be empty');
|
||||
// async create(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
|
||||
// const email = dto.email.trim();
|
||||
// const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||
// const bank_code = await this.typeResolver.findByType(LeaveTypes.HOLIDAY);
|
||||
// if(!bank_code) throw new NotFoundException(`bank_code not found`);
|
||||
// const dates = normalizeDates(dto.dates);
|
||||
// if (!dates.length) throw new BadRequestException('Dates array must not be empty');
|
||||
|
||||
const created: LeaveRequestViewDto[] = [];
|
||||
// const created: LeaveRequestViewDto[] = [];
|
||||
|
||||
for (const iso_date of dates) {
|
||||
const date = toDateOnly(iso_date);
|
||||
// for (const iso_date of dates) {
|
||||
// const date = toDateOnly(iso_date);
|
||||
|
||||
const existing = await this.prisma.leaveRequests.findUnique({
|
||||
where: {
|
||||
leave_per_employee_date: {
|
||||
employee_id: employee_id,
|
||||
leave_type: LeaveTypes.HOLIDAY,
|
||||
date,
|
||||
},
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
if (existing) {
|
||||
throw new BadRequestException(`Holiday request already exists for ${iso_date}`);
|
||||
}
|
||||
// const existing = await this.prisma.leaveRequests.findUnique({
|
||||
// where: {
|
||||
// leave_per_employee_date: {
|
||||
// employee_id: employee_id,
|
||||
// leave_type: LeaveTypes.HOLIDAY,
|
||||
// date,
|
||||
// },
|
||||
// },
|
||||
// select: { id: true },
|
||||
// });
|
||||
// if (existing) {
|
||||
// throw new BadRequestException(`Holiday request already exists for ${iso_date}`);
|
||||
// }
|
||||
|
||||
const payable = await this.holidayService.calculateHolidayPay(email, date, bank_code.modifier);
|
||||
const row = await this.prisma.leaveRequests.create({
|
||||
data: {
|
||||
employee_id: employee_id,
|
||||
bank_code_id: bank_code.id,
|
||||
leave_type: LeaveTypes.HOLIDAY,
|
||||
date,
|
||||
comment: dto.comment ?? '',
|
||||
requested_hours: dto.requested_hours ?? 8,
|
||||
payable_hours: payable,
|
||||
approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING,
|
||||
},
|
||||
select: leaveRequestsSelect,
|
||||
});
|
||||
// const payable = await this.holidayService.calculateHolidayPay(email, date, bank_code.modifier);
|
||||
// const row = await this.prisma.leaveRequests.create({
|
||||
// data: {
|
||||
// employee_id: employee_id,
|
||||
// bank_code_id: bank_code.id,
|
||||
// leave_type: LeaveTypes.HOLIDAY,
|
||||
// date,
|
||||
// comment: dto.comment ?? '',
|
||||
// requested_hours: dto.requested_hours ?? 8,
|
||||
// payable_hours: payable,
|
||||
// approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING,
|
||||
// },
|
||||
// select: leaveRequestsSelect,
|
||||
// });
|
||||
|
||||
const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
|
||||
if (row.approval_status === LeaveApprovalStatus.APPROVED) {
|
||||
await this.leaveUtils.syncShift(email, employee_id, iso_date, hours,LeaveTypes.HOLIDAY, row.comment);
|
||||
}
|
||||
// const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
|
||||
// if (row.approval_status === LeaveApprovalStatus.APPROVED) {
|
||||
// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours,LeaveTypes.HOLIDAY, row.comment);
|
||||
// }
|
||||
|
||||
created.push({ ...mapRowToView(row), action: 'create' });
|
||||
}
|
||||
// created.push({ ...mapRowToView(row), action: 'create' });
|
||||
// }
|
||||
|
||||
return { action: 'create', leave_requests: created };
|
||||
}
|
||||
}
|
||||
// return { action: 'create', leave_requests: created };
|
||||
// }
|
||||
// }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,248 +1,248 @@
|
|||
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||
import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client";
|
||||
import { roundToQuarterHour } from "src/common/utils/date-utils";
|
||||
import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto";
|
||||
import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto";
|
||||
import { mapRowToView } from "../mappers/leave-requests.mapper";
|
||||
import { leaveRequestsSelect } from "../utils/leave-requests.select";
|
||||
import { HolidayLeaveRequestsService } from "./holiday-leave-requests.service";
|
||||
import { SickLeaveRequestsService } from "./sick-leave-requests.service";
|
||||
import { VacationLeaveRequestsService } from "./vacation-leave-requests.service";
|
||||
import { HolidayService } from "src/modules/business-logics/services/holiday.service";
|
||||
import { SickLeaveService } from "src/modules/business-logics/services/sick-leave.service";
|
||||
import { VacationService } from "src/modules/business-logics/services/vacation.service";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { LeaveRequestsUtils } from "../utils/leave-request.util";
|
||||
import { normalizeDates, toDateOnly, toISODateKey } from "src/modules/shared/helpers/date-time.helpers";
|
||||
import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
|
||||
import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils";
|
||||
// import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||
// import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client";
|
||||
// import { roundToQuarterHour } from "src/common/utils/date-utils";
|
||||
// import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto";
|
||||
// import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto";
|
||||
// import { mapRowToView } from "../mappers/leave-requests.mapper";
|
||||
// import { leaveRequestsSelect } from "../utils/leave-requests.select";
|
||||
// import { HolidayLeaveRequestsService } from "./holiday-leave-requests.service";
|
||||
// import { SickLeaveRequestsService } from "./sick-leave-requests.service";
|
||||
// import { VacationLeaveRequestsService } from "./vacation-leave-requests.service";
|
||||
// import { HolidayService } from "src/modules/business-logics/services/holiday.service";
|
||||
// import { SickLeaveService } from "src/modules/business-logics/services/sick-leave.service";
|
||||
// import { VacationService } from "src/modules/business-logics/services/vacation.service";
|
||||
// import { PrismaService } from "src/prisma/prisma.service";
|
||||
// import { LeaveRequestsUtils } from "../utils/leave-request.util";
|
||||
// import { normalizeDates, toDateOnly, toISODateKey } from "src/modules/shared/helpers/date-time.helpers";
|
||||
// import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
|
||||
// import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils";
|
||||
|
||||
@Injectable()
|
||||
export class LeaveRequestsService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly holidayLeaveService: HolidayLeaveRequestsService,
|
||||
private readonly holidayService: HolidayService,
|
||||
private readonly sickLogic: SickLeaveService,
|
||||
private readonly sickLeaveService: SickLeaveRequestsService,
|
||||
private readonly vacationLeaveService: VacationLeaveRequestsService,
|
||||
private readonly vacationLogic: VacationService,
|
||||
private readonly leaveUtils: LeaveRequestsUtils,
|
||||
private readonly emailResolver: EmailToIdResolver,
|
||||
private readonly typeResolver: BankCodesResolver,
|
||||
) {}
|
||||
// @Injectable()
|
||||
// export class LeaveRequestsService {
|
||||
// constructor(
|
||||
// private readonly prisma: PrismaService,
|
||||
// private readonly holidayLeaveService: HolidayLeaveRequestsService,
|
||||
// private readonly holidayService: HolidayService,
|
||||
// private readonly sickLogic: SickLeaveService,
|
||||
// private readonly sickLeaveService: SickLeaveRequestsService,
|
||||
// private readonly vacationLeaveService: VacationLeaveRequestsService,
|
||||
// private readonly vacationLogic: VacationService,
|
||||
// private readonly leaveUtils: LeaveRequestsUtils,
|
||||
// private readonly emailResolver: EmailToIdResolver,
|
||||
// private readonly typeResolver: BankCodesResolver,
|
||||
// ) {}
|
||||
|
||||
//handle distribution to the right service according to the selected type and action
|
||||
async handle(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
|
||||
switch (dto.type) {
|
||||
case LeaveTypes.HOLIDAY:
|
||||
if( dto.action === 'create'){
|
||||
return this.holidayLeaveService.create(dto);
|
||||
} else if (dto.action === 'update') {
|
||||
return this.update(dto, LeaveTypes.HOLIDAY);
|
||||
} else if (dto.action === 'delete'){
|
||||
return this.delete(dto, LeaveTypes.HOLIDAY);
|
||||
}
|
||||
case LeaveTypes.VACATION:
|
||||
if( dto.action === 'create'){
|
||||
return this.vacationLeaveService.create(dto);
|
||||
} else if (dto.action === 'update') {
|
||||
return this.update(dto, LeaveTypes.VACATION);
|
||||
} else if (dto.action === 'delete'){
|
||||
return this.delete(dto, LeaveTypes.VACATION);
|
||||
}
|
||||
case LeaveTypes.SICK:
|
||||
if( dto.action === 'create'){
|
||||
return this.sickLeaveService.create(dto);
|
||||
} else if (dto.action === 'update') {
|
||||
return this.update(dto, LeaveTypes.SICK);
|
||||
} else if (dto.action === 'delete'){
|
||||
return this.delete(dto, LeaveTypes.SICK);
|
||||
}
|
||||
default:
|
||||
throw new BadRequestException(`Unsupported leave type: ${dto.type} or action: ${dto.action}`);
|
||||
}
|
||||
}
|
||||
// //handle distribution to the right service according to the selected type and action
|
||||
// async handle(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
|
||||
// switch (dto.type) {
|
||||
// case LeaveTypes.HOLIDAY:
|
||||
// if( dto.action === 'create'){
|
||||
// return this.holidayLeaveService.create(dto);
|
||||
// } else if (dto.action === 'update') {
|
||||
// return this.update(dto, LeaveTypes.HOLIDAY);
|
||||
// } else if (dto.action === 'delete'){
|
||||
// return this.delete(dto, LeaveTypes.HOLIDAY);
|
||||
// }
|
||||
// case LeaveTypes.VACATION:
|
||||
// if( dto.action === 'create'){
|
||||
// return this.vacationLeaveService.create(dto);
|
||||
// } else if (dto.action === 'update') {
|
||||
// return this.update(dto, LeaveTypes.VACATION);
|
||||
// } else if (dto.action === 'delete'){
|
||||
// return this.delete(dto, LeaveTypes.VACATION);
|
||||
// }
|
||||
// case LeaveTypes.SICK:
|
||||
// if( dto.action === 'create'){
|
||||
// return this.sickLeaveService.create(dto);
|
||||
// } else if (dto.action === 'update') {
|
||||
// return this.update(dto, LeaveTypes.SICK);
|
||||
// } else if (dto.action === 'delete'){
|
||||
// return this.delete(dto, LeaveTypes.SICK);
|
||||
// }
|
||||
// default:
|
||||
// throw new BadRequestException(`Unsupported leave type: ${dto.type} or action: ${dto.action}`);
|
||||
// }
|
||||
// }
|
||||
|
||||
async delete(dto: UpsertLeaveRequestDto, type: LeaveTypes): Promise<UpsertResult> {
|
||||
const email = dto.email.trim();
|
||||
const dates = normalizeDates(dto.dates);
|
||||
const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||
if (!dates.length) throw new BadRequestException("Dates array must not be empty");
|
||||
// async delete(dto: UpsertLeaveRequestDto, type: LeaveTypes): Promise<UpsertResult> {
|
||||
// const email = dto.email.trim();
|
||||
// const dates = normalizeDates(dto.dates);
|
||||
// const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||
// if (!dates.length) throw new BadRequestException("Dates array must not be empty");
|
||||
|
||||
const rows = await this.prisma.leaveRequests.findMany({
|
||||
where: {
|
||||
employee_id: employee_id,
|
||||
leave_type: type,
|
||||
date: { in: dates.map((d) => toDateOnly(d)) },
|
||||
},
|
||||
select: leaveRequestsSelect,
|
||||
});
|
||||
// const rows = await this.prisma.leaveRequests.findMany({
|
||||
// where: {
|
||||
// employee_id: employee_id,
|
||||
// leave_type: type,
|
||||
// date: { in: dates.map((d) => toDateOnly(d)) },
|
||||
// },
|
||||
// select: leaveRequestsSelect,
|
||||
// });
|
||||
|
||||
if (rows.length !== dates.length) {
|
||||
const missing = dates.filter((isoDate) => !rows.some((row) => toISODateKey(row.date) === isoDate));
|
||||
throw new NotFoundException(`No Leave request found for: ${missing.join(", ")}`);
|
||||
}
|
||||
// if (rows.length !== dates.length) {
|
||||
// const missing = dates.filter((isoDate) => !rows.some((row) => toISODateKey(row.date) === isoDate));
|
||||
// throw new NotFoundException(`No Leave request found for: ${missing.join(", ")}`);
|
||||
// }
|
||||
|
||||
for (const row of rows) {
|
||||
if (row.approval_status === LeaveApprovalStatus.APPROVED) {
|
||||
const iso = toISODateKey(row.date);
|
||||
await this.leaveUtils.removeShift(email, employee_id, iso, type);
|
||||
}
|
||||
}
|
||||
// for (const row of rows) {
|
||||
// if (row.approval_status === LeaveApprovalStatus.APPROVED) {
|
||||
// const iso = toISODateKey(row.date);
|
||||
// await this.leaveUtils.removeShift(email, employee_id, iso, type);
|
||||
// }
|
||||
// }
|
||||
|
||||
await this.prisma.leaveRequests.deleteMany({
|
||||
where: { id: { in: rows.map((row) => row.id) } },
|
||||
});
|
||||
// await this.prisma.leaveRequests.deleteMany({
|
||||
// where: { id: { in: rows.map((row) => row.id) } },
|
||||
// });
|
||||
|
||||
const deleted = rows.map((row) => ({ ...mapRowToView(row), action: "delete" as const }));
|
||||
return { action: "delete", leave_requests: deleted };
|
||||
}
|
||||
// const deleted = rows.map((row) => ({ ...mapRowToView(row), action: "delete" as const }));
|
||||
// return { action: "delete", leave_requests: deleted };
|
||||
// }
|
||||
|
||||
async update(dto: UpsertLeaveRequestDto, type: LeaveTypes): Promise<UpsertResult> {
|
||||
const email = dto.email.trim();
|
||||
const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||
const bank_code = await this.typeResolver.findByType(type);
|
||||
if(!bank_code) throw new NotFoundException(`bank_code not found`);
|
||||
const modifier = Number(bank_code.modifier ?? 1);
|
||||
const dates = normalizeDates(dto.dates);
|
||||
if (!dates.length) {
|
||||
throw new BadRequestException("Dates array must not be empty");
|
||||
}
|
||||
// async update(dto: UpsertLeaveRequestDto, type: LeaveTypes): Promise<UpsertResult> {
|
||||
// const email = dto.email.trim();
|
||||
// const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||
// const bank_code = await this.typeResolver.findByType(type);
|
||||
// if(!bank_code) throw new NotFoundException(`bank_code not found`);
|
||||
// const modifier = Number(bank_code.modifier ?? 1);
|
||||
// const dates = normalizeDates(dto.dates);
|
||||
// if (!dates.length) {
|
||||
// throw new BadRequestException("Dates array must not be empty");
|
||||
// }
|
||||
|
||||
const entries = await Promise.all(
|
||||
dates.map(async (iso_date) => {
|
||||
const date = toDateOnly(iso_date);
|
||||
const existing = await this.prisma.leaveRequests.findUnique({
|
||||
where: {
|
||||
leave_per_employee_date: {
|
||||
employee_id: employee_id,
|
||||
leave_type: type,
|
||||
date,
|
||||
},
|
||||
},
|
||||
select: leaveRequestsSelect,
|
||||
});
|
||||
if (!existing) throw new NotFoundException(`No Leave request found for ${iso_date}`);
|
||||
return { iso_date, date, existing };
|
||||
}),
|
||||
);
|
||||
// const entries = await Promise.all(
|
||||
// dates.map(async (iso_date) => {
|
||||
// const date = toDateOnly(iso_date);
|
||||
// const existing = await this.prisma.leaveRequests.findUnique({
|
||||
// where: {
|
||||
// leave_per_employee_date: {
|
||||
// employee_id: employee_id,
|
||||
// leave_type: type,
|
||||
// date,
|
||||
// },
|
||||
// },
|
||||
// select: leaveRequestsSelect,
|
||||
// });
|
||||
// if (!existing) throw new NotFoundException(`No Leave request found for ${iso_date}`);
|
||||
// return { iso_date, date, existing };
|
||||
// }),
|
||||
// );
|
||||
|
||||
const updated: LeaveRequestViewDto[] = [];
|
||||
// const updated: LeaveRequestViewDto[] = [];
|
||||
|
||||
if (type === LeaveTypes.SICK) {
|
||||
const firstExisting = entries[0].existing;
|
||||
const fallbackRequested =
|
||||
firstExisting.requested_hours !== null && firstExisting.requested_hours !== undefined
|
||||
? Number(firstExisting.requested_hours)
|
||||
: 8;
|
||||
const requested_hours_per_day = dto.requested_hours ?? fallbackRequested;
|
||||
const reference_date = entries.reduce(
|
||||
(latest, entry) => (entry.date > latest ? entry.date : latest),
|
||||
entries[0].date,
|
||||
);
|
||||
const total_payable_hours = await this.sickLogic.calculateSickLeavePay(
|
||||
employee_id,
|
||||
reference_date,
|
||||
entries.length,
|
||||
requested_hours_per_day,
|
||||
modifier,
|
||||
);
|
||||
let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours));
|
||||
const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier);
|
||||
// if (type === LeaveTypes.SICK) {
|
||||
// const firstExisting = entries[0].existing;
|
||||
// const fallbackRequested =
|
||||
// firstExisting.requested_hours !== null && firstExisting.requested_hours !== undefined
|
||||
// ? Number(firstExisting.requested_hours)
|
||||
// : 8;
|
||||
// const requested_hours_per_day = dto.requested_hours ?? fallbackRequested;
|
||||
// const reference_date = entries.reduce(
|
||||
// (latest, entry) => (entry.date > latest ? entry.date : latest),
|
||||
// entries[0].date,
|
||||
// );
|
||||
// const total_payable_hours = await this.sickLogic.calculateSickLeavePay(
|
||||
// employee_id,
|
||||
// reference_date,
|
||||
// entries.length,
|
||||
// requested_hours_per_day,
|
||||
// modifier,
|
||||
// );
|
||||
// let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours));
|
||||
// const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier);
|
||||
|
||||
for (const { iso_date, existing } of entries) {
|
||||
const previous_status = existing.approval_status;
|
||||
const payable = Math.min(remaining_payable_hours, daily_payable_cap);
|
||||
const payable_rounded = roundToQuarterHour(Math.max(0, payable));
|
||||
remaining_payable_hours = roundToQuarterHour(
|
||||
Math.max(0, remaining_payable_hours - payable_rounded),
|
||||
);
|
||||
// for (const { iso_date, existing } of entries) {
|
||||
// const previous_status = existing.approval_status;
|
||||
// const payable = Math.min(remaining_payable_hours, daily_payable_cap);
|
||||
// const payable_rounded = roundToQuarterHour(Math.max(0, payable));
|
||||
// remaining_payable_hours = roundToQuarterHour(
|
||||
// Math.max(0, remaining_payable_hours - payable_rounded),
|
||||
// );
|
||||
|
||||
const row = await this.prisma.leaveRequests.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
comment: dto.comment ?? existing.comment,
|
||||
requested_hours: requested_hours_per_day,
|
||||
payable_hours: payable_rounded,
|
||||
bank_code_id: bank_code.id,
|
||||
approval_status: dto.approval_status ?? existing.approval_status,
|
||||
},
|
||||
select: leaveRequestsSelect,
|
||||
});
|
||||
// const row = await this.prisma.leaveRequests.update({
|
||||
// where: { id: existing.id },
|
||||
// data: {
|
||||
// comment: dto.comment ?? existing.comment,
|
||||
// requested_hours: requested_hours_per_day,
|
||||
// payable_hours: payable_rounded,
|
||||
// bank_code_id: bank_code.id,
|
||||
// approval_status: dto.approval_status ?? existing.approval_status,
|
||||
// },
|
||||
// select: leaveRequestsSelect,
|
||||
// });
|
||||
|
||||
const was_approved = previous_status === LeaveApprovalStatus.APPROVED;
|
||||
const is_approved = row.approval_status === LeaveApprovalStatus.APPROVED;
|
||||
const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
|
||||
// const was_approved = previous_status === LeaveApprovalStatus.APPROVED;
|
||||
// const is_approved = row.approval_status === LeaveApprovalStatus.APPROVED;
|
||||
// const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
|
||||
|
||||
if (!was_approved && is_approved) {
|
||||
await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment);
|
||||
} else if (was_approved && !is_approved) {
|
||||
await this.leaveUtils.removeShift(email, employee_id, iso_date, type);
|
||||
} else if (was_approved && is_approved) {
|
||||
await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment);
|
||||
}
|
||||
updated.push({ ...mapRowToView(row), action: "update" });
|
||||
}
|
||||
return { action: "update", leave_requests: updated };
|
||||
}
|
||||
// if (!was_approved && is_approved) {
|
||||
// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment);
|
||||
// } else if (was_approved && !is_approved) {
|
||||
// await this.leaveUtils.removeShift(email, employee_id, iso_date, type);
|
||||
// } else if (was_approved && is_approved) {
|
||||
// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment);
|
||||
// }
|
||||
// updated.push({ ...mapRowToView(row), action: "update" });
|
||||
// }
|
||||
// return { action: "update", leave_requests: updated };
|
||||
// }
|
||||
|
||||
for (const { iso_date, date, existing } of entries) {
|
||||
const previous_status = existing.approval_status;
|
||||
const fallbackRequested =
|
||||
existing.requested_hours !== null && existing.requested_hours !== undefined
|
||||
? Number(existing.requested_hours)
|
||||
: 8;
|
||||
const requested_hours = dto.requested_hours ?? fallbackRequested;
|
||||
// for (const { iso_date, date, existing } of entries) {
|
||||
// const previous_status = existing.approval_status;
|
||||
// const fallbackRequested =
|
||||
// existing.requested_hours !== null && existing.requested_hours !== undefined
|
||||
// ? Number(existing.requested_hours)
|
||||
// : 8;
|
||||
// const requested_hours = dto.requested_hours ?? fallbackRequested;
|
||||
|
||||
let payable: number;
|
||||
switch (type) {
|
||||
case LeaveTypes.HOLIDAY:
|
||||
payable = await this.holidayService.calculateHolidayPay(email, date, modifier);
|
||||
break;
|
||||
case LeaveTypes.VACATION: {
|
||||
const days_requested = requested_hours / 8;
|
||||
payable = await this.vacationLogic.calculateVacationPay(
|
||||
employee_id,
|
||||
date,
|
||||
Math.max(0, days_requested),
|
||||
modifier,
|
||||
);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
payable = existing.payable_hours !== null && existing.payable_hours !== undefined
|
||||
? Number(existing.payable_hours)
|
||||
: requested_hours;
|
||||
}
|
||||
// let payable: number;
|
||||
// switch (type) {
|
||||
// case LeaveTypes.HOLIDAY:
|
||||
// payable = await this.holidayService.calculateHolidayPay(email, date, modifier);
|
||||
// break;
|
||||
// case LeaveTypes.VACATION: {
|
||||
// const days_requested = requested_hours / 8;
|
||||
// payable = await this.vacationLogic.calculateVacationPay(
|
||||
// employee_id,
|
||||
// date,
|
||||
// Math.max(0, days_requested),
|
||||
// modifier,
|
||||
// );
|
||||
// break;
|
||||
// }
|
||||
// default:
|
||||
// payable = existing.payable_hours !== null && existing.payable_hours !== undefined
|
||||
// ? Number(existing.payable_hours)
|
||||
// : requested_hours;
|
||||
// }
|
||||
|
||||
const row = await this.prisma.leaveRequests.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
requested_hours,
|
||||
comment: dto.comment ?? existing.comment,
|
||||
payable_hours: payable,
|
||||
bank_code_id: bank_code.id,
|
||||
approval_status: dto.approval_status ?? existing.approval_status,
|
||||
},
|
||||
select: leaveRequestsSelect,
|
||||
});
|
||||
// const row = await this.prisma.leaveRequests.update({
|
||||
// where: { id: existing.id },
|
||||
// data: {
|
||||
// requested_hours,
|
||||
// comment: dto.comment ?? existing.comment,
|
||||
// payable_hours: payable,
|
||||
// bank_code_id: bank_code.id,
|
||||
// approval_status: dto.approval_status ?? existing.approval_status,
|
||||
// },
|
||||
// select: leaveRequestsSelect,
|
||||
// });
|
||||
|
||||
const was_approved = previous_status === LeaveApprovalStatus.APPROVED;
|
||||
const is_approved = row.approval_status === LeaveApprovalStatus.APPROVED;
|
||||
const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
|
||||
// const was_approved = previous_status === LeaveApprovalStatus.APPROVED;
|
||||
// const is_approved = row.approval_status === LeaveApprovalStatus.APPROVED;
|
||||
// const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
|
||||
|
||||
if (!was_approved && is_approved) {
|
||||
await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment);
|
||||
} else if (was_approved && !is_approved) {
|
||||
await this.leaveUtils.removeShift(email, employee_id, iso_date, type);
|
||||
} else if (was_approved && is_approved) {
|
||||
await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment);
|
||||
}
|
||||
updated.push({ ...mapRowToView(row), action: "update" });
|
||||
}
|
||||
return { action: "update", leave_requests: updated };
|
||||
}
|
||||
}
|
||||
// if (!was_approved && is_approved) {
|
||||
// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment);
|
||||
// } else if (was_approved && !is_approved) {
|
||||
// await this.leaveUtils.removeShift(email, employee_id, iso_date, type);
|
||||
// } else if (was_approved && is_approved) {
|
||||
// await this.leaveUtils.syncShift(email, employee_id, iso_date, hours, type, row.comment);
|
||||
// }
|
||||
// updated.push({ ...mapRowToView(row), action: "update" });
|
||||
// }
|
||||
// return { action: "update", leave_requests: updated };
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,98 +1,98 @@
|
|||
import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto";
|
||||
import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto";
|
||||
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||
import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client";
|
||||
import { leaveRequestsSelect } from "../utils/leave-requests.select";
|
||||
import { mapRowToView } from "../mappers/leave-requests.mapper";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { SickLeaveService } from "src/modules/business-logics/services/sick-leave.service";
|
||||
import { roundToQuarterHour } from "src/common/utils/date-utils";
|
||||
import { LeaveRequestsUtils } from "../utils/leave-request.util";
|
||||
import { normalizeDates, toDateOnly } from "src/modules/shared/helpers/date-time.helpers";
|
||||
import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
|
||||
import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils";
|
||||
// import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto";
|
||||
// import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto";
|
||||
// import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||
// import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client";
|
||||
// import { leaveRequestsSelect } from "../utils/leave-requests.select";
|
||||
// import { mapRowToView } from "../mappers/leave-requests.mapper";
|
||||
// import { PrismaService } from "src/prisma/prisma.service";
|
||||
// import { SickLeaveService } from "src/modules/business-logics/services/sick-leave.service";
|
||||
// import { roundToQuarterHour } from "src/common/utils/date-utils";
|
||||
// import { LeaveRequestsUtils } from "../utils/leave-request.util";
|
||||
// import { normalizeDates, toDateOnly } from "src/modules/shared/helpers/date-time.helpers";
|
||||
// import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
|
||||
// import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils";
|
||||
|
||||
@Injectable()
|
||||
export class SickLeaveRequestsService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly sickService: SickLeaveService,
|
||||
private readonly leaveUtils: LeaveRequestsUtils,
|
||||
private readonly emailResolver: EmailToIdResolver,
|
||||
private readonly typeResolver: BankCodesResolver,
|
||||
) {}
|
||||
// @Injectable()
|
||||
// export class SickLeaveRequestsService {
|
||||
// constructor(
|
||||
// private readonly prisma: PrismaService,
|
||||
// private readonly sickService: SickLeaveService,
|
||||
// private readonly leaveUtils: LeaveRequestsUtils,
|
||||
// private readonly emailResolver: EmailToIdResolver,
|
||||
// private readonly typeResolver: BankCodesResolver,
|
||||
// ) {}
|
||||
|
||||
async create(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
|
||||
const email = dto.email.trim();
|
||||
const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||
const bank_code = await this.typeResolver.findByType(LeaveTypes.SICK);
|
||||
if(!bank_code) throw new NotFoundException(`bank_code not found`);
|
||||
// async create(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
|
||||
// const email = dto.email.trim();
|
||||
// const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||
// const bank_code = await this.typeResolver.findByType(LeaveTypes.SICK);
|
||||
// if(!bank_code) throw new NotFoundException(`bank_code not found`);
|
||||
|
||||
const modifier = bank_code.modifier ?? 1;
|
||||
const dates = normalizeDates(dto.dates);
|
||||
if (!dates.length) throw new BadRequestException("Dates array must not be empty");
|
||||
const requested_hours_per_day = dto.requested_hours ?? 8;
|
||||
// const modifier = bank_code.modifier ?? 1;
|
||||
// const dates = normalizeDates(dto.dates);
|
||||
// if (!dates.length) throw new BadRequestException("Dates array must not be empty");
|
||||
// const requested_hours_per_day = dto.requested_hours ?? 8;
|
||||
|
||||
const entries = dates.map((iso) => ({ iso, date: toDateOnly(iso) }));
|
||||
const reference_date = entries.reduce(
|
||||
(latest, entry) => (entry.date > latest ? entry.date : latest),
|
||||
entries[0].date,
|
||||
);
|
||||
const total_payable_hours = await this.sickService.calculateSickLeavePay(
|
||||
employee_id,
|
||||
reference_date,
|
||||
entries.length,
|
||||
requested_hours_per_day,
|
||||
modifier,
|
||||
);
|
||||
let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours));
|
||||
const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier);
|
||||
// const entries = dates.map((iso) => ({ iso, date: toDateOnly(iso) }));
|
||||
// const reference_date = entries.reduce(
|
||||
// (latest, entry) => (entry.date > latest ? entry.date : latest),
|
||||
// entries[0].date,
|
||||
// );
|
||||
// const total_payable_hours = await this.sickService.calculateSickLeavePay(
|
||||
// employee_id,
|
||||
// reference_date,
|
||||
// entries.length,
|
||||
// requested_hours_per_day,
|
||||
// modifier,
|
||||
// );
|
||||
// let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours));
|
||||
// const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier);
|
||||
|
||||
const created: LeaveRequestViewDto[] = [];
|
||||
// const created: LeaveRequestViewDto[] = [];
|
||||
|
||||
for (const { iso, date } of entries) {
|
||||
const existing = await this.prisma.leaveRequests.findUnique({
|
||||
where: {
|
||||
leave_per_employee_date: {
|
||||
employee_id: employee_id,
|
||||
leave_type: LeaveTypes.SICK,
|
||||
date,
|
||||
},
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
if (existing) {
|
||||
throw new BadRequestException(`Sick request already exists for ${iso}`);
|
||||
}
|
||||
// for (const { iso, date } of entries) {
|
||||
// const existing = await this.prisma.leaveRequests.findUnique({
|
||||
// where: {
|
||||
// leave_per_employee_date: {
|
||||
// employee_id: employee_id,
|
||||
// leave_type: LeaveTypes.SICK,
|
||||
// date,
|
||||
// },
|
||||
// },
|
||||
// select: { id: true },
|
||||
// });
|
||||
// if (existing) {
|
||||
// throw new BadRequestException(`Sick request already exists for ${iso}`);
|
||||
// }
|
||||
|
||||
const payable = Math.min(remaining_payable_hours, daily_payable_cap);
|
||||
const payable_rounded = roundToQuarterHour(Math.max(0, payable));
|
||||
remaining_payable_hours = roundToQuarterHour(
|
||||
Math.max(0, remaining_payable_hours - payable_rounded),
|
||||
);
|
||||
// const payable = Math.min(remaining_payable_hours, daily_payable_cap);
|
||||
// const payable_rounded = roundToQuarterHour(Math.max(0, payable));
|
||||
// remaining_payable_hours = roundToQuarterHour(
|
||||
// Math.max(0, remaining_payable_hours - payable_rounded),
|
||||
// );
|
||||
|
||||
const row = await this.prisma.leaveRequests.create({
|
||||
data: {
|
||||
employee_id: employee_id,
|
||||
bank_code_id: bank_code.id,
|
||||
leave_type: LeaveTypes.SICK,
|
||||
comment: dto.comment ?? "",
|
||||
requested_hours: requested_hours_per_day,
|
||||
payable_hours: payable_rounded,
|
||||
approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING,
|
||||
date,
|
||||
},
|
||||
select: leaveRequestsSelect,
|
||||
});
|
||||
// const row = await this.prisma.leaveRequests.create({
|
||||
// data: {
|
||||
// employee_id: employee_id,
|
||||
// bank_code_id: bank_code.id,
|
||||
// leave_type: LeaveTypes.SICK,
|
||||
// comment: dto.comment ?? "",
|
||||
// requested_hours: requested_hours_per_day,
|
||||
// payable_hours: payable_rounded,
|
||||
// approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING,
|
||||
// date,
|
||||
// },
|
||||
// select: leaveRequestsSelect,
|
||||
// });
|
||||
|
||||
const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
|
||||
if (row.approval_status === LeaveApprovalStatus.APPROVED) {
|
||||
await this.leaveUtils.syncShift(email, employee_id, iso, hours,LeaveTypes.SICK, row.comment);
|
||||
}
|
||||
// const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
|
||||
// if (row.approval_status === LeaveApprovalStatus.APPROVED) {
|
||||
// await this.leaveUtils.syncShift(email, employee_id, iso, hours,LeaveTypes.SICK, row.comment);
|
||||
// }
|
||||
|
||||
created.push({ ...mapRowToView(row), action: "create" });
|
||||
}
|
||||
// created.push({ ...mapRowToView(row), action: "create" });
|
||||
// }
|
||||
|
||||
return { action: "create", leave_requests: created };
|
||||
}
|
||||
}
|
||||
// return { action: "create", leave_requests: created };
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,93 +1,93 @@
|
|||
|
||||
import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto";
|
||||
import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto";
|
||||
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||
import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client";
|
||||
import { VacationService } from "src/modules/business-logics/services/vacation.service";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { mapRowToView } from "../mappers/leave-requests.mapper";
|
||||
import { leaveRequestsSelect } from "../utils/leave-requests.select";
|
||||
import { roundToQuarterHour } from "src/common/utils/date-utils";
|
||||
import { LeaveRequestsUtils } from "../utils/leave-request.util";
|
||||
import { normalizeDates, toDateOnly } from "src/modules/shared/helpers/date-time.helpers";
|
||||
import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
|
||||
import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils";
|
||||
// import { UpsertLeaveRequestDto, UpsertResult } from "../dtos/upsert-leave-request.dto";
|
||||
// import { LeaveRequestViewDto } from "../dtos/leave-request-view.dto";
|
||||
// import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||
// import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client";
|
||||
// import { VacationService } from "src/modules/business-logics/services/vacation.service";
|
||||
// import { PrismaService } from "src/prisma/prisma.service";
|
||||
// import { mapRowToView } from "../mappers/leave-requests.mapper";
|
||||
// import { leaveRequestsSelect } from "../utils/leave-requests.select";
|
||||
// import { roundToQuarterHour } from "src/common/utils/date-utils";
|
||||
// import { LeaveRequestsUtils } from "../utils/leave-request.util";
|
||||
// import { normalizeDates, toDateOnly } from "src/modules/shared/helpers/date-time.helpers";
|
||||
// import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
|
||||
// import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils";
|
||||
|
||||
@Injectable()
|
||||
export class VacationLeaveRequestsService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly vacationService: VacationService,
|
||||
private readonly leaveUtils: LeaveRequestsUtils,
|
||||
private readonly emailResolver: EmailToIdResolver,
|
||||
private readonly typeResolver: BankCodesResolver,
|
||||
) {}
|
||||
// @Injectable()
|
||||
// export class VacationLeaveRequestsService {
|
||||
// constructor(
|
||||
// private readonly prisma: PrismaService,
|
||||
// private readonly vacationService: VacationService,
|
||||
// private readonly leaveUtils: LeaveRequestsUtils,
|
||||
// private readonly emailResolver: EmailToIdResolver,
|
||||
// private readonly typeResolver: BankCodesResolver,
|
||||
// ) {}
|
||||
|
||||
async create(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
|
||||
const email = dto.email.trim();
|
||||
const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||
const bank_code = await this.typeResolver.findByType(LeaveTypes.VACATION);
|
||||
if(!bank_code) throw new NotFoundException(`bank_code not found`);
|
||||
// async create(dto: UpsertLeaveRequestDto): Promise<UpsertResult> {
|
||||
// const email = dto.email.trim();
|
||||
// const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||
// const bank_code = await this.typeResolver.findByType(LeaveTypes.VACATION);
|
||||
// if(!bank_code) throw new NotFoundException(`bank_code not found`);
|
||||
|
||||
const modifier = bank_code.modifier ?? 1;
|
||||
const dates = normalizeDates(dto.dates);
|
||||
const requested_hours_per_day = dto.requested_hours ?? 8;
|
||||
if (!dates.length) throw new BadRequestException("Dates array must not be empty");
|
||||
// const modifier = bank_code.modifier ?? 1;
|
||||
// const dates = normalizeDates(dto.dates);
|
||||
// const requested_hours_per_day = dto.requested_hours ?? 8;
|
||||
// if (!dates.length) throw new BadRequestException("Dates array must not be empty");
|
||||
|
||||
const entries = dates
|
||||
.map((iso) => ({ iso, date: toDateOnly(iso) }))
|
||||
.sort((a, b) => a.date.getTime() - b.date.getTime());
|
||||
const start_date = entries[0].date;
|
||||
const total_payable_hours = await this.vacationService.calculateVacationPay(
|
||||
employee_id,
|
||||
start_date,
|
||||
entries.length,
|
||||
modifier,
|
||||
);
|
||||
let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours));
|
||||
const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier);
|
||||
// const entries = dates
|
||||
// .map((iso) => ({ iso, date: toDateOnly(iso) }))
|
||||
// .sort((a, b) => a.date.getTime() - b.date.getTime());
|
||||
// const start_date = entries[0].date;
|
||||
// const total_payable_hours = await this.vacationService.calculateVacationPay(
|
||||
// employee_id,
|
||||
// start_date,
|
||||
// entries.length,
|
||||
// modifier,
|
||||
// );
|
||||
// let remaining_payable_hours = roundToQuarterHour(Math.max(0, total_payable_hours));
|
||||
// const daily_payable_cap = roundToQuarterHour(requested_hours_per_day * modifier);
|
||||
|
||||
const created: LeaveRequestViewDto[] = [];
|
||||
// const created: LeaveRequestViewDto[] = [];
|
||||
|
||||
for (const { iso, date } of entries) {
|
||||
const existing = await this.prisma.leaveRequests.findUnique({
|
||||
where: {
|
||||
leave_per_employee_date: {
|
||||
employee_id: employee_id,
|
||||
leave_type: LeaveTypes.VACATION,
|
||||
date,
|
||||
},
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
if (existing) throw new BadRequestException(`Vacation request already exists for ${iso}`);
|
||||
// for (const { iso, date } of entries) {
|
||||
// const existing = await this.prisma.leaveRequests.findUnique({
|
||||
// where: {
|
||||
// leave_per_employee_date: {
|
||||
// employee_id: employee_id,
|
||||
// leave_type: LeaveTypes.VACATION,
|
||||
// date,
|
||||
// },
|
||||
// },
|
||||
// select: { id: true },
|
||||
// });
|
||||
// if (existing) throw new BadRequestException(`Vacation request already exists for ${iso}`);
|
||||
|
||||
const payable = Math.min(remaining_payable_hours, daily_payable_cap);
|
||||
const payable_rounded = roundToQuarterHour(Math.max(0, payable));
|
||||
remaining_payable_hours = roundToQuarterHour(
|
||||
Math.max(0, remaining_payable_hours - payable_rounded),
|
||||
);
|
||||
// const payable = Math.min(remaining_payable_hours, daily_payable_cap);
|
||||
// const payable_rounded = roundToQuarterHour(Math.max(0, payable));
|
||||
// remaining_payable_hours = roundToQuarterHour(
|
||||
// Math.max(0, remaining_payable_hours - payable_rounded),
|
||||
// );
|
||||
|
||||
const row = await this.prisma.leaveRequests.create({
|
||||
data: {
|
||||
employee_id: employee_id,
|
||||
bank_code_id: bank_code.id,
|
||||
payable_hours: payable_rounded,
|
||||
requested_hours: requested_hours_per_day,
|
||||
leave_type: LeaveTypes.VACATION,
|
||||
comment: dto.comment ?? "",
|
||||
approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING,
|
||||
date,
|
||||
},
|
||||
select: leaveRequestsSelect,
|
||||
});
|
||||
// const row = await this.prisma.leaveRequests.create({
|
||||
// data: {
|
||||
// employee_id: employee_id,
|
||||
// bank_code_id: bank_code.id,
|
||||
// payable_hours: payable_rounded,
|
||||
// requested_hours: requested_hours_per_day,
|
||||
// leave_type: LeaveTypes.VACATION,
|
||||
// comment: dto.comment ?? "",
|
||||
// approval_status: dto.approval_status ?? LeaveApprovalStatus.PENDING,
|
||||
// date,
|
||||
// },
|
||||
// select: leaveRequestsSelect,
|
||||
// });
|
||||
|
||||
const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
|
||||
if (row.approval_status === LeaveApprovalStatus.APPROVED) {
|
||||
await this.leaveUtils.syncShift(email, employee_id, iso, hours, LeaveTypes.VACATION, row.comment);
|
||||
}
|
||||
created.push({ ...mapRowToView(row), action: "create" });
|
||||
}
|
||||
return { action: "create", leave_requests: created };
|
||||
}
|
||||
}
|
||||
// const hours = Number(row.payable_hours ?? row.requested_hours ?? 0);
|
||||
// if (row.approval_status === LeaveApprovalStatus.APPROVED) {
|
||||
// await this.leaveUtils.syncShift(email, employee_id, iso, hours, LeaveTypes.VACATION, row.comment);
|
||||
// }
|
||||
// created.push({ ...mapRowToView(row), action: "create" });
|
||||
// }
|
||||
// return { action: "create", leave_requests: created };
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,105 +1,104 @@
|
|||
import { hhmmFromLocal, toDateOnly, toStringFromDate } from "src/modules/shared/helpers/date-time.helpers";
|
||||
import { BadRequestException, Injectable } from "@nestjs/common";
|
||||
import { ShiftsCommandService } from "src/modules/shifts/_deprecated-files/shifts-command.service";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { LeaveTypes } from "@prisma/client";
|
||||
import { UpsertAction } from "src/modules/shared/types/upsert-actions.types";
|
||||
// import { hhmmFromLocal, toDateOnly, toStringFromDate } from "src/modules/shared/helpers/date-time.helpers";
|
||||
// import { BadRequestException, Injectable } from "@nestjs/common";
|
||||
// import { PrismaService } from "src/prisma/prisma.service";
|
||||
// import { LeaveTypes } from "@prisma/client";
|
||||
// import { UpsertAction } from "src/modules/shared/types/upsert-actions.types";
|
||||
|
||||
@Injectable()
|
||||
export class LeaveRequestsUtils {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly shiftsCommand: ShiftsCommandService,
|
||||
){}
|
||||
// @Injectable()
|
||||
// export class LeaveRequestsUtils {
|
||||
// constructor(
|
||||
// private readonly prisma: PrismaService,
|
||||
// private readonly shiftsCommand: ShiftsCommandService,
|
||||
// ){}
|
||||
|
||||
async syncShift(
|
||||
email: string,
|
||||
employee_id: number,
|
||||
date: string,
|
||||
hours: number,
|
||||
type: LeaveTypes,
|
||||
comment?: string,
|
||||
) {
|
||||
if (hours <= 0) return;
|
||||
// async syncShift(
|
||||
// email: string,
|
||||
// employee_id: number,
|
||||
// date: string,
|
||||
// hours: number,
|
||||
// type: LeaveTypes,
|
||||
// comment?: string,
|
||||
// ) {
|
||||
// if (hours <= 0) return;
|
||||
|
||||
const duration_minutes = Math.round(hours * 60);
|
||||
if (duration_minutes > 8 * 60) {
|
||||
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 duration_minutes = Math.round(hours * 60);
|
||||
// if (duration_minutes > 8 * 60) {
|
||||
// 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 end_minutes = start_minutes + duration_minutes;
|
||||
const toHHmm = (total: number) =>
|
||||
`${String(Math.floor(total / 60)).padStart(2, "0")}:${String(total % 60).padStart(2, "0")}`;
|
||||
// const start_minutes = 8 * 60;
|
||||
// const end_minutes = start_minutes + duration_minutes;
|
||||
// const toHHmm = (total: number) =>
|
||||
// `${String(Math.floor(total / 60)).padStart(2, "0")}:${String(total % 60).padStart(2, "0")}`;
|
||||
|
||||
const existing = await this.prisma.shifts.findFirst({
|
||||
where: {
|
||||
date: date_only,
|
||||
bank_code: { type },
|
||||
timesheet: { employee_id: employee_id },
|
||||
},
|
||||
include: { bank_code: true },
|
||||
});
|
||||
// const existing = await this.prisma.shifts.findFirst({
|
||||
// where: {
|
||||
// date: date_only,
|
||||
// bank_code: { type },
|
||||
// timesheet: { employee_id: employee_id },
|
||||
// },
|
||||
// include: { bank_code: true },
|
||||
// });
|
||||
|
||||
const action: UpsertAction = existing ? 'update' : 'create';
|
||||
// const action: UpsertAction = existing ? 'update' : 'create';
|
||||
|
||||
await this.shiftsCommand.upsertShifts(email, action, {
|
||||
old_shift: existing
|
||||
? {
|
||||
date: yyyy_mm_dd,
|
||||
start_time: existing.start_time.toISOString().slice(11, 16),
|
||||
end_time: existing.end_time.toISOString().slice(11, 16),
|
||||
type: existing.bank_code?.type ?? type,
|
||||
is_remote: existing.is_remote,
|
||||
is_approved:existing.is_approved,
|
||||
comment: existing.comment ?? undefined,
|
||||
}
|
||||
: undefined,
|
||||
new_shift: {
|
||||
date: yyyy_mm_dd,
|
||||
start_time: toHHmm(start_minutes),
|
||||
end_time: toHHmm(end_minutes),
|
||||
is_remote: existing?.is_remote ?? false,
|
||||
is_approved:existing?.is_approved ?? false,
|
||||
comment: comment ?? existing?.comment ?? "",
|
||||
type: type,
|
||||
},
|
||||
});
|
||||
}
|
||||
// await this.shiftsCommand.upsertShifts(email, action, {
|
||||
// old_shift: existing
|
||||
// ? {
|
||||
// date: yyyy_mm_dd,
|
||||
// start_time: existing.start_time.toISOString().slice(11, 16),
|
||||
// end_time: existing.end_time.toISOString().slice(11, 16),
|
||||
// type: existing.bank_code?.type ?? type,
|
||||
// is_remote: existing.is_remote,
|
||||
// is_approved:existing.is_approved,
|
||||
// comment: existing.comment ?? undefined,
|
||||
// }
|
||||
// : undefined,
|
||||
// new_shift: {
|
||||
// date: yyyy_mm_dd,
|
||||
// start_time: toHHmm(start_minutes),
|
||||
// end_time: toHHmm(end_minutes),
|
||||
// is_remote: existing?.is_remote ?? false,
|
||||
// is_approved:existing?.is_approved ?? false,
|
||||
// comment: comment ?? existing?.comment ?? "",
|
||||
// type: type,
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
|
||||
async removeShift(
|
||||
email: string,
|
||||
employee_id: number,
|
||||
iso_date: string,
|
||||
type: LeaveTypes,
|
||||
) {
|
||||
const date_only = toDateOnly(iso_date);
|
||||
const yyyy_mm_dd = toStringFromDate(date_only);
|
||||
const existing = await this.prisma.shifts.findFirst({
|
||||
where: {
|
||||
date: date_only,
|
||||
bank_code: { type },
|
||||
timesheet: { employee_id: employee_id },
|
||||
},
|
||||
include: { bank_code: true },
|
||||
});
|
||||
if (!existing) return;
|
||||
// async removeShift(
|
||||
// email: string,
|
||||
// employee_id: number,
|
||||
// iso_date: string,
|
||||
// type: LeaveTypes,
|
||||
// ) {
|
||||
// const date_only = toDateOnly(iso_date);
|
||||
// const yyyy_mm_dd = toStringFromDate(date_only);
|
||||
// const existing = await this.prisma.shifts.findFirst({
|
||||
// where: {
|
||||
// date: date_only,
|
||||
// bank_code: { type },
|
||||
// timesheet: { employee_id: employee_id },
|
||||
// },
|
||||
// include: { bank_code: true },
|
||||
// });
|
||||
// if (!existing) return;
|
||||
|
||||
await this.shiftsCommand.upsertShifts(email, 'delete', {
|
||||
old_shift: {
|
||||
date: yyyy_mm_dd,
|
||||
start_time: hhmmFromLocal(existing.start_time),
|
||||
end_time: hhmmFromLocal(existing.end_time),
|
||||
type: existing.bank_code?.type ?? type,
|
||||
is_remote: existing.is_remote,
|
||||
is_approved:existing.is_approved,
|
||||
comment: existing.comment ?? undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
// await this.shiftsCommand.upsertShifts(email, 'delete', {
|
||||
// old_shift: {
|
||||
// date: yyyy_mm_dd,
|
||||
// start_time: hhmmFromLocal(existing.start_time),
|
||||
// end_time: hhmmFromLocal(existing.end_time),
|
||||
// type: existing.bank_code?.type ?? type,
|
||||
// is_remote: existing.is_remote,
|
||||
// is_approved:existing.is_approved,
|
||||
// comment: existing.comment ?? undefined,
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
|
||||
}
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,33 +1,27 @@
|
|||
import { PrismaModule } from "src/prisma/prisma.module";
|
||||
import { PayPeriodsController } from "./controllers/pay-periods.controller";
|
||||
import { Module } from "@nestjs/common";
|
||||
import { PayPeriodsCommandService } from "./services/pay-periods-command.service";
|
||||
import { PayPeriodsQueryService } from "./services/pay-periods-query.service";
|
||||
import { TimesheetsModule } from "../timesheets/timesheets.module";
|
||||
import { TimesheetsCommandService } from "../timesheets/~misc_deprecated-files/timesheets-command.service";
|
||||
import { ExpensesCommandService } from "../expenses/services/expenses-command.service";
|
||||
import { ShiftsCommandService } from "../shifts/_deprecated-files/shifts-command.service";
|
||||
import { SharedModule } from "../shared/shared.module";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { BusinessLogicsModule } from "../business-logics/business-logics.module";
|
||||
import { ShiftsHelpersService } from "../shifts/_deprecated-files/shifts.helpers";
|
||||
// import { PrismaModule } from "src/prisma/prisma.module";
|
||||
// import { PayPeriodsController } from "./controllers/pay-periods.controller";
|
||||
// import { Module } from "@nestjs/common";
|
||||
// import { PayPeriodsCommandService } from "./services/pay-periods-command.service";
|
||||
// import { PayPeriodsQueryService } from "./services/pay-periods-query.service";
|
||||
// import { TimesheetsModule } from "../timesheets/timesheets.module";
|
||||
// import { ExpensesCommandService } from "../expenses/services/expenses-command.service";
|
||||
// import { SharedModule } from "../shared/shared.module";
|
||||
// import { PrismaService } from "src/prisma/prisma.service";
|
||||
// import { BusinessLogicsModule } from "../business-logics/business-logics.module";
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule, TimesheetsModule, SharedModule, BusinessLogicsModule],
|
||||
providers: [
|
||||
PayPeriodsQueryService,
|
||||
PayPeriodsCommandService,
|
||||
TimesheetsCommandService,
|
||||
ExpensesCommandService,
|
||||
ShiftsCommandService,
|
||||
PrismaService,
|
||||
ShiftsHelpersService,
|
||||
],
|
||||
controllers: [PayPeriodsController],
|
||||
exports: [
|
||||
PayPeriodsQueryService,
|
||||
PayPeriodsCommandService,
|
||||
]
|
||||
})
|
||||
// @Module({
|
||||
// imports: [PrismaModule, TimesheetsModule, SharedModule, BusinessLogicsModule],
|
||||
// providers: [
|
||||
// PayPeriodsQueryService,
|
||||
// PayPeriodsCommandService,
|
||||
// ExpensesCommandService,
|
||||
// PrismaService,
|
||||
// ],
|
||||
// controllers: [PayPeriodsController],
|
||||
// exports: [
|
||||
// PayPeriodsQueryService,
|
||||
// PayPeriodsCommandService,
|
||||
// ]
|
||||
// })
|
||||
|
||||
export class PayperiodsModule {}
|
||||
// export class PayperiodsModule {}
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from "@nestjs/common";
|
||||
import { TimesheetsCommandService } from "src/modules/timesheets/~misc_deprecated-files/timesheets-command.service";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto";
|
||||
import { PayPeriodsQueryService } from "./pay-periods-query.service";
|
||||
import { TimesheetApprovalService } from "src/modules/timesheets/services/timesheet-approval.service";
|
||||
|
||||
@Injectable()
|
||||
export class PayPeriodsCommandService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly timesheets_approval: TimesheetsCommandService,
|
||||
private readonly timesheets_approval: TimesheetApprovalService,
|
||||
private readonly query: PayPeriodsQueryService,
|
||||
) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { Prisma, Timesheets } from "@prisma/client";
|
||||
import { BaseApprovalService } from "src/common/shared/base-approval.service";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
|
||||
@Injectable()
|
||||
export class TimesheetApprovalService extends BaseApprovalService<Timesheets>{
|
||||
constructor(prisma: PrismaService){super(prisma)}
|
||||
//_____________________________________________________________________________________________
|
||||
// APPROVAL AND DELEGATE METHODS
|
||||
//_____________________________________________________________________________________________
|
||||
protected get delegate() {
|
||||
return this.prisma.timesheets;
|
||||
}
|
||||
|
||||
protected delegateFor(transaction: Prisma.TransactionClient) {
|
||||
return transaction.timesheets;
|
||||
}
|
||||
|
||||
async updateApproval(id: number, isApproved: boolean): Promise<Timesheets> {
|
||||
return this.prisma.$transaction((transaction) =>
|
||||
this.updateApprovalWithTransaction(transaction, id, isApproved),
|
||||
);
|
||||
}
|
||||
|
||||
async cascadeApprovalWithtx(transaction: Prisma.TransactionClient, timesheetId: number, isApproved: boolean): Promise<Timesheets> {
|
||||
const timesheet = await this.updateApprovalWithTransaction(transaction, timesheetId, isApproved);
|
||||
await transaction.shifts.updateMany({
|
||||
where: { timesheet_id: timesheetId },
|
||||
data: { is_approved: isApproved },
|
||||
});
|
||||
await transaction.expenses.updateManyAndReturn({
|
||||
where: { timesheet_id: timesheetId },
|
||||
data: { is_approved: isApproved },
|
||||
});
|
||||
return timesheet;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +1,33 @@
|
|||
import { Type } from "class-transformer";
|
||||
import { IsArray, IsOptional, IsString, Length, Matches, ValidateNested } from "class-validator";
|
||||
// import { Type } from "class-transformer";
|
||||
// import { IsArray, IsOptional, IsString, Length, Matches, ValidateNested } from "class-validator";
|
||||
|
||||
export class CreateTimesheetDto {
|
||||
// export class CreateTimesheetDto {
|
||||
|
||||
@IsString()
|
||||
@Matches(/^\d{4}-\d{2}-\d{2}$/)
|
||||
date!: string;
|
||||
// @IsString()
|
||||
// @Matches(/^\d{4}-\d{2}-\d{2}$/)
|
||||
// date!: string;
|
||||
|
||||
@IsString()
|
||||
@Length(1,64)
|
||||
type!: string;
|
||||
// @IsString()
|
||||
// @Length(1,64)
|
||||
// type!: string;
|
||||
|
||||
@IsString()
|
||||
@Matches(/^\d{2}:\d{2}$/)
|
||||
start_time!: string;
|
||||
// @IsString()
|
||||
// @Matches(/^\d{2}:\d{2}$/)
|
||||
// start_time!: string;
|
||||
|
||||
@IsString()
|
||||
@Matches(/^\d{2}:\d{2}$/)
|
||||
end_time!: string;
|
||||
// @IsString()
|
||||
// @Matches(/^\d{2}:\d{2}$/)
|
||||
// end_time!: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Length(0,512)
|
||||
comment?: string;
|
||||
}
|
||||
// @IsOptional()
|
||||
// @IsString()
|
||||
// @Length(0,512)
|
||||
// comment?: string;
|
||||
// }
|
||||
|
||||
export class CreateWeekShiftsDto {
|
||||
@IsArray()
|
||||
@ValidateNested({each:true})
|
||||
@Type(()=> CreateTimesheetDto)
|
||||
shifts!: CreateTimesheetDto[];
|
||||
}
|
||||
// export class CreateWeekShiftsDto {
|
||||
// @IsArray()
|
||||
// @ValidateNested({each:true})
|
||||
// @Type(()=> CreateTimesheetDto)
|
||||
// shifts!: CreateTimesheetDto[];
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
import { Type } from "class-transformer";
|
||||
import { IsBoolean, IsInt, IsOptional } from "class-validator";
|
||||
// import { Type } from "class-transformer";
|
||||
// import { IsBoolean, IsInt, IsOptional } from "class-validator";
|
||||
|
||||
|
||||
export class SearchTimesheetDto {
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
timesheet_id?: number;
|
||||
// export class SearchTimesheetDto {
|
||||
// @IsOptional()
|
||||
// @Type(() => Number)
|
||||
// @IsInt()
|
||||
// timesheet_id?: number;
|
||||
|
||||
@IsOptional()
|
||||
@Type(()=> Number)
|
||||
@IsInt()
|
||||
employee_id?: number;
|
||||
// @IsOptional()
|
||||
// @Type(()=> Number)
|
||||
// @IsInt()
|
||||
// employee_id?: number;
|
||||
|
||||
@IsOptional()
|
||||
@Type(()=> Boolean)
|
||||
@IsBoolean()
|
||||
is_approved?: boolean;
|
||||
}
|
||||
// @IsOptional()
|
||||
// @Type(()=> Boolean)
|
||||
// @IsBoolean()
|
||||
// is_approved?: boolean;
|
||||
// }
|
||||
|
|
@ -1,75 +1,75 @@
|
|||
export class TimesheetDto {
|
||||
start_day: string;
|
||||
end_day: string;
|
||||
label: string;
|
||||
shifts: ShiftDto[];
|
||||
expenses: ExpenseDto[]
|
||||
is_approved: boolean;
|
||||
}
|
||||
// export class TimesheetDto {
|
||||
// start_day: string;
|
||||
// end_day: string;
|
||||
// label: string;
|
||||
// shifts: ShiftDto[];
|
||||
// expenses: ExpenseDto[]
|
||||
// is_approved: boolean;
|
||||
// }
|
||||
|
||||
export class ShiftDto {
|
||||
date: string;
|
||||
type: string;
|
||||
start_time: string;
|
||||
end_time : string;
|
||||
comment: string;
|
||||
is_approved: boolean;
|
||||
is_remote: boolean;
|
||||
}
|
||||
// export class ShiftDto {
|
||||
// date: string;
|
||||
// type: string;
|
||||
// start_time: string;
|
||||
// end_time : string;
|
||||
// comment: string;
|
||||
// is_approved: boolean;
|
||||
// is_remote: boolean;
|
||||
// }
|
||||
|
||||
export class ExpenseDto {
|
||||
type: string;
|
||||
amount: number;
|
||||
mileage: number;
|
||||
comment: string;
|
||||
is_approved: boolean;
|
||||
supervisor_comment: string;
|
||||
}
|
||||
// export class ExpenseDto {
|
||||
// type: string;
|
||||
// amount: number;
|
||||
// mileage: number;
|
||||
// comment: string;
|
||||
// is_approved: boolean;
|
||||
// supervisor_comment: string;
|
||||
// }
|
||||
|
||||
export type DayShiftsDto = ShiftDto[];
|
||||
// export type DayShiftsDto = ShiftDto[];
|
||||
|
||||
export class DetailedShifts {
|
||||
shifts: DayShiftsDto;
|
||||
regular_hours: number;
|
||||
evening_hours: number;
|
||||
overtime_hours: number;
|
||||
emergency_hours: number;
|
||||
comment: string;
|
||||
short_date: string;
|
||||
break_durations?: number;
|
||||
}
|
||||
// export class DetailedShifts {
|
||||
// shifts: DayShiftsDto;
|
||||
// regular_hours: number;
|
||||
// evening_hours: number;
|
||||
// overtime_hours: number;
|
||||
// emergency_hours: number;
|
||||
// comment: string;
|
||||
// short_date: string;
|
||||
// break_durations?: number;
|
||||
// }
|
||||
|
||||
export class DayExpensesDto {
|
||||
expenses: ExpenseDto[] = [];
|
||||
total_mileage: number;
|
||||
total_expense: number;
|
||||
}
|
||||
// export class DayExpensesDto {
|
||||
// expenses: ExpenseDto[] = [];
|
||||
// total_mileage: number;
|
||||
// total_expense: number;
|
||||
// }
|
||||
|
||||
export class WeekDto {
|
||||
is_approved: boolean;
|
||||
shifts: {
|
||||
sun: DetailedShifts;
|
||||
mon: DetailedShifts;
|
||||
tue: DetailedShifts;
|
||||
wed: DetailedShifts;
|
||||
thu: DetailedShifts;
|
||||
fri: DetailedShifts;
|
||||
sat: DetailedShifts;
|
||||
}
|
||||
expenses: {
|
||||
sun: DayExpensesDto;
|
||||
mon: DayExpensesDto;
|
||||
tue: DayExpensesDto;
|
||||
wed: DayExpensesDto;
|
||||
thu: DayExpensesDto;
|
||||
fri: DayExpensesDto;
|
||||
sat: DayExpensesDto;
|
||||
}
|
||||
}
|
||||
// export class WeekDto {
|
||||
// is_approved: boolean;
|
||||
// shifts: {
|
||||
// sun: DetailedShifts;
|
||||
// mon: DetailedShifts;
|
||||
// tue: DetailedShifts;
|
||||
// wed: DetailedShifts;
|
||||
// thu: DetailedShifts;
|
||||
// fri: DetailedShifts;
|
||||
// sat: DetailedShifts;
|
||||
// }
|
||||
// expenses: {
|
||||
// sun: DayExpensesDto;
|
||||
// mon: DayExpensesDto;
|
||||
// tue: DayExpensesDto;
|
||||
// wed: DayExpensesDto;
|
||||
// thu: DayExpensesDto;
|
||||
// fri: DayExpensesDto;
|
||||
// sat: DayExpensesDto;
|
||||
// }
|
||||
// }
|
||||
|
||||
export class TimesheetPeriodDto {
|
||||
weeks: WeekDto[];
|
||||
employee_full_name: string;
|
||||
}
|
||||
// export class TimesheetPeriodDto {
|
||||
// weeks: WeekDto[];
|
||||
// employee_full_name: string;
|
||||
// }
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,67 +1,67 @@
|
|||
import { MS_PER_DAY } from "src/modules/shared/constants/date-time.constant";
|
||||
import { DAY_KEYS, DayKey } from "./timesheet.types";
|
||||
// import { MS_PER_DAY } from "src/modules/shared/constants/date-time.constant";
|
||||
// import { DAY_KEYS, DayKey } from "./timesheet.types";
|
||||
|
||||
export function toUTCDateOnly(date: Date | string): Date {
|
||||
const d = new Date(date);
|
||||
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()));
|
||||
}
|
||||
// export function toUTCDateOnly(date: Date | string): Date {
|
||||
// const d = new Date(date);
|
||||
// return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()));
|
||||
// }
|
||||
|
||||
export function addDays(date:Date, days: number): Date {
|
||||
return new Date(date.getTime() + days * MS_PER_DAY);
|
||||
}
|
||||
// export function addDays(date:Date, days: number): Date {
|
||||
// return new Date(date.getTime() + days * MS_PER_DAY);
|
||||
// }
|
||||
|
||||
export function endOfDayUTC(date: Date | string): Date {
|
||||
const d = toUTCDateOnly(date);
|
||||
return new Date(d.getTime() + MS_PER_DAY - 1);
|
||||
}
|
||||
// export function endOfDayUTC(date: Date | string): Date {
|
||||
// const d = toUTCDateOnly(date);
|
||||
// return new Date(d.getTime() + MS_PER_DAY - 1);
|
||||
// }
|
||||
|
||||
export function isBetweenUTC(date: Date, start: Date, end_inclusive: Date): boolean {
|
||||
const time = date.getTime();
|
||||
return time >= start.getTime() && time <= end_inclusive.getTime();
|
||||
}
|
||||
// export function isBetweenUTC(date: Date, start: Date, end_inclusive: Date): boolean {
|
||||
// const time = date.getTime();
|
||||
// return time >= start.getTime() && time <= end_inclusive.getTime();
|
||||
// }
|
||||
|
||||
export function toTimeString(date: Date): string {
|
||||
const hours = String(date.getUTCHours()).padStart(2,'0');
|
||||
const minutes = String(date.getUTCMinutes()).padStart(2,'0');
|
||||
return `${hours}:${minutes}`;
|
||||
}
|
||||
// export function toTimeString(date: Date): string {
|
||||
// const hours = String(date.getUTCHours()).padStart(2,'0');
|
||||
// const minutes = String(date.getUTCMinutes()).padStart(2,'0');
|
||||
// return `${hours}:${minutes}`;
|
||||
// }
|
||||
|
||||
export function round2(num: number) {
|
||||
return Math.round(num * 100) / 100;
|
||||
}
|
||||
// export function round2(num: number) {
|
||||
// return Math.round(num * 100) / 100;
|
||||
// }
|
||||
|
||||
export function shortDate(date:Date): string {
|
||||
const mm = String(date.getUTCMonth()+1).padStart(2,'0');
|
||||
const dd = String(date.getUTCDate()).padStart(2,'0');
|
||||
return `${mm}/${dd}`;
|
||||
}
|
||||
// export function shortDate(date:Date): string {
|
||||
// const mm = String(date.getUTCMonth()+1).padStart(2,'0');
|
||||
// const dd = String(date.getUTCDate()).padStart(2,'0');
|
||||
// return `${mm}/${dd}`;
|
||||
// }
|
||||
|
||||
export function dayKeyFromDate(date: Date, useUTC = true): DayKey {
|
||||
const index = useUTC ? date.getUTCDay() : date.getDay(); // 0=Sunday..6=Saturday
|
||||
return DAY_KEYS[index];
|
||||
}
|
||||
// export function dayKeyFromDate(date: Date, useUTC = true): DayKey {
|
||||
// const index = useUTC ? date.getUTCDay() : date.getDay(); // 0=Sunday..6=Saturday
|
||||
// return DAY_KEYS[index];
|
||||
// }
|
||||
|
||||
export const toHHmm = (date: Date) => date.toISOString().slice(11, 16);
|
||||
// export const toHHmm = (date: Date) => date.toISOString().slice(11, 16);
|
||||
|
||||
export function parseISODate(iso: string): Date {
|
||||
const [ y, m, d ] = iso.split('-').map(Number);
|
||||
return new Date(y, (m ?? 1) - 1, d ?? 1);
|
||||
}
|
||||
// export function parseISODate(iso: string): Date {
|
||||
// const [ y, m, d ] = iso.split('-').map(Number);
|
||||
// return new Date(y, (m ?? 1) - 1, d ?? 1);
|
||||
// }
|
||||
|
||||
export function parseHHmm(t: string): Date {
|
||||
const [ hh, mm ] = t.split(':').map(Number);
|
||||
return new Date(1970, 0, 1, hh || 0, mm || 0, 0, 0);
|
||||
}
|
||||
// export function parseHHmm(t: string): Date {
|
||||
// const [ hh, mm ] = t.split(':').map(Number);
|
||||
// return new Date(1970, 0, 1, hh || 0, mm || 0, 0, 0);
|
||||
// }
|
||||
|
||||
export const toNum = (value: any) =>
|
||||
value && typeof value.toNumber === 'function' ? value.toNumber() :
|
||||
typeof value === 'number' ? value :
|
||||
value ? Number(value) : 0;
|
||||
// export const toNum = (value: any) =>
|
||||
// value && typeof value.toNumber === 'function' ? value.toNumber() :
|
||||
// typeof value === 'number' ? value :
|
||||
// value ? Number(value) : 0;
|
||||
|
||||
|
||||
export const upper = (s?: string | null) => String(s ?? '').toUpperCase();
|
||||
// export const upper = (s?: string | null) => String(s ?? '').toUpperCase();
|
||||
|
||||
export const toRangeFromPeriod = (period: { period_start: Date; period_end: Date }) => ({
|
||||
from: toUTCDateOnly(period.period_start),
|
||||
to: endOfDayUTC(period.period_end),
|
||||
});
|
||||
// export const toRangeFromPeriod = (period: { period_start: Date; period_end: Date }) => ({
|
||||
// from: toUTCDateOnly(period.period_start),
|
||||
// to: endOfDayUTC(period.period_end),
|
||||
// });
|
||||
|
|
@ -1,111 +1,111 @@
|
|||
import { DayExpensesDto, WeekDto, DetailedShifts, TimesheetPeriodDto } from "../dtos/timesheet-period.dto";
|
||||
import { ShiftRow, ExpenseRow, ExpensesAmount, TimesheetMap } from "./timesheet.types";
|
||||
import { addDays, shortDate, toNum, upper } from "./timesheet.helpers";
|
||||
import { Prisma } from "@prisma/client";
|
||||
// import { DayExpensesDto, WeekDto, DetailedShifts, TimesheetPeriodDto } from "../dtos/timesheet-period.dto";
|
||||
// import { ShiftRow, ExpenseRow, ExpensesAmount, TimesheetMap } from "./timesheet.types";
|
||||
// import { addDays, shortDate, toNum, upper } from "./timesheet.helpers";
|
||||
// import { Prisma } from "@prisma/client";
|
||||
|
||||
|
||||
//mappers
|
||||
export const mapShiftRow = (shift: {
|
||||
date: Date;
|
||||
start_time: Date;
|
||||
end_time: Date;
|
||||
comment?: string | null;
|
||||
is_approved: boolean;
|
||||
is_remote: boolean;
|
||||
bank_code: { type: string };
|
||||
}): ShiftRow => ({
|
||||
date: shift.date,
|
||||
start_time: shift.start_time,
|
||||
end_time: shift.end_time,
|
||||
comment: shift.comment ?? '',
|
||||
is_approved: shift.is_approved,
|
||||
is_remote: shift.is_remote,
|
||||
type: upper(shift.bank_code.type),
|
||||
});
|
||||
// //mappers
|
||||
// export const mapShiftRow = (shift: {
|
||||
// date: Date;
|
||||
// start_time: Date;
|
||||
// end_time: Date;
|
||||
// comment?: string | null;
|
||||
// is_approved: boolean;
|
||||
// is_remote: boolean;
|
||||
// bank_code: { type: string };
|
||||
// }): ShiftRow => ({
|
||||
// date: shift.date,
|
||||
// start_time: shift.start_time,
|
||||
// end_time: shift.end_time,
|
||||
// comment: shift.comment ?? '',
|
||||
// is_approved: shift.is_approved,
|
||||
// is_remote: shift.is_remote,
|
||||
// type: upper(shift.bank_code.type),
|
||||
// });
|
||||
|
||||
export const mapExpenseRow = (expense: {
|
||||
date: Date;
|
||||
amount: Prisma.Decimal | number | null;
|
||||
mileage: Prisma.Decimal | number | null;
|
||||
comment?: string | null;
|
||||
is_approved: boolean;
|
||||
supervisor_comment?: string|null;
|
||||
bank_code: { type: string },
|
||||
}): ExpenseRow => ({
|
||||
date: expense.date,
|
||||
amount: toNum(expense.amount),
|
||||
mileage: toNum(expense.mileage),
|
||||
comment: expense.comment ?? '',
|
||||
is_approved: expense.is_approved,
|
||||
supervisor_comment: expense.supervisor_comment ?? '',
|
||||
type: upper(expense.bank_code.type),
|
||||
});
|
||||
// export const mapExpenseRow = (expense: {
|
||||
// date: Date;
|
||||
// amount: Prisma.Decimal | number | null;
|
||||
// mileage: Prisma.Decimal | number | null;
|
||||
// comment?: string | null;
|
||||
// is_approved: boolean;
|
||||
// supervisor_comment?: string|null;
|
||||
// bank_code: { type: string },
|
||||
// }): ExpenseRow => ({
|
||||
// date: expense.date,
|
||||
// amount: toNum(expense.amount),
|
||||
// mileage: toNum(expense.mileage),
|
||||
// comment: expense.comment ?? '',
|
||||
// is_approved: expense.is_approved,
|
||||
// supervisor_comment: expense.supervisor_comment ?? '',
|
||||
// type: upper(expense.bank_code.type),
|
||||
// });
|
||||
|
||||
// Factories
|
||||
export function makeEmptyDayExpenses(): DayExpensesDto {
|
||||
return {
|
||||
expenses: [],
|
||||
total_expense: -1,
|
||||
total_mileage: -1,
|
||||
};
|
||||
}
|
||||
// // Factories
|
||||
// export function makeEmptyDayExpenses(): DayExpensesDto {
|
||||
// return {
|
||||
// expenses: [],
|
||||
// total_expense: -1,
|
||||
// total_mileage: -1,
|
||||
// };
|
||||
// }
|
||||
|
||||
export function makeEmptyWeek(week_start: Date): WeekDto {
|
||||
const make_empty_shifts = (offset: number): DetailedShifts => ({
|
||||
shifts: [],
|
||||
regular_hours: 0,
|
||||
evening_hours: 0,
|
||||
emergency_hours: 0,
|
||||
overtime_hours: 0,
|
||||
comment: '',
|
||||
short_date: shortDate(addDays(week_start, offset)),
|
||||
break_durations: 0,
|
||||
});
|
||||
return {
|
||||
is_approved: true,
|
||||
shifts: {
|
||||
sun: make_empty_shifts(0),
|
||||
mon: make_empty_shifts(1),
|
||||
tue: make_empty_shifts(2),
|
||||
wed: make_empty_shifts(3),
|
||||
thu: make_empty_shifts(4),
|
||||
fri: make_empty_shifts(5),
|
||||
sat: make_empty_shifts(6),
|
||||
},
|
||||
expenses: {
|
||||
sun: makeEmptyDayExpenses(),
|
||||
mon: makeEmptyDayExpenses(),
|
||||
tue: makeEmptyDayExpenses(),
|
||||
wed: makeEmptyDayExpenses(),
|
||||
thu: makeEmptyDayExpenses(),
|
||||
fri: makeEmptyDayExpenses(),
|
||||
sat: makeEmptyDayExpenses(),
|
||||
},
|
||||
};
|
||||
}
|
||||
// export function makeEmptyWeek(week_start: Date): WeekDto {
|
||||
// const make_empty_shifts = (offset: number): DetailedShifts => ({
|
||||
// shifts: [],
|
||||
// regular_hours: 0,
|
||||
// evening_hours: 0,
|
||||
// emergency_hours: 0,
|
||||
// overtime_hours: 0,
|
||||
// comment: '',
|
||||
// short_date: shortDate(addDays(week_start, offset)),
|
||||
// break_durations: 0,
|
||||
// });
|
||||
// return {
|
||||
// is_approved: true,
|
||||
// shifts: {
|
||||
// sun: make_empty_shifts(0),
|
||||
// mon: make_empty_shifts(1),
|
||||
// tue: make_empty_shifts(2),
|
||||
// wed: make_empty_shifts(3),
|
||||
// thu: make_empty_shifts(4),
|
||||
// fri: make_empty_shifts(5),
|
||||
// sat: make_empty_shifts(6),
|
||||
// },
|
||||
// expenses: {
|
||||
// sun: makeEmptyDayExpenses(),
|
||||
// mon: makeEmptyDayExpenses(),
|
||||
// tue: makeEmptyDayExpenses(),
|
||||
// wed: makeEmptyDayExpenses(),
|
||||
// thu: makeEmptyDayExpenses(),
|
||||
// fri: makeEmptyDayExpenses(),
|
||||
// sat: makeEmptyDayExpenses(),
|
||||
// },
|
||||
// };
|
||||
// }
|
||||
|
||||
export function makeEmptyPeriod(): TimesheetPeriodDto {
|
||||
return { weeks: [makeEmptyWeek(new Date()), makeEmptyWeek(new Date())], employee_full_name: '' };
|
||||
}
|
||||
// export function makeEmptyPeriod(): TimesheetPeriodDto {
|
||||
// return { weeks: [makeEmptyWeek(new Date()), makeEmptyWeek(new Date())], employee_full_name: '' };
|
||||
// }
|
||||
|
||||
export const makeAmounts = (): ExpensesAmount => ({
|
||||
expense: 0,
|
||||
mileage: 0,
|
||||
});
|
||||
// export const makeAmounts = (): ExpensesAmount => ({
|
||||
// expense: 0,
|
||||
// mileage: 0,
|
||||
// });
|
||||
|
||||
export function makeEmptyTimesheet(params: {
|
||||
start_day: string;
|
||||
end_day: string;
|
||||
label: string;
|
||||
is_approved?: boolean;
|
||||
}): TimesheetMap {
|
||||
const { start_day, end_day, label, is_approved = false } = params;
|
||||
return {
|
||||
start_day,
|
||||
end_day,
|
||||
label,
|
||||
shifts: [],
|
||||
expenses: [],
|
||||
is_approved,
|
||||
};
|
||||
}
|
||||
// export function makeEmptyTimesheet(params: {
|
||||
// start_day: string;
|
||||
// end_day: string;
|
||||
// label: string;
|
||||
// is_approved?: boolean;
|
||||
// }): TimesheetMap {
|
||||
// const { start_day, end_day, label, is_approved = false } = params;
|
||||
// return {
|
||||
// start_day,
|
||||
// end_day,
|
||||
// label,
|
||||
// shifts: [],
|
||||
// expenses: [],
|
||||
// is_approved,
|
||||
// };
|
||||
// }
|
||||
|
|
@ -1,46 +1,46 @@
|
|||
import { EXPENSE_ASC_ORDER, EXPENSE_SELECT } from "../../../shared/selects/expenses.select";
|
||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||
import { SHIFT_ASC_ORDER, SHIFT_SELECT } from "../../../shared/selects/shifts.select";
|
||||
import { PAY_PERIOD_SELECT } from "../../../shared/selects/pay-periods.select";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
// import { EXPENSE_ASC_ORDER, EXPENSE_SELECT } from "../../../shared/selects/expenses.select";
|
||||
// import { Injectable, NotFoundException } from "@nestjs/common";
|
||||
// import { SHIFT_ASC_ORDER, SHIFT_SELECT } from "../../../shared/selects/shifts.select";
|
||||
// import { PAY_PERIOD_SELECT } from "../../../shared/selects/pay-periods.select";
|
||||
// import { PrismaService } from "src/prisma/prisma.service";
|
||||
|
||||
@Injectable()
|
||||
export class TimesheetSelectorsService {
|
||||
constructor(readonly prisma: PrismaService){}
|
||||
// @Injectable()
|
||||
// export class TimesheetSelectorsService {
|
||||
// constructor(readonly prisma: PrismaService){}
|
||||
|
||||
async getPayPeriod(pay_year: number, pay_period_no: number) {
|
||||
const period = await this.prisma.payPeriods.findFirst({
|
||||
where: { pay_year, pay_period_no },
|
||||
select: PAY_PERIOD_SELECT ,
|
||||
});
|
||||
if(!period) throw new NotFoundException(`period ${pay_year}-${pay_period_no} not found`);
|
||||
return period;
|
||||
}
|
||||
// async getPayPeriod(pay_year: number, pay_period_no: number) {
|
||||
// const period = await this.prisma.payPeriods.findFirst({
|
||||
// where: { pay_year, pay_period_no },
|
||||
// select: PAY_PERIOD_SELECT ,
|
||||
// });
|
||||
// if(!period) throw new NotFoundException(`period ${pay_year}-${pay_period_no} not found`);
|
||||
// return period;
|
||||
// }
|
||||
|
||||
async getShifts(employee_id: number, from: Date, to: Date) {
|
||||
return this.prisma.shifts.findMany({
|
||||
where: {timesheet: { is: { employee_id } }, date: { gte: from, lte: to } },
|
||||
select: SHIFT_SELECT,
|
||||
orderBy: SHIFT_ASC_ORDER,
|
||||
});
|
||||
}
|
||||
// async getShifts(employee_id: number, from: Date, to: Date) {
|
||||
// return this.prisma.shifts.findMany({
|
||||
// where: {timesheet: { is: { employee_id } }, date: { gte: from, lte: to } },
|
||||
// select: SHIFT_SELECT,
|
||||
// orderBy: SHIFT_ASC_ORDER,
|
||||
// });
|
||||
// }
|
||||
|
||||
async getExpenses(employee_id: number, from: Date, to: Date) {
|
||||
return this.prisma.expenses.findMany({
|
||||
where: { timesheet: {is: { employee_id } }, date: { gte: from, lte: to } },
|
||||
select: EXPENSE_SELECT,
|
||||
orderBy: EXPENSE_ASC_ORDER,
|
||||
});
|
||||
}
|
||||
// async getExpenses(employee_id: number, from: Date, to: Date) {
|
||||
// return this.prisma.expenses.findMany({
|
||||
// where: { timesheet: {is: { employee_id } }, date: { gte: from, lte: to } },
|
||||
// select: EXPENSE_SELECT,
|
||||
// orderBy: EXPENSE_ASC_ORDER,
|
||||
// });
|
||||
// }
|
||||
|
||||
async getTimesheetWithShiftsAndExpenses(employee_id: number, start_date_week: Date) {
|
||||
return this.prisma.timesheets.findUnique({
|
||||
where: { employee_id_start_date: { employee_id, start_date: start_date_week } },
|
||||
select: {
|
||||
is_approved: true,
|
||||
shift: { select: SHIFT_SELECT, orderBy: SHIFT_ASC_ORDER },
|
||||
expense: { select: EXPENSE_SELECT, orderBy: EXPENSE_ASC_ORDER },
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
// async getTimesheetWithShiftsAndExpenses(employee_id: number, start_date_week: Date) {
|
||||
// return this.prisma.timesheets.findUnique({
|
||||
// where: { employee_id_start_date: { employee_id, start_date: start_date_week } },
|
||||
// select: {
|
||||
// is_approved: true,
|
||||
// shift: { select: SHIFT_SELECT, orderBy: SHIFT_ASC_ORDER },
|
||||
// expense: { select: EXPENSE_SELECT, orderBy: EXPENSE_ASC_ORDER },
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
|
@ -1,74 +1,74 @@
|
|||
export type ShiftRow = {
|
||||
date: Date;
|
||||
start_time: Date;
|
||||
end_time: Date;
|
||||
comment: string;
|
||||
is_approved?: boolean;
|
||||
is_remote: boolean;
|
||||
type: string
|
||||
};
|
||||
export type ExpenseRow = {
|
||||
date: Date;
|
||||
amount: number;
|
||||
mileage?: number | null;
|
||||
comment: string;
|
||||
type: string;
|
||||
is_approved?: boolean;
|
||||
supervisor_comment: string;
|
||||
};
|
||||
// export type ShiftRow = {
|
||||
// date: Date;
|
||||
// start_time: Date;
|
||||
// end_time: Date;
|
||||
// comment: string;
|
||||
// is_approved?: boolean;
|
||||
// is_remote: boolean;
|
||||
// type: string
|
||||
// };
|
||||
// export type ExpenseRow = {
|
||||
// date: Date;
|
||||
// amount: number;
|
||||
// mileage?: number | null;
|
||||
// comment: string;
|
||||
// type: string;
|
||||
// is_approved?: boolean;
|
||||
// supervisor_comment: string;
|
||||
// };
|
||||
|
||||
export type TimesheetMap = {
|
||||
start_day: string;
|
||||
end_day: string;
|
||||
label: string;
|
||||
shifts: ShiftRow[];
|
||||
expenses: ExpenseRow[]
|
||||
is_approved: boolean;
|
||||
}
|
||||
// export type TimesheetMap = {
|
||||
// start_day: string;
|
||||
// end_day: string;
|
||||
// label: string;
|
||||
// shifts: ShiftRow[];
|
||||
// expenses: ExpenseRow[]
|
||||
// is_approved: boolean;
|
||||
// }
|
||||
|
||||
// Types
|
||||
export const SHIFT_TYPES = {
|
||||
REGULAR: 'REGULAR',
|
||||
EVENING: 'EVENING',
|
||||
OVERTIME: 'OVERTIME',
|
||||
EMERGENCY: 'EMERGENCY',
|
||||
HOLIDAY: 'HOLIDAY',
|
||||
VACATION: 'VACATION',
|
||||
SICK: 'SICK',
|
||||
} as const;
|
||||
// // Types
|
||||
// export const SHIFT_TYPES = {
|
||||
// REGULAR: 'REGULAR',
|
||||
// EVENING: 'EVENING',
|
||||
// OVERTIME: 'OVERTIME',
|
||||
// EMERGENCY: 'EMERGENCY',
|
||||
// HOLIDAY: 'HOLIDAY',
|
||||
// VACATION: 'VACATION',
|
||||
// SICK: 'SICK',
|
||||
// } as const;
|
||||
|
||||
export const EXPENSE_TYPES = {
|
||||
MILEAGE: 'MILEAGE',
|
||||
EXPENSE: 'EXPENSES',
|
||||
PER_DIEM: 'PER_DIEM',
|
||||
ON_CALL: 'ON_CALL',
|
||||
} as const;
|
||||
// export const EXPENSE_TYPES = {
|
||||
// MILEAGE: 'MILEAGE',
|
||||
// EXPENSE: 'EXPENSES',
|
||||
// PER_DIEM: 'PER_DIEM',
|
||||
// ON_CALL: 'ON_CALL',
|
||||
// } as const;
|
||||
|
||||
//makes the strings indexes for arrays
|
||||
export const DAY_KEYS = ['sun','mon','tue','wed','thu','fri','sat'] as const;
|
||||
export type DayKey = typeof DAY_KEYS[number];
|
||||
// //makes the strings indexes for arrays
|
||||
// export const DAY_KEYS = ['sun','mon','tue','wed','thu','fri','sat'] as const;
|
||||
// export type DayKey = typeof DAY_KEYS[number];
|
||||
|
||||
//shifts's hour by type
|
||||
export type ShiftsHours = {
|
||||
regular: number;
|
||||
evening: number;
|
||||
overtime: number;
|
||||
emergency: number;
|
||||
sick: number;
|
||||
vacation: number;
|
||||
holiday: number;
|
||||
};
|
||||
export const make_hours = (): ShiftsHours => ({
|
||||
regular: 0,
|
||||
evening: 0,
|
||||
overtime: 0,
|
||||
emergency: 0,
|
||||
sick: 0,
|
||||
vacation: 0,
|
||||
holiday: 0,
|
||||
});
|
||||
// //shifts's hour by type
|
||||
// export type ShiftsHours = {
|
||||
// regular: number;
|
||||
// evening: number;
|
||||
// overtime: number;
|
||||
// emergency: number;
|
||||
// sick: number;
|
||||
// vacation: number;
|
||||
// holiday: number;
|
||||
// };
|
||||
// export const make_hours = (): ShiftsHours => ({
|
||||
// regular: 0,
|
||||
// evening: 0,
|
||||
// overtime: 0,
|
||||
// emergency: 0,
|
||||
// sick: 0,
|
||||
// vacation: 0,
|
||||
// holiday: 0,
|
||||
// });
|
||||
|
||||
export type ExpensesAmount = {
|
||||
expense: number;
|
||||
mileage: number;
|
||||
};
|
||||
// export type ExpensesAmount = {
|
||||
// expense: number;
|
||||
// mileage: number;
|
||||
// };
|
||||
|
|
@ -1,171 +1,171 @@
|
|||
import {
|
||||
DayKey, DAY_KEYS, EXPENSE_TYPES, ExpenseRow,
|
||||
SHIFT_TYPES, ShiftRow, make_hours, ShiftsHours, ExpensesAmount
|
||||
} from "./timesheet.types";
|
||||
import {
|
||||
isBetweenUTC, dayKeyFromDate, toTimeString, round2,
|
||||
toUTCDateOnly, endOfDayUTC, addDays
|
||||
} from "./timesheet.helpers";
|
||||
import { WeekDto, ShiftDto, TimesheetPeriodDto, DayExpensesDto, ExpenseDto } from "../dtos/timesheet-period.dto";
|
||||
import { getWeekStart, getWeekEnd, formatDateISO } from "src/common/utils/date-utils";
|
||||
import { makeAmounts, makeEmptyWeek } from "./timesheet.mappers";
|
||||
import { toDateString } from "src/modules/pay-periods/utils/pay-year.util";
|
||||
import { MS_PER_HOUR } from "src/modules/shared/constants/date-time.constant";
|
||||
// import {
|
||||
// DayKey, DAY_KEYS, EXPENSE_TYPES, ExpenseRow,
|
||||
// SHIFT_TYPES, ShiftRow, make_hours, ShiftsHours, ExpensesAmount
|
||||
// } from "./timesheet.types";
|
||||
// import {
|
||||
// isBetweenUTC, dayKeyFromDate, toTimeString, round2,
|
||||
// toUTCDateOnly, endOfDayUTC, addDays
|
||||
// } from "./timesheet.helpers";
|
||||
// import { WeekDto, ShiftDto, TimesheetPeriodDto, DayExpensesDto, ExpenseDto } from "../dtos/timesheet-period.dto";
|
||||
// import { getWeekStart, getWeekEnd, formatDateISO } from "src/common/utils/date-utils";
|
||||
// import { makeAmounts, makeEmptyWeek } from "./timesheet.mappers";
|
||||
// import { toDateString } from "src/modules/pay-periods/utils/pay-year.util";
|
||||
// import { MS_PER_HOUR } from "src/modules/shared/constants/date-time.constant";
|
||||
|
||||
export function computeWeekRange(week_offset = 0){
|
||||
//sets current week Sunday -> Saturday
|
||||
const base = new Date();
|
||||
const offset = new Date(base);
|
||||
offset.setDate(offset.getDate() + (week_offset * 7));
|
||||
// export function computeWeekRange(week_offset = 0){
|
||||
// //sets current week Sunday -> Saturday
|
||||
// const base = new Date();
|
||||
// const offset = new Date(base);
|
||||
// offset.setDate(offset.getDate() + (week_offset * 7));
|
||||
|
||||
const start = getWeekStart(offset, 0);
|
||||
const end = getWeekEnd(start);
|
||||
const start_day = formatDateISO(start);
|
||||
const end_day = formatDateISO(end);
|
||||
const label = `${(start_day)}.${(end_day)}`;
|
||||
// const start = getWeekStart(offset, 0);
|
||||
// const end = getWeekEnd(start);
|
||||
// const start_day = formatDateISO(start);
|
||||
// const end_day = formatDateISO(end);
|
||||
// const label = `${(start_day)}.${(end_day)}`;
|
||||
|
||||
return { start, end, start_day, end_day, label }
|
||||
};
|
||||
// return { start, end, start_day, end_day, label }
|
||||
// };
|
||||
|
||||
export function buildWeek(
|
||||
week_start: Date,
|
||||
week_end: Date,
|
||||
shifts: ShiftRow[],
|
||||
expenses: ExpenseRow[],
|
||||
): WeekDto {
|
||||
const week = makeEmptyWeek(week_start);
|
||||
let all_approved = true;
|
||||
// export function buildWeek(
|
||||
// week_start: Date,
|
||||
// week_end: Date,
|
||||
// shifts: ShiftRow[],
|
||||
// expenses: ExpenseRow[],
|
||||
// ): WeekDto {
|
||||
// const week = makeEmptyWeek(week_start);
|
||||
// let all_approved = true;
|
||||
|
||||
const day_times: Record<DayKey, Array<{ start: Date; end: Date }>> = DAY_KEYS.reduce((acc, key) => {
|
||||
acc[key] = []; return acc;
|
||||
}, {} as Record<DayKey, Array<{ start: Date; end: Date}>>);
|
||||
// const day_times: Record<DayKey, Array<{ start: Date; end: Date }>> = DAY_KEYS.reduce((acc, key) => {
|
||||
// acc[key] = []; return acc;
|
||||
// }, {} as Record<DayKey, Array<{ start: Date; end: Date}>>);
|
||||
|
||||
const day_hours: Record<DayKey, ShiftsHours> = DAY_KEYS.reduce((acc, key) => {
|
||||
acc[key] = make_hours(); return acc;
|
||||
}, {} as Record<DayKey, ShiftsHours>);
|
||||
// const day_hours: Record<DayKey, ShiftsHours> = DAY_KEYS.reduce((acc, key) => {
|
||||
// acc[key] = make_hours(); return acc;
|
||||
// }, {} as Record<DayKey, ShiftsHours>);
|
||||
|
||||
const day_amounts: Record<DayKey, ExpensesAmount> = DAY_KEYS.reduce((acc, key) => {
|
||||
acc[key] = makeAmounts(); return acc;
|
||||
}, {} as Record<DayKey, ExpensesAmount>);
|
||||
// const day_amounts: Record<DayKey, ExpensesAmount> = DAY_KEYS.reduce((acc, key) => {
|
||||
// acc[key] = makeAmounts(); return acc;
|
||||
// }, {} as Record<DayKey, ExpensesAmount>);
|
||||
|
||||
const day_expense_rows: Record<DayKey, DayExpensesDto> = DAY_KEYS.reduce((acc, key) => {
|
||||
acc[key] = {
|
||||
expenses: [{
|
||||
type: '',
|
||||
amount: -1,
|
||||
mileage: -1,
|
||||
comment: '',
|
||||
is_approved: false,
|
||||
supervisor_comment: '',
|
||||
}],
|
||||
total_expense: -1,
|
||||
total_mileage: -1,
|
||||
};
|
||||
return acc;
|
||||
}, {} as Record<DayKey, DayExpensesDto>);
|
||||
// const day_expense_rows: Record<DayKey, DayExpensesDto> = DAY_KEYS.reduce((acc, key) => {
|
||||
// acc[key] = {
|
||||
// expenses: [{
|
||||
// type: '',
|
||||
// amount: -1,
|
||||
// mileage: -1,
|
||||
// comment: '',
|
||||
// is_approved: false,
|
||||
// supervisor_comment: '',
|
||||
// }],
|
||||
// total_expense: -1,
|
||||
// total_mileage: -1,
|
||||
// };
|
||||
// return acc;
|
||||
// }, {} as Record<DayKey, DayExpensesDto>);
|
||||
|
||||
//regroup hours per type of shifts
|
||||
const week_shifts = shifts.filter(shift => isBetweenUTC(shift.date, week_start, week_end));
|
||||
for (const shift of week_shifts) {
|
||||
const key = dayKeyFromDate(shift.date, true);
|
||||
week.shifts[key].shifts.push({
|
||||
date: toDateString(shift.date),
|
||||
type: shift.type,
|
||||
start_time: toTimeString(shift.start_time),
|
||||
end_time: toTimeString(shift.end_time),
|
||||
comment: shift.comment,
|
||||
is_approved: shift.is_approved ?? true,
|
||||
is_remote: shift.is_remote,
|
||||
} as ShiftDto);
|
||||
// //regroup hours per type of shifts
|
||||
// const week_shifts = shifts.filter(shift => isBetweenUTC(shift.date, week_start, week_end));
|
||||
// for (const shift of week_shifts) {
|
||||
// const key = dayKeyFromDate(shift.date, true);
|
||||
// week.shifts[key].shifts.push({
|
||||
// date: toDateString(shift.date),
|
||||
// type: shift.type,
|
||||
// start_time: toTimeString(shift.start_time),
|
||||
// end_time: toTimeString(shift.end_time),
|
||||
// comment: shift.comment,
|
||||
// is_approved: shift.is_approved ?? true,
|
||||
// is_remote: shift.is_remote,
|
||||
// } as ShiftDto);
|
||||
|
||||
day_times[key].push({ start: shift.start_time, end: shift.end_time});
|
||||
// day_times[key].push({ start: shift.start_time, end: shift.end_time});
|
||||
|
||||
const duration = Math.max(0, (shift.end_time.getTime() - shift.start_time.getTime())/ MS_PER_HOUR);
|
||||
const type = (shift.type || '').toUpperCase();
|
||||
// const duration = Math.max(0, (shift.end_time.getTime() - shift.start_time.getTime())/ MS_PER_HOUR);
|
||||
// const type = (shift.type || '').toUpperCase();
|
||||
|
||||
if ( type === SHIFT_TYPES.REGULAR) day_hours[key].regular += duration;
|
||||
else if( type === SHIFT_TYPES.EVENING) day_hours[key].evening += duration;
|
||||
else if( type === SHIFT_TYPES.EMERGENCY) day_hours[key].emergency += duration;
|
||||
else if( type === SHIFT_TYPES.OVERTIME) day_hours[key].overtime += duration;
|
||||
else if( type === SHIFT_TYPES.SICK) day_hours[key].sick += duration;
|
||||
else if( type === SHIFT_TYPES.VACATION) day_hours[key].vacation += duration;
|
||||
else if( type === SHIFT_TYPES.HOLIDAY) day_hours[key].holiday += duration;
|
||||
// if ( type === SHIFT_TYPES.REGULAR) day_hours[key].regular += duration;
|
||||
// else if( type === SHIFT_TYPES.EVENING) day_hours[key].evening += duration;
|
||||
// else if( type === SHIFT_TYPES.EMERGENCY) day_hours[key].emergency += duration;
|
||||
// else if( type === SHIFT_TYPES.OVERTIME) day_hours[key].overtime += duration;
|
||||
// else if( type === SHIFT_TYPES.SICK) day_hours[key].sick += duration;
|
||||
// else if( type === SHIFT_TYPES.VACATION) day_hours[key].vacation += duration;
|
||||
// else if( type === SHIFT_TYPES.HOLIDAY) day_hours[key].holiday += duration;
|
||||
|
||||
all_approved = all_approved && (shift.is_approved ?? true );
|
||||
}
|
||||
// all_approved = all_approved && (shift.is_approved ?? true );
|
||||
// }
|
||||
|
||||
//regroupe amounts to type of expenses
|
||||
const week_expenses = expenses.filter(expense => isBetweenUTC(expense.date, week_start, week_end));
|
||||
for (const expense of week_expenses) {
|
||||
const key = dayKeyFromDate(expense.date, true);
|
||||
const type = (expense.type || '').toUpperCase();
|
||||
// //regroupe amounts to type of expenses
|
||||
// const week_expenses = expenses.filter(expense => isBetweenUTC(expense.date, week_start, week_end));
|
||||
// for (const expense of week_expenses) {
|
||||
// const key = dayKeyFromDate(expense.date, true);
|
||||
// const type = (expense.type || '').toUpperCase();
|
||||
|
||||
const row: ExpenseDto = {
|
||||
type,
|
||||
amount: round2(expense.amount ?? 0),
|
||||
mileage: round2(expense.mileage ?? 0),
|
||||
comment: expense.comment ?? '',
|
||||
is_approved: expense.is_approved ?? true,
|
||||
supervisor_comment: expense.supervisor_comment ?? '',
|
||||
};
|
||||
// const row: ExpenseDto = {
|
||||
// type,
|
||||
// amount: round2(expense.amount ?? 0),
|
||||
// mileage: round2(expense.mileage ?? 0),
|
||||
// comment: expense.comment ?? '',
|
||||
// is_approved: expense.is_approved ?? true,
|
||||
// supervisor_comment: expense.supervisor_comment ?? '',
|
||||
// };
|
||||
|
||||
day_expense_rows[key].expenses.push(row);
|
||||
// day_expense_rows[key].expenses.push(row);
|
||||
|
||||
if(type === EXPENSE_TYPES.MILEAGE) {
|
||||
day_amounts[key].mileage += row.mileage ?? 0;
|
||||
} else {
|
||||
day_amounts[key].expense += row.amount;
|
||||
}
|
||||
// if(type === EXPENSE_TYPES.MILEAGE) {
|
||||
// day_amounts[key].mileage += row.mileage ?? 0;
|
||||
// } else {
|
||||
// day_amounts[key].expense += row.amount;
|
||||
// }
|
||||
|
||||
all_approved = all_approved && row.is_approved;
|
||||
}
|
||||
// all_approved = all_approved && row.is_approved;
|
||||
// }
|
||||
|
||||
for (const key of DAY_KEYS) {
|
||||
//return exposed dto data
|
||||
week.shifts[key].regular_hours = round2(day_hours[key].regular);
|
||||
week.shifts[key].evening_hours = round2(day_hours[key].evening);
|
||||
week.shifts[key].overtime_hours = round2(day_hours[key].overtime);
|
||||
week.shifts[key].emergency_hours = round2(day_hours[key].emergency);
|
||||
// for (const key of DAY_KEYS) {
|
||||
// //return exposed dto data
|
||||
// week.shifts[key].regular_hours = round2(day_hours[key].regular);
|
||||
// week.shifts[key].evening_hours = round2(day_hours[key].evening);
|
||||
// week.shifts[key].overtime_hours = round2(day_hours[key].overtime);
|
||||
// week.shifts[key].emergency_hours = round2(day_hours[key].emergency);
|
||||
|
||||
//calculate gaps between shifts
|
||||
const times = day_times[key].sort((a,b) => a.start.getTime() - b.start.getTime());
|
||||
let gaps = 0;
|
||||
for (let i = 1; i < times.length; i++) {
|
||||
const gap = (times[i].start.getTime() - times[i - 1].end.getTime()) / MS_PER_HOUR;
|
||||
if(gap > 0) gaps += gap;
|
||||
}
|
||||
week.shifts[key].break_durations = round2(gaps);
|
||||
// //calculate gaps between shifts
|
||||
// const times = day_times[key].sort((a,b) => a.start.getTime() - b.start.getTime());
|
||||
// let gaps = 0;
|
||||
// for (let i = 1; i < times.length; i++) {
|
||||
// const gap = (times[i].start.getTime() - times[i - 1].end.getTime()) / MS_PER_HOUR;
|
||||
// if(gap > 0) gaps += gap;
|
||||
// }
|
||||
// week.shifts[key].break_durations = round2(gaps);
|
||||
|
||||
//daily totals
|
||||
const totals = day_amounts[key];
|
||||
// //daily totals
|
||||
// const totals = day_amounts[key];
|
||||
|
||||
day_expense_rows[key].total_mileage = round2(totals.mileage);
|
||||
day_expense_rows[key].total_expense = round2(totals.expense);
|
||||
}
|
||||
// day_expense_rows[key].total_mileage = round2(totals.mileage);
|
||||
// day_expense_rows[key].total_expense = round2(totals.expense);
|
||||
// }
|
||||
|
||||
week.is_approved = all_approved;
|
||||
return week;
|
||||
}
|
||||
// week.is_approved = all_approved;
|
||||
// return week;
|
||||
// }
|
||||
|
||||
export function buildPeriod(
|
||||
period_start: Date,
|
||||
period_end: Date,
|
||||
shifts: ShiftRow[],
|
||||
expenses: ExpenseRow[],
|
||||
employeeFullName = ''
|
||||
): TimesheetPeriodDto {
|
||||
const week1_start = toUTCDateOnly(period_start);
|
||||
const week1_end = endOfDayUTC(addDays(week1_start, 6));
|
||||
const week2_start = toUTCDateOnly(addDays(week1_start, 7));
|
||||
const week2_end = endOfDayUTC(period_end);
|
||||
// export function buildPeriod(
|
||||
// period_start: Date,
|
||||
// period_end: Date,
|
||||
// shifts: ShiftRow[],
|
||||
// expenses: ExpenseRow[],
|
||||
// employeeFullName = ''
|
||||
// ): TimesheetPeriodDto {
|
||||
// const week1_start = toUTCDateOnly(period_start);
|
||||
// const week1_end = endOfDayUTC(addDays(week1_start, 6));
|
||||
// const week2_start = toUTCDateOnly(addDays(week1_start, 7));
|
||||
// const week2_end = endOfDayUTC(period_end);
|
||||
|
||||
const weeks: WeekDto[] = [
|
||||
buildWeek(week1_start, week1_end, shifts, expenses),
|
||||
buildWeek(week2_start, week2_end, shifts, expenses),
|
||||
];
|
||||
// const weeks: WeekDto[] = [
|
||||
// buildWeek(week1_start, week1_end, shifts, expenses),
|
||||
// buildWeek(week2_start, week2_end, shifts, expenses),
|
||||
// ];
|
||||
|
||||
return {
|
||||
weeks,
|
||||
employee_full_name: employeeFullName,
|
||||
};
|
||||
}
|
||||
// return {
|
||||
// weeks,
|
||||
// employee_full_name: employeeFullName,
|
||||
// };
|
||||
// }
|
||||
|
|
@ -1,137 +1,137 @@
|
|||
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||
import { EmployeeTimesheetResolver } from "src/modules/shared/utils/resolve-timesheet.utils";
|
||||
import { getWeekEnd, getWeekStart } from "src/common/utils/date-utils";
|
||||
import { parseISODate, parseHHmm } from "./utils-helpers-others/timesheet.helpers";
|
||||
import { TimesheetsQueryService } from "./timesheets-query.service";
|
||||
import { BaseApprovalService } from "src/common/shared/base-approval.service";
|
||||
import { Prisma, Timesheets } from "@prisma/client";
|
||||
import { CreateTimesheetDto } from "./create-timesheet.dto";
|
||||
import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
|
||||
import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { TimesheetMap } from "./utils-helpers-others/timesheet.types";
|
||||
import { Shift, Expense } from "../dtos/timesheet.dto";
|
||||
// import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||
// import { EmployeeTimesheetResolver } from "src/modules/shared/utils/resolve-timesheet.utils";
|
||||
// import { getWeekEnd, getWeekStart } from "src/common/utils/date-utils";
|
||||
// import { parseISODate, parseHHmm } from "./utils-helpers-others/timesheet.helpers";
|
||||
// import { TimesheetsQueryService } from "./timesheets-query.service";
|
||||
// import { BaseApprovalService } from "src/common/shared/base-approval.service";
|
||||
// import { Prisma, Timesheets } from "@prisma/client";
|
||||
// import { CreateTimesheetDto } from "./create-timesheet.dto";
|
||||
// import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
|
||||
// import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils";
|
||||
// import { PrismaService } from "src/prisma/prisma.service";
|
||||
// import { TimesheetMap } from "./utils-helpers-others/timesheet.types";
|
||||
// import { Shift, Expense } from "../dtos/timesheet.dto";
|
||||
|
||||
@Injectable()
|
||||
export class TimesheetsCommandService extends BaseApprovalService<Timesheets>{
|
||||
constructor(
|
||||
prisma: PrismaService,
|
||||
private readonly query: TimesheetsQueryService,
|
||||
private readonly emailResolver: EmailToIdResolver,
|
||||
private readonly timesheetResolver: EmployeeTimesheetResolver,
|
||||
private readonly bankTypeResolver: BankCodesResolver,
|
||||
) {super(prisma);}
|
||||
//_____________________________________________________________________________________________
|
||||
// APPROVAL AND DELEGATE METHODS
|
||||
//_____________________________________________________________________________________________
|
||||
protected get delegate() {
|
||||
return this.prisma.timesheets;
|
||||
}
|
||||
// @Injectable()
|
||||
// export class TimesheetsCommandService extends BaseApprovalService<Timesheets>{
|
||||
// constructor(
|
||||
// prisma: PrismaService,
|
||||
// private readonly query: TimesheetsQueryService,
|
||||
// private readonly emailResolver: EmailToIdResolver,
|
||||
// private readonly timesheetResolver: EmployeeTimesheetResolver,
|
||||
// private readonly bankTypeResolver: BankCodesResolver,
|
||||
// ) {super(prisma);}
|
||||
// //_____________________________________________________________________________________________
|
||||
// // APPROVAL AND DELEGATE METHODS
|
||||
// //_____________________________________________________________________________________________
|
||||
// protected get delegate() {
|
||||
// return this.prisma.timesheets;
|
||||
// }
|
||||
|
||||
protected delegateFor(transaction: Prisma.TransactionClient) {
|
||||
return transaction.timesheets;
|
||||
}
|
||||
// protected delegateFor(transaction: Prisma.TransactionClient) {
|
||||
// return transaction.timesheets;
|
||||
// }
|
||||
|
||||
async updateApproval(id: number, isApproved: boolean): Promise<Timesheets> {
|
||||
return this.prisma.$transaction((transaction) =>
|
||||
this.updateApprovalWithTransaction(transaction, id, isApproved),
|
||||
);
|
||||
}
|
||||
// async updateApproval(id: number, isApproved: boolean): Promise<Timesheets> {
|
||||
// return this.prisma.$transaction((transaction) =>
|
||||
// this.updateApprovalWithTransaction(transaction, id, isApproved),
|
||||
// );
|
||||
// }
|
||||
|
||||
async cascadeApprovalWithtx(transaction: Prisma.TransactionClient, timesheetId: number, isApproved: boolean): Promise<Timesheets> {
|
||||
const timesheet = await this.updateApprovalWithTransaction(transaction, timesheetId, isApproved);
|
||||
await transaction.shifts.updateMany({
|
||||
where: { timesheet_id: timesheetId },
|
||||
data: { is_approved: isApproved },
|
||||
});
|
||||
await transaction.expenses.updateManyAndReturn({
|
||||
where: { timesheet_id: timesheetId },
|
||||
data: { is_approved: isApproved },
|
||||
});
|
||||
return timesheet;
|
||||
}
|
||||
// async cascadeApprovalWithtx(transaction: Prisma.TransactionClient, timesheetId: number, isApproved: boolean): Promise<Timesheets> {
|
||||
// const timesheet = await this.updateApprovalWithTransaction(transaction, timesheetId, isApproved);
|
||||
// await transaction.shifts.updateMany({
|
||||
// where: { timesheet_id: timesheetId },
|
||||
// data: { is_approved: isApproved },
|
||||
// });
|
||||
// await transaction.expenses.updateManyAndReturn({
|
||||
// where: { timesheet_id: timesheetId },
|
||||
// data: { is_approved: isApproved },
|
||||
// });
|
||||
// return timesheet;
|
||||
// }
|
||||
|
||||
/**_____________________________________________________________________________________________
|
||||
create/update/delete shifts and expenses from 1 or many timesheet(s)
|
||||
// /**_____________________________________________________________________________________________
|
||||
// create/update/delete shifts and expenses from 1 or many timesheet(s)
|
||||
|
||||
-this function receives an email and an array of timesheets
|
||||
// -this function receives an email and an array of timesheets
|
||||
|
||||
-this function will find the timesheets with all shifts and expenses
|
||||
-this function will calculate total hours, total expenses, filtered by types,
|
||||
cumulate in daily and weekly.
|
||||
// -this function will find the timesheets with all shifts and expenses
|
||||
// -this function will calculate total hours, total expenses, filtered by types,
|
||||
// cumulate in daily and weekly.
|
||||
|
||||
-the timesheet_id will be determined using the employee email
|
||||
-with the timesheet_id, all shifts and expenses will be fetched
|
||||
// -the timesheet_id will be determined using the employee email
|
||||
// -with the timesheet_id, all shifts and expenses will be fetched
|
||||
|
||||
-with shift_id and expense_id, this function will compare both
|
||||
datas from the DB and from the body of the function and then:
|
||||
-it will create a shift if no shift is found in the DB
|
||||
-it will update a shift if a shift is found in the DB
|
||||
-it will delete a shift if a shift is found and no data is received from the frontend
|
||||
// -with shift_id and expense_id, this function will compare both
|
||||
// datas from the DB and from the body of the function and then:
|
||||
// -it will create a shift if no shift is found in the DB
|
||||
// -it will update a shift if a shift is found in the DB
|
||||
// -it will delete a shift if a shift is found and no data is received from the frontend
|
||||
|
||||
This function will be used for the Timesheet Page for an employee to enter, modify or delete and entry
|
||||
This function will also be used in the modal of the timesheet validation page to
|
||||
allow a supervisor to enter, modify or delete and entry of a selected employee
|
||||
_____________________________________________________________________________________________*/
|
||||
// This function will be used for the Timesheet Page for an employee to enter, modify or delete and entry
|
||||
// This function will also be used in the modal of the timesheet validation page to
|
||||
// allow a supervisor to enter, modify or delete and entry of a selected employee
|
||||
// _____________________________________________________________________________________________*/
|
||||
|
||||
async findTimesheetsByEmailAndPayPeriod(email: string, year: number, period_no: number, timesheets: Timesheets[]): Promise<Timesheets[]> {
|
||||
const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||
// async findTimesheetsByEmailAndPayPeriod(email: string, year: number, period_no: number, timesheets: Timesheets[]): Promise<Timesheets[]> {
|
||||
// const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||
|
||||
|
||||
return timesheets;
|
||||
}
|
||||
// return timesheets;
|
||||
// }
|
||||
|
||||
async upsertOrDeleteShiftsByEmailAndDate(email:string, shift_ids: Shift[]) {}
|
||||
// async upsertOrDeleteShiftsByEmailAndDate(email:string, shift_ids: Shift[]) {}
|
||||
|
||||
async upsertOrDeleteExpensesByEmailAndDate(email:string, expenses_id: Expense[]) {}
|
||||
// async upsertOrDeleteExpensesByEmailAndDate(email:string, expenses_id: Expense[]) {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//_____________________________________________________________________________________________
|
||||
//
|
||||
//_____________________________________________________________________________________________
|
||||
// //_____________________________________________________________________________________________
|
||||
// //
|
||||
// //_____________________________________________________________________________________________
|
||||
|
||||
async createWeekShiftsAndReturnOverview(
|
||||
email:string,
|
||||
shifts: CreateTimesheetDto[],
|
||||
week_offset = 0,
|
||||
): Promise<TimesheetMap> {
|
||||
//fetchs employee matchint user's email
|
||||
const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||
if(!employee_id) throw new NotFoundException(`employee for ${ email } not found`);
|
||||
// async createWeekShiftsAndReturnOverview(
|
||||
// email:string,
|
||||
// shifts: CreateTimesheetDto[],
|
||||
// week_offset = 0,
|
||||
// ): Promise<TimesheetMap> {
|
||||
// //fetchs employee matchint user's email
|
||||
// const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||
// if(!employee_id) throw new NotFoundException(`employee for ${ email } not found`);
|
||||
|
||||
//insure that the week starts on sunday and finishes on saturday
|
||||
const base = new Date();
|
||||
base.setDate(base.getDate() + week_offset * 7);
|
||||
const start_week = getWeekStart(base, 0);
|
||||
const end_week = getWeekEnd(start_week);
|
||||
// //insure that the week starts on sunday and finishes on saturday
|
||||
// const base = new Date();
|
||||
// base.setDate(base.getDate() + week_offset * 7);
|
||||
// const start_week = getWeekStart(base, 0);
|
||||
// const end_week = getWeekEnd(start_week);
|
||||
|
||||
const timesheet = await this.timesheetResolver.findTimesheetIdByEmail(email, base)
|
||||
if(!timesheet) throw new NotFoundException(`no timesheet found for employe ${employee_id}`);
|
||||
// const timesheet = await this.timesheetResolver.findTimesheetIdByEmail(email, base)
|
||||
// if(!timesheet) throw new NotFoundException(`no timesheet found for employe ${employee_id}`);
|
||||
|
||||
//validations and insertions
|
||||
for(const shift of shifts) {
|
||||
const date = parseISODate(shift.date);
|
||||
if (date < start_week || date > end_week) throw new BadRequestException(`date ${shift.date} not in current week`);
|
||||
// //validations and insertions
|
||||
// for(const shift of shifts) {
|
||||
// const date = parseISODate(shift.date);
|
||||
// if (date < start_week || date > end_week) throw new BadRequestException(`date ${shift.date} not in current week`);
|
||||
|
||||
const bank_code = await this.bankTypeResolver.findByType(shift.type)
|
||||
if(!bank_code) throw new BadRequestException(`Invalid bank_code type: ${shift.type}`);
|
||||
// const bank_code = await this.bankTypeResolver.findByType(shift.type)
|
||||
// if(!bank_code) throw new BadRequestException(`Invalid bank_code type: ${shift.type}`);
|
||||
|
||||
await this.prisma.shifts.create({
|
||||
data: {
|
||||
timesheet_id: timesheet.id,
|
||||
bank_code_id: bank_code.id,
|
||||
date: date,
|
||||
start_time: parseHHmm(shift.start_time),
|
||||
end_time: parseHHmm(shift.end_time),
|
||||
comment: shift.comment ?? null,
|
||||
is_approved: false,
|
||||
is_remote: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
return this.query.getTimesheetByEmail(email, week_offset);
|
||||
}
|
||||
}
|
||||
// await this.prisma.shifts.create({
|
||||
// data: {
|
||||
// timesheet_id: timesheet.id,
|
||||
// bank_code_id: bank_code.id,
|
||||
// date: date,
|
||||
// start_time: parseHHmm(shift.start_time),
|
||||
// end_time: parseHHmm(shift.end_time),
|
||||
// comment: shift.comment ?? null,
|
||||
// is_approved: false,
|
||||
// is_remote: false,
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
// return this.query.getTimesheetByEmail(email, week_offset);
|
||||
// }
|
||||
// }
|
||||
|
|
@ -1,54 +1,54 @@
|
|||
import { makeEmptyTimesheet, mapExpenseRow, mapShiftRow } from './utils-helpers-others/timesheet.mappers';
|
||||
import { buildPeriod, computeWeekRange } from './utils-helpers-others/timesheet.utils';
|
||||
import { TimesheetSelectorsService } from './utils-helpers-others/timesheet.selectors';
|
||||
import { TimesheetPeriodDto } from './timesheet-period.dto';
|
||||
import { toRangeFromPeriod } from './utils-helpers-others/timesheet.helpers';
|
||||
import { EmailToIdResolver } from 'src/modules/shared/utils/resolve-email-id.utils';
|
||||
import { FullNameResolver } from 'src/modules/shared/utils/resolve-full-name.utils';
|
||||
import { PrismaService } from 'src/prisma/prisma.service';
|
||||
import { TimesheetMap } from './utils-helpers-others/timesheet.types';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
// import { makeEmptyTimesheet, mapExpenseRow, mapShiftRow } from './utils-helpers-others/timesheet.mappers';
|
||||
// import { buildPeriod, computeWeekRange } from './utils-helpers-others/timesheet.utils';
|
||||
// import { TimesheetSelectorsService } from './utils-helpers-others/timesheet.selectors';
|
||||
// import { TimesheetPeriodDto } from './timesheet-period.dto';
|
||||
// import { toRangeFromPeriod } from './utils-helpers-others/timesheet.helpers';
|
||||
// import { EmailToIdResolver } from 'src/modules/shared/utils/resolve-email-id.utils';
|
||||
// import { FullNameResolver } from 'src/modules/shared/utils/resolve-full-name.utils';
|
||||
// import { PrismaService } from 'src/prisma/prisma.service';
|
||||
// import { TimesheetMap } from './utils-helpers-others/timesheet.types';
|
||||
// import { Injectable } from '@nestjs/common';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class TimesheetsQueryService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly emailResolver: EmailToIdResolver,
|
||||
private readonly fullNameResolver: FullNameResolver,
|
||||
private readonly selectors: TimesheetSelectorsService,
|
||||
) {}
|
||||
// @Injectable()
|
||||
// export class TimesheetsQueryService {
|
||||
// constructor(
|
||||
// private readonly prisma: PrismaService,
|
||||
// private readonly emailResolver: EmailToIdResolver,
|
||||
// private readonly fullNameResolver: FullNameResolver,
|
||||
// private readonly selectors: TimesheetSelectorsService,
|
||||
// ) {}
|
||||
|
||||
async findAll(year: number, period_no: number, email: string): Promise<TimesheetPeriodDto> {
|
||||
const employee_id = await this.emailResolver.findIdByEmail(email); //finds the employee using email
|
||||
const full_name = await this.fullNameResolver.resolveFullName(employee_id); //finds the employee full name using employee_id
|
||||
const period = await this.selectors.getPayPeriod(year, period_no);//finds the pay period using year and period_no
|
||||
const{ from, to } = toRangeFromPeriod(period); //finds start and end dates
|
||||
//finds all shifts from selected period
|
||||
const [raw_shifts, raw_expenses] = await Promise.all([
|
||||
this.selectors.getShifts(employee_id, from, to),
|
||||
this.selectors.getExpenses(employee_id, from, to),
|
||||
]);
|
||||
// data mapping
|
||||
const shifts = raw_shifts.map(mapShiftRow);
|
||||
const expenses = raw_expenses.map(mapExpenseRow);
|
||||
// async findAll(year: number, period_no: number, email: string): Promise<TimesheetPeriodDto> {
|
||||
// const employee_id = await this.emailResolver.findIdByEmail(email); //finds the employee using email
|
||||
// const full_name = await this.fullNameResolver.resolveFullName(employee_id); //finds the employee full name using employee_id
|
||||
// const period = await this.selectors.getPayPeriod(year, period_no);//finds the pay period using year and period_no
|
||||
// const{ from, to } = toRangeFromPeriod(period); //finds start and end dates
|
||||
// //finds all shifts from selected period
|
||||
// const [raw_shifts, raw_expenses] = await Promise.all([
|
||||
// this.selectors.getShifts(employee_id, from, to),
|
||||
// this.selectors.getExpenses(employee_id, from, to),
|
||||
// ]);
|
||||
// // data mapping
|
||||
// const shifts = raw_shifts.map(mapShiftRow);
|
||||
// const expenses = raw_expenses.map(mapExpenseRow);
|
||||
|
||||
return buildPeriod(period.period_start, period.period_end, shifts , expenses, full_name);
|
||||
}
|
||||
// return buildPeriod(period.period_start, period.period_end, shifts , expenses, full_name);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
async getTimesheetByEmail(email: string, week_offset = 0): Promise<TimesheetMap> {
|
||||
const employee_id = await this.emailResolver.findIdByEmail(email); //finds the employee using email
|
||||
const { start, start_day, end_day, label } = computeWeekRange(week_offset);
|
||||
const timesheet = await this.selectors.getTimesheetWithShiftsAndExpenses(employee_id, start); //fetch timesheet shifts and expenses
|
||||
if(!timesheet) return makeEmptyTimesheet({ start_day, end_day, label});
|
||||
// async getTimesheetByEmail(email: string, week_offset = 0): Promise<TimesheetMap> {
|
||||
// const employee_id = await this.emailResolver.findIdByEmail(email); //finds the employee using email
|
||||
// const { start, start_day, end_day, label } = computeWeekRange(week_offset);
|
||||
// const timesheet = await this.selectors.getTimesheetWithShiftsAndExpenses(employee_id, start); //fetch timesheet shifts and expenses
|
||||
// if(!timesheet) return makeEmptyTimesheet({ start_day, end_day, label});
|
||||
|
||||
//maps all shifts of selected timesheet
|
||||
const shifts = timesheet.shift.map(mapShiftRow);
|
||||
const expenses = timesheet.expense.map(mapExpenseRow);
|
||||
// //maps all shifts of selected timesheet
|
||||
// const shifts = timesheet.shift.map(mapShiftRow);
|
||||
// const expenses = timesheet.expense.map(mapExpenseRow);
|
||||
|
||||
|
||||
return { start_day, end_day, label, shifts, expenses, is_approved: timesheet.is_approved};
|
||||
}
|
||||
}
|
||||
// return { start_day, end_day, label, shifts, expenses, is_approved: timesheet.is_approved};
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,51 +1,51 @@
|
|||
import { BadRequestException, Body, Controller, Get, Param, ParseIntPipe, Post, Query } from '@nestjs/common';
|
||||
import { TimesheetsQueryService } from './timesheets-query.service';
|
||||
import { CreateWeekShiftsDto } from './create-timesheet.dto';
|
||||
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
||||
import { Roles as RoleEnum } from '.prisma/client';
|
||||
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||
import { TimesheetsCommandService } from './timesheets-command.service';
|
||||
import { TimesheetPeriodDto } from './timesheet-period.dto';
|
||||
import { TimesheetMap } from './timesheet.types';
|
||||
// import { BadRequestException, Body, Controller, Get, Param, ParseIntPipe, Post, Query } from '@nestjs/common';
|
||||
// import { TimesheetsQueryService } from './timesheets-query.service';
|
||||
// import { CreateWeekShiftsDto } from './create-timesheet.dto';
|
||||
// import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
||||
// import { Roles as RoleEnum } from '.prisma/client';
|
||||
// import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||
// import { TimesheetsCommandService } from './timesheets-command.service';
|
||||
// import { TimesheetPeriodDto } from './timesheet-period.dto';
|
||||
// import { TimesheetMap } from './timesheet.types';
|
||||
|
||||
|
||||
@ApiTags('Timesheets')
|
||||
@ApiBearerAuth('access-token')
|
||||
// @UseGuards()
|
||||
@Controller('timesheets')
|
||||
export class TimesheetsController {
|
||||
constructor(
|
||||
private readonly timesheetsQuery: TimesheetsQueryService,
|
||||
private readonly timesheetsCommand: TimesheetsCommandService,
|
||||
) {}
|
||||
// @ApiTags('Timesheets')
|
||||
// @ApiBearerAuth('access-token')
|
||||
// // @UseGuards()
|
||||
// @Controller('timesheets')
|
||||
// export class TimesheetsController {
|
||||
// constructor(
|
||||
// private readonly timesheetsQuery: TimesheetsQueryService,
|
||||
// private readonly timesheetsCommand: TimesheetsCommandService,
|
||||
// ) {}
|
||||
|
||||
@Get()
|
||||
//@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
async getPeriodByQuery(
|
||||
@Query('year', ParseIntPipe ) year: number,
|
||||
@Query('period_no', ParseIntPipe ) period_no: number,
|
||||
@Query('email') email?: string
|
||||
): Promise<TimesheetPeriodDto> {
|
||||
if(!email || !(email = email.trim())) throw new BadRequestException('Query param "email" is mandatory for this route.');
|
||||
return this.timesheetsQuery.findAll(year, period_no, email);
|
||||
}
|
||||
// @Get()
|
||||
// //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||
// async getPeriodByQuery(
|
||||
// @Query('year', ParseIntPipe ) year: number,
|
||||
// @Query('period_no', ParseIntPipe ) period_no: number,
|
||||
// @Query('email') email?: string
|
||||
// ): Promise<TimesheetPeriodDto> {
|
||||
// if(!email || !(email = email.trim())) throw new BadRequestException('Query param "email" is mandatory for this route.');
|
||||
// return this.timesheetsQuery.findAll(year, period_no, email);
|
||||
// }
|
||||
|
||||
@Get('/:email')
|
||||
async getByEmail(
|
||||
@Param('email') email: string,
|
||||
@Query('offset') offset?: string,
|
||||
): Promise<TimesheetMap> {
|
||||
const week_offset = Number.isFinite(Number(offset)) ? Number(offset) : 0;
|
||||
return this.timesheetsQuery.getTimesheetByEmail(email, week_offset);
|
||||
}
|
||||
// @Get('/:email')
|
||||
// async getByEmail(
|
||||
// @Param('email') email: string,
|
||||
// @Query('offset') offset?: string,
|
||||
// ): Promise<TimesheetMap> {
|
||||
// const week_offset = Number.isFinite(Number(offset)) ? Number(offset) : 0;
|
||||
// return this.timesheetsQuery.getTimesheetByEmail(email, week_offset);
|
||||
// }
|
||||
|
||||
@Post('shifts/:email')
|
||||
async createTimesheetShifts(
|
||||
@Param('email') email: string,
|
||||
@Body() dto: CreateWeekShiftsDto,
|
||||
@Query('offset') offset?: string,
|
||||
): Promise<TimesheetMap> {
|
||||
const week_offset = Number.isFinite(Number(offset)) ? Number(offset) : 0;
|
||||
return this.timesheetsCommand.createWeekShiftsAndReturnOverview(email, dto.shifts, week_offset);
|
||||
}
|
||||
}
|
||||
// @Post('shifts/:email')
|
||||
// async createTimesheetShifts(
|
||||
// @Param('email') email: string,
|
||||
// @Body() dto: CreateWeekShiftsDto,
|
||||
// @Query('offset') offset?: string,
|
||||
// ): Promise<TimesheetMap> {
|
||||
// const week_offset = Number.isFinite(Number(offset)) ? Number(offset) : 0;
|
||||
// return this.timesheetsCommand.createWeekShiftsAndReturnOverview(email, dto.shifts, week_offset);
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user