refactor(expenses): ajusted the controller and service to match new session set-up and did some cleaning
This commit is contained in:
parent
eb166dbc46
commit
8dca65d00e
|
|
@ -1,17 +1,16 @@
|
||||||
import { Controller, Post, Param, Body, Patch, Delete, Req, UnauthorizedException } from "@nestjs/common";
|
import { Controller, Post, Param, Body, Patch, Delete, Req, UnauthorizedException } from "@nestjs/common";
|
||||||
import { CreateExpenseResult, UpdateExpenseResult } from "src/time-and-attendance/utils/type.utils";
|
import { CreateExpenseResult } from "src/time-and-attendance/utils/type.utils";
|
||||||
import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service";
|
import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service";
|
||||||
import { updateExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-update.dto";
|
|
||||||
import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto";
|
import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto";
|
||||||
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
||||||
import { Roles as RoleEnum } from '.prisma/client';
|
import { GLOBAL_CONTROLLER_ROLES } from "src/common/shared/role-groupes";
|
||||||
|
|
||||||
@Controller('expense')
|
@Controller('expense')
|
||||||
|
@RolesAllowed(...GLOBAL_CONTROLLER_ROLES)
|
||||||
export class ExpenseController {
|
export class ExpenseController {
|
||||||
constructor( private readonly upsert_service: ExpenseUpsertService ){}
|
constructor( private readonly upsert_service: ExpenseUpsertService ){}
|
||||||
|
|
||||||
@Post('create')
|
@Post('create')
|
||||||
@RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN)
|
|
||||||
create( @Req() req, @Body() dto: ExpenseDto): Promise<CreateExpenseResult>{
|
create( @Req() req, @Body() dto: ExpenseDto): Promise<CreateExpenseResult>{
|
||||||
const email = req.user?.email;
|
const email = req.user?.email;
|
||||||
if(!email) throw new UnauthorizedException('Unauthorized User');
|
if(!email) throw new UnauthorizedException('Unauthorized User');
|
||||||
|
|
@ -19,13 +18,11 @@ export class ExpenseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch('update')
|
@Patch('update')
|
||||||
@RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN)
|
update(@Body() dto: ExpenseDto): Promise<ExpenseDto>{
|
||||||
update(@Body() body: { update :{ id: number; dto: updateExpenseDto }}): Promise<UpdateExpenseResult>{
|
return this.upsert_service.updateExpense(dto);
|
||||||
return this.upsert_service.updateExpense(body.update);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete('delete/:expense_id')
|
@Delete('delete/:expense_id')
|
||||||
@RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN)
|
|
||||||
remove(@Param('expense_id') expense_id: number) {
|
remove(@Param('expense_id') expense_id: number) {
|
||||||
return this.upsert_service.deleteExpense(expense_id);
|
return this.upsert_service.deleteExpense(expense_id);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { IsBoolean, IsInt, IsOptional, IsString, MaxLength } from "class-validator";
|
import { IsBoolean, IsInt, IsOptional, IsString, MaxLength } from "class-validator";
|
||||||
|
|
||||||
export class ExpenseDto {
|
export class ExpenseDto {
|
||||||
|
@IsInt() @IsOptional() id: number;
|
||||||
@IsInt() bank_code_id!: number;
|
@IsInt() bank_code_id!: number;
|
||||||
@IsInt() timesheet_id!: number;
|
@IsInt() timesheet_id!: number;
|
||||||
@IsInt() @IsOptional() attachment?: number;
|
@IsInt() @IsOptional() attachment?: number;
|
||||||
|
|
|
||||||
13
src/time-and-attendance/expenses/dtos/expense-entity.dto.ts
Normal file
13
src/time-and-attendance/expenses/dtos/expense-entity.dto.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
export class ExpenseEntity {
|
||||||
|
id: number;
|
||||||
|
timesheet_id: number;
|
||||||
|
bank_code_id: number;
|
||||||
|
attachment?:number;
|
||||||
|
date: Date;
|
||||||
|
amount?: number;
|
||||||
|
mileage?:number;
|
||||||
|
comment: string;
|
||||||
|
supervisor_comment?:string;
|
||||||
|
is_approved: boolean;
|
||||||
|
}
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
import { OmitType, PartialType } from "@nestjs/swagger";
|
|
||||||
import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto";
|
|
||||||
|
|
||||||
export class updateExpenseDto extends PartialType (
|
|
||||||
OmitType(ExpenseDto, ['is_approved', 'timesheet_id'] as const)
|
|
||||||
){}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { CreateExpenseResult, UpdateExpensePayload, UpdateExpenseResult, DeleteExpenseResult, NormalizedExpense } from "src/time-and-attendance/utils/type.utils";
|
import { CreateExpenseResult, DeleteExpenseResult, NormalizedExpense } from "src/time-and-attendance/utils/type.utils";
|
||||||
import { toDateFromString, toStringFromDate, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils";
|
import { toDateFromString, toStringFromDate, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils";
|
||||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
import { expense_select } from "src/time-and-attendance/utils/selects.utils";
|
import { expense_select } from "src/time-and-attendance/utils/selects.utils";
|
||||||
|
|
@ -6,6 +6,7 @@ import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto";
|
import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto";
|
||||||
import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto";
|
import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto";
|
||||||
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
|
import { ExpenseEntity } from "src/time-and-attendance/expenses/dtos/expense-entity.dto";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -23,17 +24,11 @@ export class ExpenseUpsertService {
|
||||||
//fetch employee_id using req.user.email
|
//fetch employee_id using req.user.email
|
||||||
const employee_id = await this.emailResolver.findIdByEmail(email);
|
const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||||
|
|
||||||
//normalize strings and dates
|
//normalize strings and dates and Parse numbers
|
||||||
const normed_expense = this.normalizeExpenseDto(dto);
|
const normed_expense = this.normalizeAndParseExpenseDto(dto);
|
||||||
|
|
||||||
//finds the timesheet using expense.date
|
//finds the timesheet using expense.date by finding the sunday
|
||||||
const start_date = weekStartSunday(normed_expense.date);
|
const start_date = weekStartSunday(normed_expense.date);
|
||||||
|
|
||||||
//parse numbers
|
|
||||||
const parsed_amount = this.parseOptionalNumber(dto.amount, "amount");
|
|
||||||
const parsed_mileage = this.parseOptionalNumber(dto.mileage, "mileage");
|
|
||||||
const parsed_attachment = this.parseOptionalNumber(dto.attachment, "attachment");
|
|
||||||
|
|
||||||
const timesheet = await this.prisma.timesheets.findFirst({
|
const timesheet = await this.prisma.timesheets.findFirst({
|
||||||
where: { start_date, employee_id },
|
where: { start_date, employee_id },
|
||||||
select: { id: true, employee_id: true },
|
select: { id: true, employee_id: true },
|
||||||
|
|
@ -43,14 +38,9 @@ export class ExpenseUpsertService {
|
||||||
//create a new expense
|
//create a new expense
|
||||||
const expense = await this.prisma.expenses.create({
|
const expense = await this.prisma.expenses.create({
|
||||||
data: {
|
data: {
|
||||||
|
...normed_expense,
|
||||||
timesheet_id: timesheet.id,
|
timesheet_id: timesheet.id,
|
||||||
bank_code_id: dto.bank_code_id,
|
bank_code_id: dto.bank_code_id,
|
||||||
attachment: parsed_attachment,
|
|
||||||
date: normed_expense.date,
|
|
||||||
amount: parsed_amount,
|
|
||||||
mileage: parsed_mileage,
|
|
||||||
comment: normed_expense.comment,
|
|
||||||
supervisor_comment: normed_expense.supervisor_comment,
|
|
||||||
is_approved: dto.is_approved,
|
is_approved: dto.is_approved,
|
||||||
},
|
},
|
||||||
//return the newly created expense with id
|
//return the newly created expense with id
|
||||||
|
|
@ -59,16 +49,12 @@ export class ExpenseUpsertService {
|
||||||
|
|
||||||
//build an object to return to the frontend to display
|
//build an object to return to the frontend to display
|
||||||
const created: GetExpenseDto = {
|
const created: GetExpenseDto = {
|
||||||
id: expense.id,
|
...expense,
|
||||||
timesheet_id: expense.timesheet_id,
|
|
||||||
bank_code_id: expense.bank_code_id,
|
|
||||||
attachment: expense.attachment ?? undefined,
|
|
||||||
date: toStringFromDate(expense.date),
|
date: toStringFromDate(expense.date),
|
||||||
amount: expense.amount?.toNumber(),
|
amount: expense.amount?.toNumber(),
|
||||||
mileage: expense.mileage?.toNumber(),
|
mileage: expense.mileage?.toNumber(),
|
||||||
comment: expense.comment,
|
attachment: expense.attachment ?? undefined,
|
||||||
supervisor_comment: expense.supervisor_comment ?? undefined,
|
supervisor_comment: expense.supervisor_comment ?? undefined,
|
||||||
is_approved: expense.is_approved,
|
|
||||||
};
|
};
|
||||||
return { ok: true, data: created }
|
return { ok: true, data: created }
|
||||||
|
|
||||||
|
|
@ -80,47 +66,39 @@ export class ExpenseUpsertService {
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
// UPDATE
|
// UPDATE
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
async updateExpense({id, dto}: UpdateExpensePayload): Promise<UpdateExpenseResult> {
|
async updateExpense(dto: ExpenseDto): Promise<ExpenseDto> {
|
||||||
try {
|
try {
|
||||||
//checks for modifications
|
//normalize string , date format and parse numbers
|
||||||
const data: Record<string, unknown> = {};
|
const normed_expense = this.normalizeAndParseExpenseDto(dto);
|
||||||
if (dto.date !== undefined) data.date = toDateFromString(dto.date);
|
|
||||||
if (dto.comment !== undefined) data.comment = this.truncate280(dto.comment);
|
|
||||||
if (dto.attachment !== undefined) data.attachment = this.parseOptionalNumber(dto.attachment, "attachment");
|
|
||||||
if (dto.amount !== undefined) data.amount = this.parseOptionalNumber(dto.amount, "amount");
|
|
||||||
if (dto.mileage !== undefined) data.mileage = this.parseOptionalNumber(dto.mileage, "mileage");
|
|
||||||
if (dto.bank_code_id !== undefined) data.bank_code_id = dto.bank_code_id;
|
|
||||||
if (dto.supervisor_comment !== undefined) {
|
|
||||||
data.supervisor_comment = dto.supervisor_comment?.trim()
|
|
||||||
? this.truncate280(dto.supervisor_comment.trim())
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
//return an error if no fields needs an update
|
|
||||||
if(!Object.keys(data).length) {
|
|
||||||
return { ok: false, id, error: new Error("Nothing to update")};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
//checks for modifications
|
||||||
|
const data: ExpenseEntity = {
|
||||||
|
...normed_expense,
|
||||||
|
id: dto.id,
|
||||||
|
timesheet_id: dto.timesheet_id,
|
||||||
|
bank_code_id: dto.bank_code_id,
|
||||||
|
is_approved: dto.is_approved,
|
||||||
|
};
|
||||||
|
|
||||||
|
//push updates and get updated datas
|
||||||
const expense = await this.prisma.expenses.update({
|
const expense = await this.prisma.expenses.update({
|
||||||
where: { id },
|
where: { id: dto.id, timesheet_id: dto.timesheet_id },
|
||||||
data,
|
data,
|
||||||
select: expense_select,
|
select: expense_select,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//build an object to return to the frontend
|
||||||
const updated: GetExpenseDto = {
|
const updated: GetExpenseDto = {
|
||||||
id: expense.id,
|
...expense,
|
||||||
timesheet_id: expense.timesheet_id,
|
|
||||||
bank_code_id: expense.bank_code_id,
|
|
||||||
attachment: expense.attachment ?? undefined,
|
|
||||||
date: toStringFromDate(expense.date),
|
date: toStringFromDate(expense.date),
|
||||||
amount: expense.amount?.toNumber(),
|
amount: expense.amount?.toNumber(),
|
||||||
mileage: expense.mileage?.toNumber(),
|
mileage: expense.mileage?.toNumber(),
|
||||||
comment: expense.comment,
|
attachment: expense.attachment ?? undefined,
|
||||||
supervisor_comment: expense.supervisor_comment ?? undefined,
|
supervisor_comment: expense.supervisor_comment ?? undefined,
|
||||||
is_approved: expense.is_approved,
|
|
||||||
};
|
};
|
||||||
return { ok: true, id: expense.id, data: updated };
|
return updated;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { ok: false, id: id, error: error}
|
return error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
|
|
@ -148,14 +126,22 @@ export class ExpenseUpsertService {
|
||||||
// LOCAL HELPERS
|
// LOCAL HELPERS
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
//makes sure that comments are the right length the date is of Date type
|
//makes sure that comments are the right length the date is of Date type
|
||||||
private normalizeExpenseDto(dto: ExpenseDto): NormalizedExpense {
|
private normalizeAndParseExpenseDto(dto: ExpenseDto): NormalizedExpense {
|
||||||
const date = toDateFromString(dto.date);
|
const parsed_attachment = this.parseOptionalNumber(dto.attachment, "attachment");
|
||||||
|
const parsed_mileage = this.parseOptionalNumber(dto.mileage, "mileage");
|
||||||
|
const parsed_amount = this.parseOptionalNumber(dto.amount, "amount");
|
||||||
const comment = this.truncate280(dto.comment);
|
const comment = this.truncate280(dto.comment);
|
||||||
const supervisor_comment =
|
const supervisor_comment = dto.supervisor_comment && dto.supervisor_comment.trim()
|
||||||
dto.supervisor_comment && dto.supervisor_comment.trim()
|
? this.truncate280(dto.supervisor_comment.trim()) : undefined;
|
||||||
? this.truncate280(dto.supervisor_comment.trim())
|
const date = toDateFromString(dto.date);
|
||||||
: undefined;
|
return {
|
||||||
return { date, comment, supervisor_comment };
|
date,
|
||||||
|
comment,
|
||||||
|
supervisor_comment,
|
||||||
|
parsed_amount,
|
||||||
|
parsed_attachment,
|
||||||
|
parsed_mileage
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
//makes sure that a string cannot exceed 280 chars
|
//makes sure that a string cannot exceed 280 chars
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { BankCodesResolver } from "src/time-and-attendance/utils/resolve-bank-ty
|
||||||
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto";
|
import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto";
|
||||||
import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto";
|
import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-entity.dto";
|
||||||
import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto";
|
import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-create.dto";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import { Prisma, PrismaClient } from "@prisma/client";
|
import { Prisma, PrismaClient } from "@prisma/client";
|
||||||
import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto";
|
import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto";
|
||||||
import { updateExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-update.dto";
|
|
||||||
import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto";
|
import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto";
|
||||||
import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto";
|
import { GetShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-get.dto";
|
||||||
import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-payload.dto";
|
import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-entity.dto";
|
||||||
import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils";
|
import { leaveRequestsSelect } from "src/time-and-attendance/utils/selects.utils";
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -24,26 +23,38 @@ export type TotalExpenses = {
|
||||||
mileage: number;
|
mileage: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Normalized = { date: Date; start_time: Date; end_time: Date; bank_code_id: number};
|
export type Normalized = {
|
||||||
|
date: Date;
|
||||||
|
start_time: Date;
|
||||||
|
end_time: Date;
|
||||||
|
bank_code_id: number;
|
||||||
|
};
|
||||||
export type CreateShiftResult = { ok: true; data: GetShiftDto } | { ok: false; error: any };
|
export type CreateShiftResult = { ok: true; data: GetShiftDto } | { ok: false; error: any };
|
||||||
export type UpdateShiftResult = { ok: true; id: number; data: GetShiftDto } | { ok: false; id: number; error: any };
|
export type UpdateShiftResult = { ok: true; id: number; data: GetShiftDto } | { ok: false; id: number; error: any };
|
||||||
|
export type NormedOk = {
|
||||||
|
index: number;
|
||||||
|
dto: ShiftEntity;
|
||||||
|
normed: Normalized;
|
||||||
|
timesheet_id: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type DeletePresetResult = { ok: true; id: number; } | { ok: false; id: number; error: any };
|
export type DeletePresetResult = { ok: true; id: number; } | { ok: false; id: number; error: any };
|
||||||
export type CreatePresetResult = { ok: true; } | { ok: false; error: any };
|
export type CreatePresetResult = { ok: true; } | { ok: false; error: any };
|
||||||
export type UpdatePresetResult = { ok: true; id: number; data: SchedulePresetsDto } | { ok: false; id: number; error: any };
|
export type UpdatePresetResult = { ok: true; id: number; data: SchedulePresetsDto } | { ok: false; id: number; error: any };
|
||||||
|
|
||||||
|
|
||||||
export type NormalizedExpense = { date: Date; comment: string; supervisor_comment?: string; };
|
export type NormalizedExpense = {
|
||||||
|
date: Date;
|
||||||
|
comment: string;
|
||||||
|
supervisor_comment?: string;
|
||||||
|
parsed_amount?: number;
|
||||||
|
parsed_mileage?: number;
|
||||||
|
parsed_attachment?: number;
|
||||||
|
};
|
||||||
export type CreateExpenseResult = { ok: true; data: GetExpenseDto } | { ok: false; error: any };
|
export type CreateExpenseResult = { ok: true; data: GetExpenseDto } | { ok: false; error: any };
|
||||||
export type UpdateExpensePayload = { id: number; dto: updateExpenseDto };
|
|
||||||
export type UpdateExpenseResult = { ok: true; id: number; data: GetExpenseDto } | { ok: false; id: number; error: any };
|
|
||||||
export type DeleteExpenseResult = { ok: true; id: number; } | { ok: false; id: number; error: any };
|
export type DeleteExpenseResult = { ok: true; id: number; } | { ok: false; id: number; error: any };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export type NormedOk = { index: number; dto: ShiftEntity; normed: Normalized, timesheet_id: number };
|
|
||||||
|
|
||||||
|
|
||||||
export type ShiftResponse = {
|
export type ShiftResponse = {
|
||||||
week_day: string;
|
week_day: string;
|
||||||
sort_order: number;
|
sort_order: number;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user