Merge pull request #19 from Frappet/feat/notify

feat: data changes notification
This commit is contained in:
Methapon Metanipat 2025-03-05 15:41:01 +07:00 committed by GitHub
commit 6265ac8a65
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 382 additions and 220 deletions

View file

@ -374,6 +374,7 @@ export class ProductController extends Controller {
const record = await prisma.product.update({ const record = await prisma.product.update({
include: { include: {
productGroup: true,
createdBy: true, createdBy: true,
updatedBy: true, updatedBy: true,
}, },
@ -398,6 +399,17 @@ export class ProductController extends Controller {
}); });
} }
await prisma.notification.create({
data: {
title: "สินค้ามีการเปลี่ยนแปลง / Product Updated",
detail: "รหัส / code : " + record.code,
groupReceiver: {
create: [{ name: "sale" }, { name: "head_of_sale" }],
},
registeredBranchId: record.productGroup.registeredBranchId,
},
});
return record; return record;
} }

View file

@ -473,6 +473,7 @@ export class ServiceController extends Controller {
return await tx.service.update({ return await tx.service.update({
include: { include: {
productGroup: true,
createdBy: true, createdBy: true,
updatedBy: true, updatedBy: true,
}, },
@ -523,6 +524,17 @@ export class ServiceController extends Controller {
}); });
}); });
await prisma.notification.create({
data: {
title: "แพคเกจมีการเปลี่ยนแปลง / Package Updated",
detail: "รหัส / code : " + record.code,
groupReceiver: {
create: [{ name: "sale" }, { name: "head_of_sale" }],
},
registeredBranchId: record.productGroup.registeredBranchId,
},
});
return record; return record;
} }

View file

@ -177,55 +177,66 @@ export class QuotationPayment extends Controller {
}, },
}); });
await tx.quotation.update({ await tx.quotation
where: { id: quotation.id }, .update({
data: { where: { id: quotation.id },
quotationStatus: data: {
(paymentSum._sum.amount || 0) >= quotation.finalPrice quotationStatus:
? "PaymentSuccess" (paymentSum._sum.amount || 0) >= quotation.finalPrice
: "PaymentInProcess", ? "PaymentSuccess"
requestData: await (async () => { : "PaymentInProcess",
if ( requestData: await (async () => {
body.paymentStatus === "PaymentSuccess" && if (
(paymentSum._sum.amount || 0) - payment.amount <= 0 body.paymentStatus === "PaymentSuccess" &&
) { (paymentSum._sum.amount || 0) - payment.amount <= 0
const lastRequest = await tx.runningNo.upsert({ ) {
where: { const lastRequest = await tx.runningNo.upsert({
key: `REQUEST_${year}${month}`, where: {
}, key: `REQUEST_${year}${month}`,
create: { },
key: `REQUEST_${year}${month}`, create: {
value: quotation.worker.length, key: `REQUEST_${year}${month}`,
}, value: quotation.worker.length,
update: { value: { increment: quotation.worker.length } }, },
}); update: { value: { increment: quotation.worker.length } },
return { });
create: quotation.worker.flatMap((v, i) => { return {
const productEmployee = quotation.productServiceList.flatMap((item) => create: quotation.worker.flatMap((v, i) => {
item.worker.findIndex((w) => w.employeeId === v.employeeId) !== -1 const productEmployee = quotation.productServiceList.flatMap((item) =>
? { productServiceId: item.id } item.worker.findIndex((w) => w.employeeId === v.employeeId) !== -1
: [], ? { productServiceId: item.id }
); : [],
);
if (productEmployee.length <= 0) return []; if (productEmployee.length <= 0) return [];
return { return {
code: `TR${year}${month}${(lastRequest.value - quotation.worker.length + i + 1).toString().padStart(6, "0")}`, code: `TR${year}${month}${(lastRequest.value - quotation.worker.length + i + 1).toString().padStart(6, "0")}`,
employeeId: v.employeeId, employeeId: v.employeeId,
requestWork: { requestWork: {
create: quotation.productServiceList.flatMap((item) => create: quotation.productServiceList.flatMap((item) =>
item.worker.findIndex((w) => w.employeeId === v.employeeId) !== -1 item.worker.findIndex((w) => w.employeeId === v.employeeId) !== -1
? { productServiceId: item.id } ? { productServiceId: item.id }
: [], : [],
), ),
}, },
}; };
}), }),
}; };
} }
})(), })(),
}, },
}); })
.then(async (res) => {
if (quotation.quotationStatus !== res.quotationStatus)
await tx.notification.create({
data: {
title: "สถานะใบเสนอราคาเปลี่ยนแปลง / Quotation Status Updated",
detail: "รหัส / code : " + res.code + " " + res.quotationStatus,
receiverId: res.createdByUserId,
},
});
});
return payment; return payment;
}); });

