refactor: employee

This commit is contained in:
Methapon Metanipat 2024-09-13 17:39:12 +07:00
parent 77739da154
commit c51a403f2a
7 changed files with 637 additions and 277 deletions

View file

@ -0,0 +1,99 @@
/*
Warnings:
- You are about to drop the column `entryDate` on the `Employee` table. All the data in the column will be lost.
- You are about to drop the column `passportExpiryDate` on the `Employee` table. All the data in the column will be lost.
- You are about to drop the column `passportIssueDate` on the `Employee` table. All the data in the column will be lost.
- You are about to drop the column `passportIssuingCountry` on the `Employee` table. All the data in the column will be lost.
- You are about to drop the column `passportIssuingPlace` on the `Employee` table. All the data in the column will be lost.
- You are about to drop the column `passportNumber` on the `Employee` table. All the data in the column will be lost.
- You are about to drop the column `passportType` on the `Employee` table. All the data in the column will be lost.
- You are about to drop the column `previousPassportReference` on the `Employee` table. All the data in the column will be lost.
- You are about to drop the column `tm6Number` on the `Employee` table. All the data in the column will be lost.
- You are about to drop the column `visaExpiryDate` on the `Employee` table. All the data in the column will be lost.
- You are about to drop the column `visaIssueDate` on the `Employee` table. All the data in the column will be lost.
- You are about to drop the column `visaIssuingPlace` on the `Employee` table. All the data in the column will be lost.
- You are about to drop the column `visaNumber` on the `Employee` table. All the data in the column will be lost.
- You are about to drop the column `visaStayUntilDate` on the `Employee` table. All the data in the column will be lost.
- You are about to drop the column `visaType` on the `Employee` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "Employee" DROP COLUMN "entryDate",
DROP COLUMN "passportExpiryDate",
DROP COLUMN "passportIssueDate",
DROP COLUMN "passportIssuingCountry",
DROP COLUMN "passportIssuingPlace",
DROP COLUMN "passportNumber",
DROP COLUMN "passportType",
DROP COLUMN "previousPassportReference",
DROP COLUMN "tm6Number",
DROP COLUMN "visaExpiryDate",
DROP COLUMN "visaIssueDate",
DROP COLUMN "visaIssuingPlace",
DROP COLUMN "visaNumber",
DROP COLUMN "visaStayUntilDate",
DROP COLUMN "visaType",
ADD COLUMN "workerType" TEXT;
-- CreateTable
CREATE TABLE "EmployeePassport" (
"id" TEXT NOT NULL,
"number" TEXT NOT NULL,
"type" TEXT NOT NULL,
"issueDate" DATE NOT NULL,
"expireDate" DATE NOT NULL,
"issueCountry" TEXT NOT NULL,
"issuePlace" TEXT NOT NULL,
"previousPassportRef" TEXT,
"employeeId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "EmployeePassport_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "EmployeeVisa" (
"id" TEXT NOT NULL,
"number" TEXT NOT NULL,
"type" TEXT NOT NULL,
"entryCount" INTEGER NOT NULL,
"issueCountry" TEXT NOT NULL,
"issuePlace" TEXT NOT NULL,
"issueDate" DATE NOT NULL,
"expireDate" DATE NOT NULL,
"mrz" TEXT,
"remark" TEXT,
"employeeId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "EmployeeVisa_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "EmployeeInCountryNotice" (
"id" TEXT NOT NULL,
"noticeNumber" TEXT NOT NULL,
"noticeDate" TEXT NOT NULL,
"nextNoticeDate" DATE NOT NULL,
"tmNumber" TEXT NOT NULL,
"entryDate" DATE NOT NULL,
"travelBy" TEXT NOT NULL,
"travelFrom" TEXT NOT NULL,
"employeeId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "EmployeeInCountryNotice_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "EmployeePassport" ADD CONSTRAINT "EmployeePassport_employeeId_fkey" FOREIGN KEY ("employeeId") REFERENCES "Employee"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "EmployeeVisa" ADD CONSTRAINT "EmployeeVisa_employeeId_fkey" FOREIGN KEY ("employeeId") REFERENCES "Employee"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "EmployeeInCountryNotice" ADD CONSTRAINT "EmployeeInCountryNotice_employeeId_fkey" FOREIGN KEY ("employeeId") REFERENCES "Employee"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View file

@ -552,22 +552,7 @@ model Employee {
subDistrict SubDistrict? @relation(fields: [subDistrictId], references: [id], onDelete: SetNull)
subDistrictId String?
passportType String
passportNumber String
passportIssueDate DateTime @db.Date
passportExpiryDate DateTime @db.Date
passportIssuingCountry String
passportIssuingPlace String
previousPassportReference String?
visaType String?
visaNumber String?
visaIssueDate DateTime? @db.Date
visaExpiryDate DateTime? @db.Date
visaIssuingPlace String?
visaStayUntilDate DateTime? @db.Date
tm6Number String?
entryDate DateTime? @db.Date
workerType String?
workerStatus String?
customerBranch CustomerBranch @relation(fields: [customerBranchId], references: [id], onDelete: Cascade)
@ -584,6 +569,9 @@ model Employee {
updatedBy User? @relation(name: "EmployeeUpdatedByUser", fields: [updatedByUserId], references: [id], onDelete: SetNull)
updatedByUserId String?
employeePassport EmployeePassport[]
employeeVisa EmployeeVisa[]
employeeInCountryNotice EmployeeInCountryNotice[]
employeeCheckup EmployeeCheckup[]
employeeWork EmployeeWork[]
employeeOtherInfo EmployeeOtherInfo?
@ -606,6 +594,63 @@ model EmployeeHistory {
master Employee @relation(fields: [masterId], references: [id], onDelete: Cascade)
}
model EmployeePassport {
id String @id @default(cuid())
number String
type String
issueDate DateTime @db.Date
expireDate DateTime @db.Date
issueCountry String
issuePlace String
previousPassportRef String?
employee Employee @relation(fields: [employeeId], references: [id])
employeeId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model EmployeeVisa {
id String @id @default(cuid())
number String
type String
entryCount Int
issueCountry String
issuePlace String
issueDate DateTime @db.Date
expireDate DateTime @db.Date
mrz String?
remark String?
employee Employee @relation(fields: [employeeId], references: [id])
employeeId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model EmployeeInCountryNotice {
id String @id @default(cuid())
noticeNumber String
noticeDate String
nextNoticeDate DateTime @db.Date
tmNumber String
entryDate DateTime @db.Date
travelBy String
travelFrom String
employee Employee @relation(fields: [employeeId], references: [id])
employeeId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model EmployeeCheckup {
id String @id @default(cuid())

View file

@ -26,7 +26,7 @@ import {
} from "../services/permission";
import { connectOrDisconnect, connectOrNot, whereAddressQuery } from "../utils/relation";
import { notFoundError, relationError } from "../utils/error";
import { deleteFile, fileLocation, getFile, listFile, setFile } from "../utils/minio";
import { deleteFile, deleteFolder, fileLocation, getFile, listFile, setFile } from "../utils/minio";
if (!process.env.MINIO_BUCKET) {
throw Error("Require MinIO bucket.");
@ -77,69 +77,13 @@ type EmployeeCreate = {
street?: string | null;
streetEN?: string | null;
passportType: string;
passportNumber: string;
passportIssueDate: Date;
passportExpiryDate: Date;
passportIssuingCountry: string;
passportIssuingPlace: string;
previousPassportReference?: string;
visaType?: string | null;
visaNumber?: string | null;
visaIssueDate?: Date | null;
visaExpiryDate?: Date | null;
visaIssuingPlace?: string | null;
visaStayUntilDate?: Date | null;
tm6Number?: string | null;
entryDate?: Date | null;
workerStatus?: string | null;
subDistrictId?: string | null;
districtId?: string | null;
provinceId?: string | null;
workerType?: string | null;
workerStatus?: string | null;
selectedImage?: string | null;
employeeWork?: {
ownerName?: string | null;
positionName?: string | null;
jobType?: string | null;
workplace?: string | null;
workPermitNo?: string | null;
workPermitIssuDate?: Date | null;
workPermitExpireDate?: Date | null;
workEndDate?: Date | null;
remark?: string | null;
}[];
employeeCheckup?: {
checkupType?: string | null;
checkupResult?: string | null;
provinceId?: string | null;
hospitalName?: string | null;
remark?: string | null;
medicalBenefitScheme?: string | null;
insuranceCompany?: string | null;
coverageStartDate?: Date | null;
coverageExpireDate?: Date | null;
}[];
employeeOtherInfo?: {
citizenId?: string | null;
fatherFirstName?: string | null;
fatherLastName?: string | null;
fatherBirthPlace?: string | null;
motherFirstName?: string | null;
motherLastName?: string | null;
motherBirthPlace?: string | null;
fatherFirstNameEN?: string | null;
fatherLastNameEN?: string | null;
motherFirstNameEN?: string | null;
motherLastNameEN?: string | null;
};
};
type EmployeeUpdate = {
@ -169,71 +113,13 @@ type EmployeeUpdate = {
street?: string | null;
streetEN?: string | null;
passportType?: string;
passportNumber?: string;
passportIssueDate?: Date;
passportExpiryDate?: Date;
passportIssuingCountry?: string;
passportIssuingPlace?: string;
previousPassportReference?: string;
visaType?: string | null;
visaNumber?: string | null;
visaIssueDate?: Date | null;
visaExpiryDate?: Date | null;
visaIssuingPlace?: string | null;
visaStayUntilDate?: Date | null;
tm6Number?: string | null;
entryDate?: Date | null;
workerType?: string | null;
workerStatus?: string | null;
selectedImage?: string | null;
subDistrictId?: string | null;
districtId?: string | null;
provinceId?: string | null;
employeeWork?: {
id?: string;
ownerName?: string | null;
positionName?: string | null;
jobType?: string | null;
workplace?: string | null;
workPermitNo?: string | null;
workPermitIssuDate?: Date | null;
workPermitExpireDate?: Date | null;
workEndDate?: Date | null;
remark?: string | null;
}[];
employeeCheckup?: {
id?: string;
checkupType?: string | null;
checkupResult?: string | null;
provinceId?: string | null;
hospitalName?: string | null;
remark?: string | null;
medicalBenefitScheme?: string | null;
insuranceCompany?: string | null;
coverageStartDate?: Date | null;
coverageExpireDate?: Date | null;
}[];
employeeOtherInfo?: {
citizenId?: string | null;
fatherFirstName?: string | null;
fatherLastName?: string | null;
fatherBirthPlace?: string | null;
motherFirstName?: string | null;
motherLastName?: string | null;
motherBirthPlace?: string | null;
fatherFirstNameEN?: string | null;
fatherLastNameEN?: string | null;
motherFirstNameEN?: string | null;
motherLastNameEN?: string | null;
};
};
@Route("api/v1/employee")
@ -306,7 +192,6 @@ export class EmployeeController extends Controller {
{ firstNameEN: { contains: query } },
{ lastName: { contains: query } },
{ lastNameEN: { contains: query } },
{ passportNumber: { contains: query } },
...whereAddressQuery(query),
],
AND: {
@ -403,35 +288,7 @@ export class EmployeeController extends Controller {
await permissionCheck(req.user, customerBranch.customer.registeredBranch);
const {
provinceId,
districtId,
subDistrictId,
customerBranchId,
employeeWork,
employeeCheckup,
employeeOtherInfo,
...rest
} = body;
const listProvinceId = employeeCheckup?.reduce<string[]>((acc, cur) => {
if (cur.provinceId && !acc.includes(cur.provinceId)) return acc.concat(cur.provinceId);
if (!cur.provinceId) cur.provinceId = null;
return acc;
}, []);
if (listProvinceId) {
const [listProvince] = await prisma.$transaction([
prisma.province.findMany({ where: { id: { in: listProvinceId } } }),
]);
if (listProvince.length !== listProvinceId.length) {
throw new HttpError(
HttpStatus.BAD_REQUEST,
"Some province cannot be found.",
"someProvinceNotFound",
);
}
}
const { provinceId, districtId, subDistrictId, customerBranchId, ...rest } = body;
const record = await prisma.$transaction(
async (tx) => {
@ -465,23 +322,6 @@ export class EmployeeController extends Controller {
...rest,
statusOrder: +(rest.status === "INACTIVE"),
code: `${customerBranch.code}-${`${new Date().getFullYear()}`.slice(-2).padStart(2, "0")}${`${last.value}`.padStart(7, "0")}`,
employeeWork: {
createMany: {
data: employeeWork || [],
},
},
employeeCheckup: {
createMany: {
data:
employeeCheckup?.map((v) => ({
...v,
provinceId: !!v.provinceId ? null : v.provinceId,
})) || [],
},
},
employeeOtherInfo: {
create: employeeOtherInfo,
},
province: connectOrNot(provinceId),
district: connectOrNot(districtId),
subDistrict: connectOrNot(subDistrictId),
@ -568,35 +408,7 @@ export class EmployeeController extends Controller {
await permissionCheck(req.user, customerBranch.customer.registeredBranch);
}
const {
provinceId,
districtId,
subDistrictId,
customerBranchId,
employeeWork,
employeeCheckup,
employeeOtherInfo,
...rest
} = body;
const listProvinceId = employeeCheckup?.reduce<string[]>((acc, cur) => {
if (cur.provinceId && !acc.includes(cur.provinceId)) return acc.concat(cur.provinceId);
if (!cur.provinceId) cur.provinceId = null;
return acc;
}, []);
if (listProvinceId) {
const [listProvince] = await prisma.$transaction([
prisma.province.findMany({ where: { id: { in: listProvinceId } } }),
]);
if (listProvince.length !== listProvinceId.length) {
throw new HttpError(
HttpStatus.BAD_REQUEST,
"Some province cannot be found.",
"someProvinceNotFound",
);
}
}
const { provinceId, districtId, subDistrictId, customerBranchId, ...rest } = body;
const record = await prisma.$transaction(async (tx) => {
let code: string | undefined;
@ -640,53 +452,6 @@ export class EmployeeController extends Controller {
statusOrder: +(rest.status === "INACTIVE"),
code,
customerBranch: connectOrNot(customerBranchId),
employeeWork: employeeWork
? {
deleteMany: {
id: {
notIn: employeeWork.map((v) => v.id).filter((v): v is string => !!v) || [],
},
},
upsert: employeeWork.map((v) => ({
where: { id: v.id || "" },
create: {
...v,
createdByUserId: req.user.sub,
updatedByUserId: req.user.sub,
id: undefined,
},
update: {
...v,
updatedByUserId: req.user.sub,
},
})),
}
: undefined,
employeeCheckup: employeeCheckup
? {
deleteMany: {
id: {
notIn: employeeCheckup.map((v) => v.id).filter((v): v is string => !!v) || [],
},
},
upsert: employeeCheckup.map((v) => ({
where: { id: v.id || "" },
create: {
...v,
provinceId: !v.provinceId ? undefined : v.provinceId,
createdByUserId: req.user.sub,
updatedByUserId: req.user.sub,
id: undefined,
},
update: {
...v,
updatedByUserId: req.user.sub,
},
})),
}
: undefined,
employeeOtherInfo: employeeOtherInfo ? { update: employeeOtherInfo } : undefined,
province: connectOrDisconnect(provinceId),
district: connectOrDisconnect(districtId),
subDistrict: connectOrDisconnect(subDistrictId),
@ -698,13 +463,7 @@ export class EmployeeController extends Controller {
const historyEntries: { field: string; valueBefore: string; valueAfter: string }[] = [];
for (const k of Object.keys(body)) {
const field = k as keyof typeof body;
if (field === "employeeCheckup") continue;
if (field === "employeeOtherInfo") continue;
if (field === "employeeWork") continue;
for (const field of Object.keys(body) as (keyof typeof body)[]) {
let valueBefore = employee[field];
let valueAfter = body[field];
@ -757,6 +516,14 @@ export class EmployeeController extends Controller {
throw new HttpError(HttpStatus.FORBIDDEN, "Employee is in used.", "employeeInUsed");
}
await Promise.all([
deleteFolder(fileLocation.employee.img(employeeId)),
deleteFolder(fileLocation.employee.attachment(employeeId)),
deleteFolder(fileLocation.employee.passport(employeeId)),
deleteFolder(fileLocation.employee.visa(employeeId)),
deleteFolder(fileLocation.employee.inCountryNotice(employeeId)),
]);
return await prisma.employee.delete({
include: {
createdBy: true,
@ -876,4 +643,111 @@ export class EmployeeFileController extends Controller {
await this.checkPermission(req.user, employeeId);
return await deleteFile(fileLocation.employee.attachment(employeeId, name));
}
@Get("file-passport")
@Security("keycloak")
async listPassport(@Request() req: RequestWithUser, @Path() employeeId: string) {
await this.checkPermission(req.user, employeeId);
return await listFile(fileLocation.employee.passport(employeeId));
}
@Get("file-passport/{passportId}")
@Security("keycloak")
async getPassport(@Path() employeeId: string, @Path() passportId: string) {
return await getFile(fileLocation.employee.passport(employeeId, passportId));
}
@Put("file-passport/{passportId}")
@Security("keycloak")
async putPassport(
@Request() req: RequestWithUser,
@Path() employeeId: string,
@Path() passportId: string,
) {
await this.checkPermission(req.user, employeeId);
return req.res?.redirect(await setFile(fileLocation.employee.passport(employeeId, passportId)));
}
@Delete("file-passport/{passportId}")
@Security("keycloak")
async delPassport(
@Request() req: RequestWithUser,
@Path() employeeId: string,
@Path() passportId: string,
) {
await this.checkPermission(req.user, employeeId);
return await deleteFile(fileLocation.employee.passport(employeeId, passportId));
}
@Get("file-visa")
@Security("keycloak")
async listVisa(@Request() req: RequestWithUser, @Path() employeeId: string) {
await this.checkPermission(req.user, employeeId);
return await listFile(fileLocation.employee.visa(employeeId));
}
@Get("file-visa/{visaId}")
@Security("keycloak")
async getVisa(@Path() employeeId: string, @Path() visaId: string) {
return await getFile(fileLocation.employee.visa(employeeId, visaId));
}
@Put("file-visa/{visaId}")
@Security("keycloak")
async putVisa(
@Request() req: RequestWithUser,
@Path() employeeId: string,
@Path() visaId: string,
) {
await this.checkPermission(req.user, employeeId);
return req.res?.redirect(await setFile(fileLocation.employee.visa(employeeId, visaId)));
}
@Delete("file-visa/{visaId}")
@Security("keycloak")
async delVisa(
@Request() req: RequestWithUser,
@Path() employeeId: string,
@Path() visaId: string,
) {
await this.checkPermission(req.user, employeeId);
return await deleteFile(fileLocation.employee.visa(employeeId, visaId));
}
@Get("file-in-country-notice")
@Security("keycloak")
async listNotice(@Request() req: RequestWithUser, @Path() employeeId: string) {
await this.checkPermission(req.user, employeeId);
return await listFile(fileLocation.employee.inCountryNotice(employeeId));
}
@Get("file-in-country-notice/{noticeId}")
@Security("keycloak")
async getNotice(@Path() employeeId: string, @Path() noticeId: string) {
return await getFile(fileLocation.employee.inCountryNotice(employeeId, noticeId));
}
@Put("file-in-country-notice/{noticeId}")
@Security("keycloak")
async putNotice(
@Request() req: RequestWithUser,
@Path() employeeId: string,
@Path() noticeId: string,
) {
await this.checkPermission(req.user, employeeId);
return req.res?.redirect(
await setFile(fileLocation.employee.inCountryNotice(employeeId, noticeId)),
);
}
@Delete("file-in-country-notice/{noticeId}")
@Security("keycloak")
async delNotice(
@Request() req: RequestWithUser,
@Path() employeeId: string,
@Path() noticeId: string,
) {
await this.checkPermission(req.user, employeeId);
return await deleteFile(fileLocation.employee.inCountryNotice(employeeId, noticeId));
}
}

View file

@ -0,0 +1,116 @@
import {
Body,
Controller,
Delete,
Get,
Middlewares,
Path,
Post,
Put,
Route,
Security,
Tags,
} from "tsoa";
import { RequestWithUser } from "../interfaces/user";
import prisma from "../db";
import HttpStatus from "../interfaces/http-status";
import { permissionCheck } from "../middlewares/employee";
import { notFoundError } from "../utils/error";
const MANAGE_ROLES = [
"system",
"head_of_admin",
"admin",
"head_of_account",
"account",
"head_of_sale",
];
function globalAllow(user: RequestWithUser["user"]) {
const allowList = ["system", "head_of_admin", "admin", "head_of_account", "head_of_sale"];
return allowList.some((v) => user.roles?.includes(v));
}
type EmployeeInCountryNoticePayload = {
noticeNumber: string;
noticeDate: string;
nextNoticeDate: Date;
tmNumber: string;
entryDate: Date;
travelBy: string;
travelFrom: string;
};
@Route("api/v1/employee/{employeeId}/work")
@Tags("Employee Work")
@Middlewares(permissionCheck(globalAllow))
export class EmployeeInCountryNoticeController extends Controller {
@Get()
@Security("keycloak")
async list(@Path() employeeId: string) {
return prisma.employeeInCountryNotice.findMany({
orderBy: { createdAt: "asc" },
where: { employeeId },
});
}
@Get("{passportId}")
@Security("keycloak")
async getById(@Path() employeeId: string, @Path() passportId: string) {
const record = await prisma.employeeInCountryNotice.findFirst({
where: { id: passportId, employeeId },
});
if (!record) throw notFoundError("Employee Work");
return record;
}
@Post()
@Security("keycloak", MANAGE_ROLES)
async create(@Path() employeeId: string, @Body() body: EmployeeInCountryNoticePayload) {
const record = await prisma.employeeInCountryNotice.create({
data: {
...body,
employee: { connect: { id: employeeId } },
},
});
this.setStatus(HttpStatus.CREATED);
return record;
}
@Put("{passportId}")
@Security("keycloak", MANAGE_ROLES)
async editById(
@Path() employeeId: string,
@Path() passportId: string,
@Body() body: EmployeeInCountryNoticePayload,
) {
const work = await prisma.employeeInCountryNotice.findUnique({
where: { id: passportId, employeeId },
});
if (!work) throw notFoundError("Employee Work");
const record = await prisma.employeeInCountryNotice.update({
where: { id: passportId, employeeId },
data: { ...body },
});
this.setStatus(HttpStatus.CREATED);
return record;
}
@Delete("{passportId}")
@Security("keycloak", MANAGE_ROLES)
async deleteById(@Path() employeeId: string, @Path() passportId: string) {
const record = await prisma.employeeInCountryNotice.findFirst({
where: { id: passportId, employeeId },
});
if (!record) throw notFoundError("Employee Work");
return await prisma.employeeInCountryNotice.delete({ where: { id: passportId, employeeId } });
}
}

View file

@ -0,0 +1,116 @@
import {
Body,
Controller,
Delete,
Get,
Middlewares,
Path,
Post,
Put,
Route,
Security,
Tags,
} from "tsoa";
import { RequestWithUser } from "../interfaces/user";
import prisma from "../db";
import HttpStatus from "../interfaces/http-status";
import { permissionCheck } from "../middlewares/employee";
import { notFoundError } from "../utils/error";
const MANAGE_ROLES = [
"system",
"head_of_admin",
"admin",
"head_of_account",
"account",
"head_of_sale",
];
function globalAllow(user: RequestWithUser["user"]) {
const allowList = ["system", "head_of_admin", "admin", "head_of_account", "head_of_sale"];
return allowList.some((v) => user.roles?.includes(v));
}
type EmployeePassportPayload = {
number: string;
type: string;
issueDate: Date;
expireDate: Date;
issueCountry: string;
issuePlace: string;
previousPassportRef?: string | null;
};
@Route("api/v1/employee/{employeeId}/work")
@Tags("Employee Work")
@Middlewares(permissionCheck(globalAllow))
export class EmployeePassportController extends Controller {
@Get()
@Security("keycloak")
async list(@Path() employeeId: string) {
return prisma.employeePassport.findMany({
orderBy: { createdAt: "asc" },
where: { employeeId },
});
}
@Get("{passportId}")
@Security("keycloak")
async getById(@Path() employeeId: string, @Path() passportId: string) {
const record = await prisma.employeePassport.findFirst({
where: { id: passportId, employeeId },
});
if (!record) throw notFoundError("Employee Work");
return record;
}
@Post()
@Security("keycloak", MANAGE_ROLES)
async create(@Path() employeeId: string, @Body() body: EmployeePassportPayload) {
const record = await prisma.employeePassport.create({
data: {
...body,
employee: { connect: { id: employeeId } },
},
});
this.setStatus(HttpStatus.CREATED);
return record;
}
@Put("{passportId}")
@Security("keycloak", MANAGE_ROLES)
async editById(
@Path() employeeId: string,
@Path() passportId: string,
@Body() body: EmployeePassportPayload,
) {
const work = await prisma.employeePassport.findUnique({
where: { id: passportId, employeeId },
});
if (!work) throw notFoundError("Employee Work");
const record = await prisma.employeePassport.update({
where: { id: passportId, employeeId },
data: { ...body },
});
this.setStatus(HttpStatus.CREATED);
return record;
}
@Delete("{passportId}")
@Security("keycloak", MANAGE_ROLES)
async deleteById(@Path() employeeId: string, @Path() passportId: string) {
const record = await prisma.employeePassport.findFirst({
where: { id: passportId, employeeId },
});
if (!record) throw notFoundError("Employee Work");
return await prisma.employeePassport.delete({ where: { id: passportId, employeeId } });
}
}

View file

@ -0,0 +1,118 @@
import {
Body,
Controller,
Delete,
Get,
Middlewares,
Path,
Post,
Put,
Route,
Security,
Tags,
} from "tsoa";
import { RequestWithUser } from "../interfaces/user";
import prisma from "../db";
import HttpStatus from "../interfaces/http-status";
import { permissionCheck } from "../middlewares/employee";
import { notFoundError } from "../utils/error";
const MANAGE_ROLES = [
"system",
"head_of_admin",
"admin",
"head_of_account",
"account",
"head_of_sale",
];
function globalAllow(user: RequestWithUser["user"]) {
const allowList = ["system", "head_of_admin", "admin", "head_of_account", "head_of_sale"];
return allowList.some((v) => user.roles?.includes(v));
}
type EmployeeVisaPayload = {
number: string;
type: string;
entryCount: number;
issueCountry: string;
issuePlace: string;
issueDate: Date;
expireDate: Date;
mrz?: string;
remark?: string;
};
@Route("api/v1/employee/{employeeId}/work")
@Tags("Employee Work")
@Middlewares(permissionCheck(globalAllow))
export class EmployeeVisaController extends Controller {
@Get()
@Security("keycloak")
async list(@Path() employeeId: string) {
return prisma.employeeVisa.findMany({
orderBy: { createdAt: "asc" },
where: { employeeId },
});
}
@Get("{passportId}")
@Security("keycloak")
async getById(@Path() employeeId: string, @Path() passportId: string) {
const record = await prisma.employeeVisa.findFirst({
where: { id: passportId, employeeId },
});
if (!record) throw notFoundError("Employee Work");
return record;
}
@Post()
@Security("keycloak", MANAGE_ROLES)
async create(@Path() employeeId: string, @Body() body: EmployeeVisaPayload) {
const record = await prisma.employeeVisa.create({
data: {
...body,
employee: { connect: { id: employeeId } },
},
});
this.setStatus(HttpStatus.CREATED);
return record;
}
@Put("{passportId}")
@Security("keycloak", MANAGE_ROLES)
async editById(
@Path() employeeId: string,
@Path() passportId: string,
@Body() body: EmployeeVisaPayload,
) {
const work = await prisma.employeeVisa.findUnique({
where: { id: passportId, employeeId },
});
if (!work) throw notFoundError("Employee Work");
const record = await prisma.employeeVisa.update({
where: { id: passportId, employeeId },
data: { ...body },
});
this.setStatus(HttpStatus.CREATED);
return record;
}
@Delete("{passportId}")
@Security("keycloak", MANAGE_ROLES)
async deleteById(@Path() employeeId: string, @Path() passportId: string) {
const record = await prisma.employeeVisa.findFirst({
where: { id: passportId, employeeId },
});
if (!record) throw notFoundError("Employee Work");
return await prisma.employeeVisa.delete({ where: { id: passportId, employeeId } });
}
}

View file

@ -113,14 +113,6 @@ type QuotationUpdate = {
addressEN: string;
address: string;
zipCode: string;
passportType: string;
passportNumber: string;
passportIssueDate: Date;
passportExpiryDate: Date;
passportIssuingCountry: string;
passportIssuingPlace: string;
previousPassportReference?: string;
}
)[];