jws-backend/src/controllers/07-task-controller.ts

1170 lines
34 KiB
TypeScript
Raw Normal View History

import {
Body,
Controller,
Delete,
Get,
2024-12-06 17:36:45 +07:00
Head,
Path,
Post,
Put,
Query,
Request,
Route,
Security,
Tags,
} from "tsoa";
import prisma from "../db";
import { notFoundError } from "../utils/error";
import {
Prisma,
QuotationStatus,
RequestDataStatus,
RequestWorkStatus,
2025-02-10 10:03:32 +07:00
Status,
TaskOrderStatus,
TaskStatus,
UserTaskStatus,
} from "@prisma/client";
import { RequestWithUser } from "../interfaces/user";
2024-12-03 17:11:44 +07:00
import {
branchRelationPermInclude,
createPermCheck,
createPermCondition,
} from "../services/permission";
import HttpError from "../interfaces/http-error";
import HttpStatus from "../interfaces/http-status";
2025-01-13 15:44:29 +07:00
import {
deleteFile,
deleteFolder,
fileLocation,
getFile,
getPresigned,
listFile,
setFile,
} from "../utils/minio";
2025-04-17 13:41:22 +07:00
import { queryOrNot, whereDateQuery } from "../utils/relation";
2024-12-03 17:11:44 +07:00
const MANAGE_ROLES = ["system", "head_of_admin", "admin", "document_checker"];
function globalAllow(user: RequestWithUser["user"]) {
const allowList = ["system", "head_of_admin"];
return allowList.some((v) => user.roles?.includes(v));
}
const permissionCondCompany = createPermCondition((_) => true);
const permissionCheck = createPermCheck(globalAllow);
const permissionCheckCompany = createPermCheck((_) => true);
@Route("/api/v1/task-order")
@Tags("Task Order")
export class TaskController extends Controller {
2024-12-03 09:37:35 +07:00
@Get("stats")
async getTaskOrderStats() {
const task = await prisma.taskOrder.groupBy({
2024-12-10 10:03:51 +07:00
by: ["taskOrderStatus"],
2024-12-03 09:37:35 +07:00
_count: true,
});
2024-12-10 10:03:51 +07:00
return task.reduce<Record<TaskOrderStatus, number>>(
(a, c) => Object.assign(a, { [c.taskOrderStatus]: c._count }),
2024-12-03 10:20:45 +07:00
{
2024-12-10 10:03:51 +07:00
[TaskOrderStatus.Pending]: 0,
[TaskOrderStatus.InProgress]: 0,
[TaskOrderStatus.Validate]: 0,
[TaskOrderStatus.Complete]: 0,
[TaskOrderStatus.Canceled]: 0,
2024-12-03 10:20:45 +07:00
},
2024-12-03 09:37:35 +07:00
);
}
@Get()
@Security("keycloak")
async getTaskOrderList(
2024-12-03 17:11:44 +07:00
@Request() req: RequestWithUser,
@Query() query: string = "",
@Query() page = 1,
@Query() pageSize = 30,
@Query() assignedByUserId?: string,
2024-12-10 10:03:51 +07:00
@Query() taskOrderStatus?: TaskOrderStatus,
2025-04-17 17:56:55 +07:00
@Query() startDate?: Date,
@Query() endDate?: Date,
2024-12-06 13:21:57 +07:00
) {
return this.getTaskOrderListByCriteria(
req,
query,
page,
pageSize,
assignedByUserId,
taskOrderStatus,
2025-04-17 17:56:55 +07:00
startDate,
endDate,
);
2024-12-06 13:21:57 +07:00
}
@Post("list")
@Security("keycloak")
async getTaskOrderListByCriteria(
@Request() req: RequestWithUser,
@Query() query: string = "",
@Query() page = 1,
@Query() pageSize = 30,
@Query() assignedUserId?: string,
2024-12-10 10:03:51 +07:00
@Query() taskOrderStatus?: TaskOrderStatus,
2025-04-17 13:41:22 +07:00
@Query() startDate?: Date,
@Query() endDate?: Date,
2025-04-17 17:56:55 +07:00
@Body() body?: { code?: string[] },
) {
const where = {
taskOrderStatus,
registeredBranch: { OR: permissionCondCompany(req.user) },
taskList: assignedUserId
? {
some: {
requestWorkStep: { responsibleUserId: assignedUserId },
},
}
: undefined,
code: body?.code ? { in: body.code } : undefined,
OR: queryOrNot(query, [
{ code: { contains: query, mode: "insensitive" } },
2025-04-09 11:54:52 +07:00
{ taskName: { contains: query, mode: "insensitive" } },
{ contactName: { contains: query, mode: "insensitive" } },
{ contactTel: { contains: query, mode: "insensitive" } },
]),
2025-04-17 13:41:22 +07:00
...whereDateQuery(startDate, endDate),
} satisfies Prisma.TaskOrderWhereInput;
const [result, total] = await prisma.$transaction([
prisma.taskOrder.findMany({
where,
include: {
2024-12-20 11:30:34 +07:00
userTask: true,
taskList: true,
institution: true,
registeredBranch: true,
createdBy: true,
},
2025-02-24 09:48:07 +07:00
orderBy: [{ createdAt: "desc" }],
}),
prisma.taskOrder.count({ where }),
]);
return { result, total, page, pageSize };
}
@Get("{taskOrderId}")
@Security("keycloak")
async getTaskOrder(
@Request() req: RequestWithUser,
@Path() taskOrderId: string,
@Query() taskAssignedUserId?: string,
) {
const record = await prisma.taskOrder.findFirst({
where: { id: taskOrderId },
2025-01-24 10:04:06 +07:00
orderBy: [{ urgent: "desc" }, { createdAt: "desc" }],
include: {
userTask: true,
2024-12-24 18:11:40 +07:00
taskProduct: true,
taskList: {
where: {
requestWorkStep: { responsibleUserId: taskAssignedUserId },
},
2025-01-09 14:09:49 +07:00
orderBy: { id: "asc" },
include: {
2024-12-10 10:03:51 +07:00
requestWorkStep: {
include: {
2024-12-11 16:41:57 +07:00
responsibleUser: true,
2024-12-10 10:03:51 +07:00
requestWork: {
include: {
2025-01-23 11:13:31 +07:00
stepStatus: true,
2024-12-10 10:03:51 +07:00
request: {
include: {
employee: true,
2025-01-23 16:09:40 +07:00
quotation: {
include: {
customerBranch: {
include: {
customer: true,
},
},
},
},
2024-12-10 10:03:51 +07:00
},
},
productService: {
include: {
service: {
include: {
workflow: {
include: {
step: {
include: {
value: true,
2025-04-24 13:55:22 +07:00
responsibleGroup: true,
responsiblePerson: {
include: { user: true },
},
responsibleInstitution: true,
},
},
},
},
},
},
2024-12-10 10:03:51 +07:00
work: true,
product: true,
},
},
2024-12-06 14:37:23 +07:00
},
},
},
},
},
},
institution: true,
registeredBranch: true,
createdBy: true,
},
});
if (!record) throw notFoundError("Task Order");
return record;
}
@Post()
2024-12-03 17:11:44 +07:00
@Security("keycloak", MANAGE_ROLES)
async createTaskOrderList(
@Request() req: RequestWithUser,
@Body()
body: {
taskName: string;
contactName: string;
contactTel: string;
institutionId: string;
2024-12-03 17:11:44 +07:00
registeredBranchId?: string;
2025-01-23 14:12:15 +07:00
remark?: string;
taskList: { requestWorkId: string; step: number }[];
2025-01-23 14:19:06 +07:00
taskProduct?: { productId: string; discount?: number }[];
},
) {
return await prisma.$transaction(async (tx) => {
const last = await tx.runningNo.upsert({
where: {
key: "TASK",
},
create: {
key: "TASK",
value: 1,
},
update: {
value: { increment: 1 },
},
});
const current = new Date();
const year = `${current.getFullYear()}`.slice(-2).padStart(2, "0");
const month = `${current.getMonth() + 1}`.padStart(2, "0");
const code = `PO${year}${month}${last.value.toString().padStart(6, "0")}`;
2024-12-24 18:11:40 +07:00
const { taskList, taskProduct, ...rest } = body;
2024-12-03 17:11:44 +07:00
const userAffiliatedBranch = await tx.branch.findFirst({
include: branchRelationPermInclude(req.user),
where: body.registeredBranchId
? { id: body.registeredBranchId }
: {
user: { some: { userId: req.user.sub } },
},
});
if (!userAffiliatedBranch) {
throw new HttpError(
HttpStatus.BAD_REQUEST,
"You must be affilated with at least one branch or specify branch to be registered (System permission required).",
"reqMinAffilatedBranch",
);
}
await permissionCheckCompany(req.user, userAffiliatedBranch);
const updated = await tx.requestWorkStepStatus.updateMany({
where: {
OR: taskList,
workStatus: RequestWorkStatus.Ready,
},
data: { workStatus: RequestWorkStatus.InProgress },
});
if (updated.count !== taskList.length) {
throw new HttpError(
HttpStatus.PRECONDITION_FAILED,
"All request work to issue task order must be in ready state.",
"requestWorkMustReady",
);
}
2025-02-10 10:03:32 +07:00
await tx.institution.updateMany({
where: { id: body.institutionId, status: Status.CREATED },
data: { status: Status.ACTIVE },
});
2025-01-24 09:40:54 +07:00
const work = await tx.requestWorkStepStatus.findMany({
include: {
requestWork: {
include: {
request: {
include: {
quotation: true,
},
},
},
},
},
where: { OR: taskList },
});
2024-12-06 11:25:38 +07:00
return await tx.taskOrder.create({
include: {
taskList: {
include: {
2024-12-10 10:03:51 +07:00
requestWorkStep: {
include: {
2024-12-10 10:03:51 +07:00
requestWork: {
include: {
2024-12-10 10:03:51 +07:00
request: {
include: {
employee: true,
2025-01-23 14:55:43 +07:00
quotation: {
include: {
customerBranch: {
include: {
customer: true,
},
},
},
},
2024-12-10 10:03:51 +07:00
},
},
productService: {
include: {
service: {
include: {
workflow: {
include: {
step: {
include: {
value: true,
responsiblePerson: {
include: { user: true },
},
responsibleInstitution: true,
},
},
},
},
},
},
2024-12-10 10:03:51 +07:00
work: true,
product: true,
},
},
},
},
},
},
},
},
institution: true,
createdBy: true,
},
data: {
...rest,
code,
2025-01-24 09:40:54 +07:00
urgent: work.some((v) => v.requestWork.request.quotation.urgent),
2024-12-03 17:11:44 +07:00
registeredBranchId: userAffiliatedBranch.id,
createdByUserId: req.user.sub,
2024-12-10 10:03:51 +07:00
taskList: { create: taskList },
2024-12-24 18:11:40 +07:00
taskProduct: { create: taskProduct },
},
});
});
}
@Put("{taskOrderId}")
@Security("keycloak")
async editTaskById(
2024-12-03 17:11:44 +07:00
@Request() req: RequestWithUser,
@Path() taskOrderId: string,
@Body()
body: {
taskName: string;
taskOrderStatus?: TaskOrderStatus;
contactName: string;
contactTel: string;
institutionId: string;
2025-01-23 14:12:15 +07:00
remark?: string;
taskList: { requestWorkId: string; step: number }[];
2025-01-23 14:19:06 +07:00
taskProduct?: { productId: string; discount?: number }[];
},
) {
const record = await prisma.taskOrder.findFirst({
where: { id: taskOrderId },
include: {
2024-12-03 17:11:44 +07:00
registeredBranch: { include: branchRelationPermInclude(req.user) },
taskList: {
2024-12-10 10:03:51 +07:00
include: {
requestWorkStep: {
include: { requestWork: true },
},
},
},
institution: true,
createdBy: true,
},
});
2024-12-03 17:11:44 +07:00
if (!record) throw notFoundError("Task Order");
await permissionCheckCompany(req.user, record.registeredBranch);
if (record.taskList.some((v) => v.taskStatus !== "Pending")) {
throw new HttpError(
HttpStatus.BAD_REQUEST,
"One or more task is not pending",
"taskListNotPending",
);
}
2025-03-05 14:41:55 +07:00
return await prisma
.$transaction(async (tx) => {
await Promise.all(
record.taskList
.filter(
(lhs) =>
!body.taskList.find(
(rhs) => lhs.requestWorkId === rhs.requestWorkId && lhs.step === rhs.step,
),
)
.map((v) =>
tx.task.update({
where: { id: v.id },
data: {
requestWorkStep: { update: { workStatus: "Ready" } },
},
}),
),
);
2025-03-05 14:41:55 +07:00
await tx.requestWorkStepStatus.updateMany({
where: {
OR: body.taskList,
workStatus: RequestWorkStatus.Ready,
},
data: { workStatus: RequestWorkStatus.InProgress },
});
2025-01-23 15:50:55 +07:00
2025-03-05 14:41:55 +07:00
const work = await tx.requestWorkStepStatus.findMany({
include: {
requestWork: {
include: {
request: {
include: { quotation: true },
},
2025-01-24 09:40:54 +07:00
},
},
},
2025-03-05 14:41:55 +07:00
where: { OR: body.taskList },
});
2025-01-24 09:40:54 +07:00
2025-03-05 14:41:55 +07:00
return await tx.taskOrder.update({
where: { id: taskOrderId },
include: {
taskList: {
include: {
requestWorkStep: {
include: {
requestWork: true,
},
},
},
},
2025-03-05 14:41:55 +07:00
institution: true,
registeredBranch: true,
createdBy: true,
},
2025-03-05 14:41:55 +07:00
data: {
...body,
urgent: work.some((v) => v.requestWork.request.quotation.urgent),
taskList: {
deleteMany: record?.taskList
.filter(
(lhs) =>
!body.taskList.find(
(rhs) => lhs.requestWorkId === rhs.requestWorkId && lhs.step === rhs.step,
),
)
.map((v) => ({ id: v.id })),
createMany: {
data: body.taskList.filter(
(lhs) =>
!record?.taskList.find(
(rhs) => lhs.requestWorkId === rhs.requestWorkId && lhs.step === rhs.step,
),
),
skipDuplicates: true,
},
},
2025-03-05 14:41:55 +07:00
taskProduct: { deleteMany: {}, create: body.taskProduct },
2024-12-10 10:03:51 +07:00
},
2025-03-05 14:41:55 +07:00
});
})
.then(async (ret) => {
if (body.taskOrderStatus && record.taskOrderStatus !== body.taskOrderStatus) {
await prisma.notification.create({
data: {
2025-03-05 14:50:08 +07:00
title: "มีการส่งงาน / Task Submitted",
detail: "รหัสใบสั่งงาน / Order : " + record.code,
2025-03-05 14:41:55 +07:00
receiverId: record.createdByUserId,
},
});
}
return ret;
});
}
@Delete("{taskOrderId}")
2024-12-03 17:11:44 +07:00
@Security("keycloak", MANAGE_ROLES)
async deleteTask(@Request() req: RequestWithUser, @Path() taskOrderId: string) {
await prisma.$transaction(async (tx) => {
2024-12-03 17:11:44 +07:00
let record = await tx.taskOrder.findFirst({
where: { id: taskOrderId },
2024-12-03 17:11:44 +07:00
include: {
registeredBranch: {
include: branchRelationPermInclude(req.user),
},
taskList: true,
2024-12-03 17:11:44 +07:00
},
});
if (!record) throw notFoundError("Task Order");
2024-12-03 17:11:44 +07:00
await permissionCheck(req.user, record.registeredBranch);
2025-01-13 15:44:29 +07:00
if (record.taskList.some((v) => v.taskStatus !== "Pending")) {
throw new HttpError(
HttpStatus.BAD_REQUEST,
"One or more task is not pending",
"taskListNotPending",
);
}
2025-01-13 15:44:29 +07:00
await Promise.all([deleteFolder(fileLocation.task.attachment(taskOrderId))]);
2025-01-23 16:14:24 +07:00
await Promise.all(
record.taskList.map((v) =>
tx.task.update({
where: { id: v.id },
data: { requestWorkStep: { update: { workStatus: "Ready" } } },
}),
),
);
2025-01-13 15:44:29 +07:00
await tx.taskOrder.delete({ where: { id: taskOrderId } });
});
}
}
@Route("/api/v1/task-order/{taskOrderId}")
@Tags("Task Order")
export class TaskActionController extends Controller {
async #getLineToken() {
if (!process.env.LINE_MESSAGING_API_TOKEN) {
console.warn("Line Webhook Activated but LINE_MESSAGING_API_TOKEN not set.");
}
return process.env.LINE_MESSAGING_API_TOKEN;
}
2024-12-13 11:04:50 +07:00
@Post("set-task-status")
@Security("keycloak")
async changeTaskOrderTaskListStatus(
@Request() req: RequestWithUser,
@Path() taskOrderId: string,
2024-12-13 11:04:50 +07:00
@Body()
body: {
step: number;
requestWorkId: string;
taskStatus: TaskStatus;
failedType?: string;
failedComment?: string;
2024-12-13 11:04:50 +07:00
}[],
) {
return await prisma.$transaction(async (tx) => {
const promises = body.map(async (v) => {
const record = await tx.task.findFirst({
include: { requestWorkStep: true },
2024-12-13 11:04:50 +07:00
where: {
step: v.step,
requestWorkId: v.requestWorkId,
taskOrderId: taskOrderId,
2024-12-13 11:04:50 +07:00
},
});
if (!record) throw notFoundError("Task List");
if (v.taskStatus === TaskStatus.Restart && record.requestWorkStep.responsibleUserId) {
await tx.userTask.updateMany({
where: {
taskOrderId: record.taskOrderId,
userId: record.requestWorkStep.responsibleUserId,
},
data: { userTaskStatus: UserTaskStatus.Restart },
});
}
2024-12-13 11:04:50 +07:00
return await tx.task.update({
where: { id: record.id },
data: {
taskStatus: v.taskStatus,
failedType: v.failedType,
failedComment: v.failedComment,
},
2024-12-13 11:04:50 +07:00
});
});
return await Promise.all(promises);
});
}
@Post("submit")
@Security("keycloak")
2024-12-11 12:06:29 +07:00
async submitTaskOrder(
@Request() req: RequestWithUser,
@Path() taskOrderId: string,
2024-12-11 12:06:29 +07:00
@Query() submitUserId?: string, // for explicit
) {
submitUserId = submitUserId ?? req.user.sub;
const record = await prisma.taskOrder.findFirst({ where: { id: taskOrderId } });
2024-12-11 12:06:29 +07:00
if (!record) throw notFoundError("Task Order");
await prisma.$transaction([
2024-12-25 16:58:42 +07:00
prisma.requestWorkStepStatus.updateMany({
where: {
task: {
some: {
taskOrderId: taskOrderId,
taskStatus: TaskStatus.Success,
requestWorkStep: { responsibleUserId: submitUserId },
},
},
},
data: { workStatus: RequestWorkStatus.Validate },
}),
prisma.task.updateMany({
where: {
taskOrderId: taskOrderId,
taskStatus: TaskStatus.Success,
requestWorkStep: { responsibleUserId: submitUserId },
},
data: {
taskStatus: TaskStatus.Validate,
},
}),
prisma.userTask.updateMany({
where: {
taskOrderId: taskOrderId,
userId: submitUserId,
},
data: { userTaskStatus: UserTaskStatus.Submit, submittedAt: new Date() },
}),
2025-03-05 14:41:55 +07:00
prisma.notification.create({
data: {
2025-03-05 14:50:08 +07:00
title: "มีการส่งงาน / Task Submitted",
detail: "รหัสใบสั่งงาน / Order : " + record.code,
2025-03-05 14:41:55 +07:00
receiverId: record.createdByUserId,
},
}),
]);
2024-12-11 12:06:29 +07:00
}
@Post("complete")
@Security("keycloak")
async completeTaskOrder(@Request() req: RequestWithUser, @Path() taskOrderId: string) {
2024-12-16 11:27:10 +07:00
const record = await prisma.taskOrder.findFirst({ where: { id: taskOrderId } });
if (!record) throw notFoundError("Task Order");
2024-12-24 16:04:54 +07:00
await prisma.$transaction(async (tx) => {
const last = await tx.runningNo.upsert({
where: {
key: "TASK_RI",
},
create: {
key: "TASK_RI",
value: 1,
},
update: {
value: { increment: 1 },
},
});
const current = new Date();
const year = `${current.getFullYear()}`.padStart(2, "0");
const month = `${current.getMonth() + 1}`.padStart(2, "0");
const code = `RI${year}${month}${last.value.toString().padStart(6, "0")}`;
2024-12-24 16:04:54 +07:00
await Promise.all([
tx.taskOrder.update({
where: { id: taskOrderId },
data: {
2025-01-24 09:40:54 +07:00
urgent: false,
taskOrderStatus: TaskOrderStatus.Complete,
codeProductReceived: code,
userTask: {
updateMany: {
where: { taskOrderId },
data: {
userTaskStatus: UserTaskStatus.Submit,
},
},
},
},
2024-12-24 16:04:54 +07:00
}),
tx.requestWorkStepStatus.updateMany({
where: {
task: {
some: {
taskOrderId,
2025-01-09 10:34:20 +07:00
taskStatus: {
notIn: [
TaskStatus.Canceled,
TaskStatus.Success,
TaskStatus.Validate,
TaskStatus.Complete,
],
},
},
2024-12-24 16:04:54 +07:00
},
},
data: { workStatus: RequestWorkStatus.Ready },
}),
tx.task.updateMany({
where: {
taskOrderId: taskOrderId,
2025-01-09 10:34:20 +07:00
taskStatus: {
notIn: [
TaskStatus.Canceled,
TaskStatus.Success,
TaskStatus.Validate,
TaskStatus.Complete,
],
},
},
data: { taskStatus: TaskStatus.Redo },
}),
2024-12-24 16:04:54 +07:00
tx.task.updateMany({
where: {
taskOrderId: taskOrderId,
taskStatus: TaskStatus.Validate,
},
data: { taskStatus: TaskStatus.Complete },
}),
]);
await tx.requestWorkStepStatus.updateMany({
where: {
task: {
2024-12-24 16:04:54 +07:00
some: { taskOrderId, taskStatus: TaskStatus.Complete },
},
},
2024-12-24 16:04:54 +07:00
data: { workStatus: RequestWorkStatus.Completed },
});
const requestList = await tx.requestData.findMany({
include: {
requestWork: {
include: {
productService: {
include: {
product: true,
service: true,
work: {
include: { productOnWork: true },
},
},
},
stepStatus: true,
},
},
},
where: {
requestWork: {
some: {
stepStatus: {
some: {
task: { some: { taskOrderId } },
},
},
},
},
},
});
const completed: string[] = [];
requestList.forEach((item) => {
const completeCheck = item.requestWork.every((work) => {
const stepCount =
work.productService.work?.productOnWork.find(
(v) => v.productId === work.productService.productId,
)?.stepCount || 0;
const completeCount = work.stepStatus.filter(
(v) =>
v.workStatus === RequestWorkStatus.Completed ||
v.workStatus === RequestWorkStatus.Ended ||
v.workStatus === RequestWorkStatus.Canceled,
).length;
// NOTE: step found then check if complete count equals step count
if (stepCount === completeCount && completeCount > 0) return true;
// NOTE: likely no step found and completed at least one
if (stepCount === 0 && completeCount > 0) return true;
});
if (completeCheck) completed.push(item.id);
});
await tx.requestData.updateMany({
where: { id: { in: completed } },
data: { requestDataStatus: RequestDataStatus.Completed },
});
2025-03-05 13:00:10 +07:00
await tx.quotation
.updateManyAndReturn({
where: {
quotationStatus: {
notIn: [QuotationStatus.Canceled, QuotationStatus.ProcessComplete],
},
AND: [
{
requestData: {
every: {
requestDataStatus: {
in: [RequestDataStatus.Canceled, RequestDataStatus.Completed],
},
},
2025-03-05 13:00:10 +07:00
},
},
{
requestData: {
some: {},
},
},
],
},
2025-03-05 13:00:10 +07:00
data: { quotationStatus: QuotationStatus.ProcessComplete, urgent: false },
include: {
customerBranch: {
include: {
customer: {
include: {
branch: {
where: { userId: { not: null } },
},
},
},
},
},
},
2025-03-05 13:00:10 +07:00
})
.then(async (res) => {
await tx.notification.createMany({
data: res.map((v) => ({
2025-03-05 14:50:08 +07:00
title: "สถานะใบเสนอราคาเปลี่ยนแปลง / Quotation Status Updated",
detail: "รหัส / code : " + v.code + " Completed",
2025-03-05 13:00:10 +07:00
receiverId: v.createdByUserId,
})),
});
const token = await this.#getLineToken();
if (!token) return;
const textHead = "JWS ALERT:";
const textAlert = "ขอแจ้งให้ทราบว่าใบเสนอราคา";
const textAlert2 = "ได้ดำเนินการเสร็จสิ้นทุกกระบวนการเรียบร้อยแล้ว";
const textAlert3 = "หากต้องการข้อมูลเพิ่มเติม กรุณาแจ้งให้ฝ่ายที่เกี่ยวข้องทราบ 🙏";
let finalTextWork = "";
let textData = "";
let dataCustomerId: string[] = [];
let textWorkList: string[] = [];
let dataUserId: string[] = [];
if (res) {
res.forEach((data, index) => {
data.customerBranch.customer.branch.forEach((item) => {
if (!dataCustomerId?.includes(item.id) && item.userId) {
dataCustomerId.push(item.id);
dataUserId.push(item.userId);
}
});
textWorkList.push(`${index + 1}. เลขที่ใบเสนอราคา ${data.code} ${data.workName}`);
});
finalTextWork = textWorkList.join("\n");
}
textData = `${textHead}\n\n${textAlert}\n${finalTextWork}\n${textAlert2}\n\n${textAlert3}`;
const data = {
to: dataUserId,
messages: [
{
type: "text",
text: textData,
},
],
};
await fetch("https://api.line.me/v2/bot/message/multicast", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
2025-03-05 13:00:10 +07:00
});
});
2024-12-16 11:27:10 +07:00
}
}
2024-12-04 10:49:22 +07:00
@Route("api/v1/task-order/{taskOrderId}")
2024-12-04 10:49:22 +07:00
@Tags("Task Order")
2024-12-06 17:36:45 +07:00
export class TaskOrderAttachmentController extends Controller {
private async checkPermission(user: RequestWithUser["user"], id: string) {
const data = await prisma.taskOrder.findUnique({
include: { registeredBranch: { include: branchRelationPermInclude(user) } },
where: { id },
});
2024-12-06 17:42:27 +07:00
if (!data) throw notFoundError("Task Order");
2024-12-06 17:36:45 +07:00
await permissionCheck(user, data.registeredBranch);
}
2024-12-11 12:06:29 +07:00
2024-12-06 17:36:45 +07:00
@Get("attachment")
@Security("keycloak")
async listAttachment(@Request() req: RequestWithUser, @Path() taskOrderId: string) {
await this.checkPermission(req.user, taskOrderId);
return await listFile(fileLocation.task.attachment(taskOrderId));
2024-12-06 17:36:45 +07:00
}
@Get("attachment/{name}")
@Security("keycloak")
async getAttachment(@Path() taskOrderId: string, @Path() name: string) {
return await getFile(fileLocation.task.attachment(taskOrderId, name));
2024-12-06 17:36:45 +07:00
}
@Head("attachment/{name}")
async headAttachment(@Path() taskOrderId: string, @Path() name: string) {
return await getPresigned("head", fileLocation.task.attachment(taskOrderId, name));
2024-12-06 17:36:45 +07:00
}
@Put("attachment/{name}")
@Security("keycloak")
async putAttachment(
@Request() req: RequestWithUser,
@Path() taskOrderId: string,
2024-12-06 17:36:45 +07:00
@Path() name: string,
) {
await this.checkPermission(req.user, taskOrderId);
return await setFile(fileLocation.task.attachment(taskOrderId, name));
2024-12-06 17:36:45 +07:00
}
@Delete("attachment/{name}")
@Security("keycloak")
async delAttachment(
@Request() req: RequestWithUser,
@Path() taskOrderId: string,
2024-12-06 17:36:45 +07:00
@Path() name: string,
) {
await this.checkPermission(req.user, taskOrderId);
return await deleteFile(fileLocation.task.attachment(taskOrderId, name));
2024-12-06 17:36:45 +07:00
}
}
@Route("api/v1/user-task-order")
2024-12-16 10:50:03 +07:00
@Tags("User Task Order")
export class UserTaskController extends Controller {
@Get()
@Security("keycloak")
async getUserTask(
@Request() req: RequestWithUser,
@Query() query: string = "",
@Query() page = 1,
@Query() pageSize = 30,
@Query() userTaskStatus?: UserTaskStatus,
2025-04-17 13:41:22 +07:00
@Query() startDate?: Date,
@Query() endDate?: Date,
) {
const where = {
taskList: {
some: {
requestWorkStep: { responsibleUserId: req.user.sub },
},
},
2025-01-08 14:23:21 +07:00
AND: userTaskStatus
? [
{
OR:
2025-01-08 13:42:24 +07:00
userTaskStatus === UserTaskStatus.Pending
2025-01-08 14:23:21 +07:00
? [
{
userTask: {
some: {
userTaskStatus: {
in: [UserTaskStatus.Pending, UserTaskStatus.Restart],
},
userId: req.user.sub,
},
},
},
{
userTask: { none: { userId: req.user.sub } },
},
]
: undefined,
userTask:
userTaskStatus !== UserTaskStatus.Pending
? {
some: {
userTaskStatus,
userId: req.user.sub,
},
}
: undefined,
2025-01-08 13:42:24 +07:00
},
2025-01-08 14:23:21 +07:00
]
: undefined,
OR: queryOrNot(query, [
{ code: { contains: query, mode: "insensitive" } },
2025-04-09 11:54:52 +07:00
{ taskName: { contains: query, mode: "insensitive" } },
{ contactName: { contains: query, mode: "insensitive" } },
{ contactTel: { contains: query, mode: "insensitive" } },
]),
2025-04-17 13:41:22 +07:00
...whereDateQuery(startDate, endDate),
} satisfies Prisma.TaskOrderWhereInput;
const [result, total] = await prisma.$transaction([
prisma.taskOrder.findMany({
where,
include: {
userTask: {
where: { userId: req.user.sub },
},
registeredBranch: true,
institution: true,
createdBy: true,
},
}),
prisma.taskOrder.count({ where }),
]);
return {
result: result.map((lhs) => ({
...lhs,
taskOrderStatus:
lhs.userTask.find((rhs) => rhs.taskOrderId === lhs.id)?.userTaskStatus ??
UserTaskStatus.Pending,
userTask: undefined,
})),
page,
pageSize,
total,
};
}
2024-12-16 10:50:03 +07:00
@Post("accept")
@Security("keycloak")
async acceptTaskOrder(
@Request() req: RequestWithUser,
@Body()
body: {
taskOrderId: string[];
},
) {
const record = await prisma.taskOrder.findMany({
include: {
taskList: {
orderBy: { step: "asc" },
},
},
where: { id: { in: body.taskOrderId } },
});
if (!record) throw notFoundError("Task Order");
await prisma.$transaction(async (tx) => {
const promises = body.taskOrderId.flatMap((taskOrderId) => [
2025-03-05 15:23:37 +07:00
tx.taskOrder
.update({
where: { id: taskOrderId },
data: {
taskOrderStatus: TaskOrderStatus.InProgress,
userTask: {
deleteMany: { userId: req.user.sub },
create: {
userId: req.user.sub,
userTaskStatus: UserTaskStatus.Accept,
acceptedAt: new Date(),
},
2024-12-16 10:50:03 +07:00
},
},
2025-03-05 15:23:37 +07:00
})
.then(async (v) => {
await tx.notification.createMany({
data: [
{
title: "สถานะใบส่งงานมีการเปลี่ยนแปลง / Order Status Changed",
detail: "รหัสใบสั่งงาน / Order : " + v.code + " InProgress",
receiverId: v.createdByUserId,
},
{
title: "มีการรับงาน / Task Accepted",
detail: "รหัสใบสั่งงาน / Order : " + v.code,
receiverId: v.createdByUserId,
},
],
});
}),
2024-12-16 10:50:03 +07:00
tx.task.updateMany({
where: {
taskOrderId: taskOrderId,
2025-01-08 14:41:26 +07:00
taskStatus: { in: [TaskStatus.Pending, TaskStatus.Restart] },
2024-12-16 10:50:03 +07:00
requestWorkStep: { responsibleUserId: req.user.sub },
},
data: {
taskStatus: TaskStatus.InProgress,
},
}),
tx.requestData.updateMany({
where: {
requestWork: {
some: {
stepStatus: {
some: { task: { some: { taskOrderId: taskOrderId } } },
},
},
},
},
data: { requestDataStatus: RequestDataStatus.InProgress },
}),
]);
await Promise.all(promises);
});
}
}