View file

@ -454,7 +454,7 @@ export class QuotationController extends Controller {
const { productServiceList: _productServiceList, worker: _worker, ...rest } = body; const { productServiceList: _productServiceList, worker: _worker, ...rest } = body;
return await prisma.$transaction(async (tx) => { const ret = await prisma.$transaction(async (tx) => {
const nonExistEmployee = body.worker.filter((v) => typeof v !== "string"); const nonExistEmployee = body.worker.filter((v) => typeof v !== "string");
const lastEmployee = await tx.runningNo.upsert({ const lastEmployee = await tx.runningNo.upsert({
where: { where: {
@ -639,6 +639,17 @@ export class QuotationController extends Controller {
}, },
}); });
}); });
await prisma.notification.create({
data: {
title: "ใบเสนอราคาใหม่ / New Quotation",
detail: "รหัส / code : " + ret.code,
registeredBranchId: ret.registeredBranchId,
groupReceiver: { create: [{ name: "accountant" }, { name: "head_of_accountant" }] },
},
});
return ret;
} }
@Put("{quotationId}") @Put("{quotationId}")
@ -1125,41 +1136,53 @@ export class QuotationActionController extends Controller {
}, },
update: { value: { increment: quotation.worker.length } }, update: { value: { increment: quotation.worker.length } },
}); });
await tx.quotation.update({ await tx.quotation
where: { id: quotationId, isDebitNote: false }, .update({
data: { include: { requestData: true },
quotationStatus: QuotationStatus.PaymentSuccess, // NOTE: change back if already complete or canceled where: { id: quotationId, isDebitNote: false },
worker: { data: {
createMany: { quotationStatus: QuotationStatus.PaymentSuccess, // NOTE: change back if already complete or canceled
data: rearrange worker: {
.filter((lhs) => !quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId)) createMany: {
.map((v, i) => ({ data: rearrange
no: quotation._count.worker + i + 1, .filter((lhs) => !quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId))
employeeId: v.workerId, .map((v, i) => ({
})), no: quotation._count.worker + i + 1,
employeeId: v.workerId,
})),
},
}, },
requestData:
quotation.quotationStatus === "PaymentInProcess" ||
quotation.quotationStatus === "PaymentSuccess"
? {
create: rearrange
.filter(
(lhs) =>
!quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId) &&
lhs.productServiceId.length > 0,
)
.map((v, i) => ({
code: `TR${year}${month}${(lastRequest.value - quotation._count.worker + i + 1).toString().padStart(6, "0")}`,
employeeId: v.workerId,
requestWork: {
create: v.productServiceId.map((v) => ({ productServiceId: v })),
},
})),
}
: undefined,
}, },
requestData: })
quotation.quotationStatus === "PaymentInProcess" || .then(async (ret) => {
quotation.quotationStatus === "PaymentSuccess" await prisma.notification.create({
? { data: {
create: rearrange title: "รายการคำขอใหม่ / New Request",
.filter( detail: "รหัส / code : " + ret.requestData.map((v) => v.code).join(", "),
(lhs) => registeredBranchId: ret.registeredBranchId,
!quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId) && groupReceiver: { create: { name: "document_checker" } },
lhs.productServiceId.length > 0, },
) });
.map((v, i) => ({ });
code: `TR${year}${month}${(lastRequest.value - quotation._count.worker + i + 1).toString().padStart(6, "0")}`,
employeeId: v.workerId,
requestWork: {
create: v.productServiceId.map((v) => ({ productServiceId: v })),
},
})),
}
: undefined,
},
});
}); });
} }
} }

View file

@ -268,14 +268,24 @@ export class RequestDataActionController extends Controller {
}), }),
]); ]);
await Promise.all([ await Promise.all([
tx.quotation.updateMany({ tx.quotation
where: { .updateManyAndReturn({
requestData: { where: {
every: { requestDataStatus: RequestDataStatus.Canceled }, requestData: {
every: { requestDataStatus: RequestDataStatus.Canceled },
},
}, },
}, data: { quotationStatus: QuotationStatus.Canceled, urgent: false },
data: { quotationStatus: QuotationStatus.Canceled, urgent: false }, })
}), .then(async (res) => {
await tx.notification.createMany({
data: res.map((v) => ({
title: "สถานะใบเสนอราคาเปลี่ยนแปลง / Quotation Status Updated",
detail: "รหัส / code : " + v.code + " Canceled",
receiverId: v.createdByUserId,
})),
});
}),
tx.taskOrder.updateMany({ tx.taskOrder.updateMany({
where: { where: {
taskList: { taskList: {
@ -405,14 +415,25 @@ export class RequestDataActionController extends Controller {
data: { taskStatus: TaskStatus.Canceled }, data: { taskStatus: TaskStatus.Canceled },
}); });
await Promise.all([ await Promise.all([
tx.quotation.updateMany({ tx.quotation
where: { .updateManyAndReturn({
requestData: { where: {
every: { requestDataStatus: RequestDataStatus.Canceled }, quotationStatus: { not: QuotationStatus.Canceled },
requestData: {
every: { requestDataStatus: RequestDataStatus.Canceled },
},
}, },
}, data: { quotationStatus: QuotationStatus.Canceled, urgent: false },
data: { quotationStatus: QuotationStatus.Canceled, urgent: false }, })
}), .then(async (res) => {
await tx.notification.createMany({
data: res.map((v) => ({
title: "สถานะใบเสนอราคาเปลี่ยนแปลง / Quotation Status Updated",
detail: "รหัส / code : " + v.code + " Canceled",
receiverId: v.createdByUserId,
})),
});
}),
tx.taskOrder.updateMany({ tx.taskOrder.updateMany({
where: { where: {
taskList: { taskList: {
@ -479,21 +500,31 @@ export class RequestDataActionController extends Controller {
where: { id: { in: completed } }, where: { id: { in: completed } },
data: { requestDataStatus: RequestDataStatus.Completed }, data: { requestDataStatus: RequestDataStatus.Completed },
}); });
await tx.quotation.updateMany({ await tx.quotation
where: { .updateManyAndReturn({
quotationStatus: { where: {
notIn: [QuotationStatus.Canceled, QuotationStatus.ProcessComplete], quotationStatus: {
}, notIn: [QuotationStatus.Canceled, QuotationStatus.ProcessComplete],
requestData: { },
every: { requestData: {
requestDataStatus: { every: {
in: [RequestDataStatus.Canceled, RequestDataStatus.Completed], requestDataStatus: {
in: [RequestDataStatus.Canceled, RequestDataStatus.Completed],
},
}, },
}, },
}, },
}, data: { quotationStatus: QuotationStatus.ProcessComplete, urgent: false },
data: { quotationStatus: QuotationStatus.ProcessComplete, urgent: false }, })
}); .then(async (res) => {
await tx.notification.createMany({
data: res.map((v) => ({
title: "สถานะใบเสนอราคาเปลี่ยนแปลง / Quotation Status Updated",
detail: "รหัส / code : " + v.code + " Completed",
receiverId: v.createdByUserId,
})),
});
});
// dataRecord.push(record); // dataRecord.push(record);
return data; return data;
}); });
@ -812,14 +843,25 @@ export class RequestListController extends Controller {
data: { taskStatus: TaskStatus.Canceled }, data: { taskStatus: TaskStatus.Canceled },
}); });
await Promise.all([ await Promise.all([
tx.quotation.updateMany({ tx.quotation
where: { .updateManyAndReturn({
requestData: { where: {
every: { requestDataStatus: RequestDataStatus.Canceled }, quotationStatus: { not: QuotationStatus.Canceled },
requestData: {
every: { requestDataStatus: RequestDataStatus.Canceled },
},
}, },
}, data: { quotationStatus: QuotationStatus.Canceled, urgent: false },
data: { quotationStatus: QuotationStatus.Canceled, urgent: false }, })
}), .then(async (res) => {
await tx.notification.createMany({
data: res.map((v) => ({
title: "สถานะใบเสนอราคาเปลี่ยนแปลง / Quotation Status Updated",
detail: "รหัส / code : " + v.code + " Canceled",
receiverId: v.createdByUserId,
})),
});
}),
tx.taskOrder.updateMany({ tx.taskOrder.updateMany({
where: { where: {
taskList: { taskList: {
@ -887,19 +929,32 @@ export class RequestListController extends Controller {
where: { id: { in: completed } }, where: { id: { in: completed } },
data: { requestDataStatus: RequestDataStatus.Completed }, data: { requestDataStatus: RequestDataStatus.Completed },
}); });
await tx.quotation.updateMany({ await tx.quotation
where: { .updateManyAndReturn({
quotationStatus: { where: {
notIn: [QuotationStatus.Canceled, QuotationStatus.ProcessComplete], quotationStatus: {
}, notIn: [QuotationStatus.Canceled, QuotationStatus.ProcessComplete],
requestData: { },
every: { requestData: {
requestDataStatus: { in: [RequestDataStatus.Canceled, RequestDataStatus.Completed] }, every: {
requestDataStatus: {
in: [RequestDataStatus.Canceled, RequestDataStatus.Completed],
},
},
}, },
}, },
}, data: { quotationStatus: QuotationStatus.ProcessComplete, urgent: false },
data: { quotationStatus: QuotationStatus.ProcessComplete, urgent: false }, })
}); .then(async (res) => {
await tx.notification.createMany({
data: res.map((v) => ({
title: "สถานะใบเสนอราคาเปลี่ยนแปลง / Quotation Status Updated",
detail: "รหัส / code : " + v.code + " Completed",
receiverId: v.createdByUserId,
})),
});
});
return record; return record;
}); });
} }

View file

@ -433,88 +433,101 @@ export class TaskController extends Controller {
); );
} }
return await prisma.$transaction(async (tx) => { return await prisma
await Promise.all( .$transaction(async (tx) => {
record.taskList await Promise.all(
.filter( record.taskList
(lhs) => .filter(
!body.taskList.find( (lhs) =>
(rhs) => lhs.requestWorkId === rhs.requestWorkId && lhs.step === rhs.step, !body.taskList.find(
), (rhs) => lhs.requestWorkId === rhs.requestWorkId && lhs.step === rhs.step,
) ),
.map((v) => )
tx.task.update({ .map((v) =>
where: { id: v.id }, tx.task.update({
data: { where: { id: v.id },
requestWorkStep: { update: { workStatus: "Ready" } }, data: {
}, requestWorkStep: { update: { workStatus: "Ready" } },
}), },
), }),
); ),
);
await tx.requestWorkStepStatus.updateMany({ await tx.requestWorkStepStatus.updateMany({
where: { where: {
OR: body.taskList, OR: body.taskList,
workStatus: RequestWorkStatus.Ready, workStatus: RequestWorkStatus.Ready,
},
data: { workStatus: RequestWorkStatus.InProgress },
});
const work = await tx.requestWorkStepStatus.findMany({
include: {
requestWork: {
include: {
request: {
include: { quotation: true },
},
},
}, },
}, data: { workStatus: RequestWorkStatus.InProgress },
where: { OR: body.taskList }, });
});
return await tx.taskOrder.update({ const work = await tx.requestWorkStepStatus.findMany({
where: { id: taskOrderId }, include: {
include: { requestWork: {
taskList: { include: {
include: { request: {
requestWorkStep: { include: { quotation: true },
include: {
requestWork: true,
}, },
}, },
}, },
}, },
institution: true, where: { OR: body.taskList },
registeredBranch: true, });
createdBy: true,
}, return await tx.taskOrder.update({
data: { where: { id: taskOrderId },
...body, include: {
urgent: work.some((v) => v.requestWork.request.quotation.urgent), taskList: {
taskList: { include: {
deleteMany: record?.taskList requestWorkStep: {
.filter( include: {
(lhs) => requestWork: true,
!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,
}, },
institution: true,
registeredBranch: true,
createdBy: true,
}, },
taskProduct: { deleteMany: {}, create: body.taskProduct }, 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,
},
},
taskProduct: { deleteMany: {}, create: body.taskProduct },
},
});
})
.then(async (ret) => {
if (body.taskOrderStatus && record.taskOrderStatus !== body.taskOrderStatus) {
await prisma.notification.create({
data: {
title: "มีการส่งงาน / Task Submitted",
detail: "รหัสใบสั่งงาน / Order : " + record.code,
receiverId: record.createdByUserId,
},
});
}
return ret;
}); });
});
} }
@Delete("{taskOrderId}") @Delete("{taskOrderId}")
@ -651,6 +664,13 @@ export class TaskActionController extends Controller {
}, },
data: { userTaskStatus: UserTaskStatus.Submit, submittedAt: new Date() }, data: { userTaskStatus: UserTaskStatus.Submit, submittedAt: new Date() },
}), }),
prisma.notification.create({
data: {
title: "มีการส่งงาน / Task Submitted",
detail: "รหัสใบสั่งงาน / Order : " + record.code,
receiverId: record.createdByUserId,
},
}),
]); ]);
} }
@ -785,19 +805,31 @@ export class TaskActionController extends Controller {
where: { id: { in: completed } }, where: { id: { in: completed } },
data: { requestDataStatus: RequestDataStatus.Completed }, data: { requestDataStatus: RequestDataStatus.Completed },
}); });
await tx.quotation.updateMany({ await tx.quotation
where: { .updateManyAndReturn({
quotationStatus: { where: {
notIn: [QuotationStatus.Canceled, QuotationStatus.ProcessComplete], quotationStatus: {
}, notIn: [QuotationStatus.Canceled, QuotationStatus.ProcessComplete],
requestData: { },
every: { requestData: {
requestDataStatus: { in: [RequestDataStatus.Canceled, RequestDataStatus.Completed] }, every: {
requestDataStatus: {
in: [RequestDataStatus.Canceled, RequestDataStatus.Completed],
},
},
}, },
}, },
}, data: { quotationStatus: QuotationStatus.ProcessComplete, urgent: false },
data: { quotationStatus: QuotationStatus.ProcessComplete, urgent: false }, })
}); .then(async (res) => {
await tx.notification.createMany({
data: res.map((v) => ({
title: "สถานะใบเสนอราคาเปลี่ยนแปลง / Quotation Status Updated",
detail: "รหัส / code : " + v.code + " Completed",
receiverId: v.createdByUserId,
})),
});
});
}); });
} }
} }
@ -965,20 +997,37 @@ export class UserTaskController extends Controller {
await prisma.$transaction(async (tx) => { await prisma.$transaction(async (tx) => {
const promises = body.taskOrderId.flatMap((taskOrderId) => [ const promises = body.taskOrderId.flatMap((taskOrderId) => [
tx.taskOrder.update({ tx.taskOrder
where: { id: taskOrderId }, .update({
data: { where: { id: taskOrderId },
taskOrderStatus: TaskOrderStatus.InProgress, data: {
userTask: { taskOrderStatus: TaskOrderStatus.InProgress,
deleteMany: { userId: req.user.sub }, userTask: {
create: { deleteMany: { userId: req.user.sub },
userId: req.user.sub, create: {
userTaskStatus: UserTaskStatus.Accept, userId: req.user.sub,
acceptedAt: new Date(), userTaskStatus: UserTaskStatus.Accept,
acceptedAt: new Date(),
},
}, },
}, },
}, })
}), .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,
},
],
});
}),
tx.task.updateMany({ tx.task.updateMany({
where: { where: {
taskOrderId: taskOrderId, taskOrderId: taskOrderId,