Merge branch 'develop'
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 3s

This commit is contained in:
Methapon2001 2025-07-25 09:46:27 +07:00
commit 23334a4388
4 changed files with 375 additions and 95 deletions

View file

@ -321,6 +321,14 @@ export class RequestDataController extends Controller {
@Route("/api/v1/request-data/{requestDataId}") @Route("/api/v1/request-data/{requestDataId}")
@Tags("Request List") @Tags("Request List")
export class RequestDataActionController extends Controller { export class RequestDataActionController 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;
}
@Post("reject-request-cancel") @Post("reject-request-cancel")
@Security("keycloak") @Security("keycloak")
async rejectRequestCancel( async rejectRequestCancel(
@ -395,6 +403,17 @@ export class RequestDataActionController extends Controller {
}, },
}, },
}, },
include: {
quotation: {
include: {
customerBranch: {
include: {
customer: { include: { branch: { where: { userId: { not: null } } } } },
},
},
},
},
},
}); });
if (!result) throw notFoundError("Request Data"); if (!result) throw notFoundError("Request Data");
@ -445,15 +464,73 @@ export class RequestDataActionController extends Controller {
})), })),
}); });
}), }),
tx.taskOrder.updateMany({ tx.taskOrder
where: { .updateManyAndReturn({
taskList: { where: {
every: { taskStatus: TaskStatus.Canceled }, taskList: {
every: { taskStatus: TaskStatus.Canceled },
},
}, },
}, data: { taskOrderStatus: TaskStatus.Canceled },
data: { taskOrderStatus: TaskStatus.Canceled }, })
}), .then(async (res) => {
await Promise.all(
res.map((v) =>
tx.notification.create({
data: {
title: "สถานะใบเสนอราคาเปลี่ยนแปลง / Quotation Status Updated",
detail: "รหัส / code : " + v.code + " Canceled",
receiverId: v.createdByUserId,
groupReceiver: { create: { name: "document_checker" } },
},
}),
),
);
}),
]); ]);
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 dataUserId: string[] = [];
result.quotation.customerBranch.customer.branch.forEach((item) => {
if (!dataCustomerId?.includes(item.id) && item.userId) {
dataCustomerId.push(item.id);
dataUserId.push(item.userId);
}
});
finalTextWork = `เลขที่ใบเสนอราคา: ${result.code} ${result.quotation.workName}`;
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),
});
}); });
} }
@ -674,6 +751,19 @@ export class RequestDataActionController extends Controller {
}, },
}, },
data: { quotationStatus: QuotationStatus.ProcessComplete, urgent: false }, data: { quotationStatus: QuotationStatus.ProcessComplete, urgent: false },
include: {
customerBranch: {
include: {
customer: {
include: {
branch: {
where: { userId: { not: null } },
},
},
},
},
},
},
}) })
.then(async (res) => { .then(async (res) => {
await tx.notification.createMany({ await tx.notification.createMany({
@ -683,6 +773,56 @@ export class RequestDataActionController extends Controller {
receiverId: v.createdByUserId, 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),
});
}); });
// dataRecord.push(record); // dataRecord.push(record);
return data; return data;

View file

@ -925,14 +925,18 @@ export class TaskActionController extends Controller {
data: { requestDataStatus: RequestDataStatus.Completed }, data: { requestDataStatus: RequestDataStatus.Completed },
}) })
.then(async (res) => { .then(async (res) => {
await tx.notification.createMany({ await Promise.all(
data: res.map((v) => ({ res.map((v) =>
title: "รายการคำขอเสร็จสิ้น / Request Complete", tx.notification.create({
detail: "รหัส / code : " + v.code + " Completed", data: {
receiverId: v.quotation.createdByUserId, title: "สถานะใบเสนอราคาเปลี่ยนแปลง / Quotation Status Updated",
groupReceiver: { create: { name: "document_checker" } }, detail: "รหัส / code : " + v.code + " Completed",
})), receiverId: v.quotation.createdByUserId,
}); groupReceiver: { create: { name: "document_checker" } },
},
}),
),
);
}); });
await tx.quotation await tx.quotation
.updateManyAndReturn({ .updateManyAndReturn({
@ -973,14 +977,18 @@ export class TaskActionController extends Controller {
}, },
}) })
.then(async (res) => { .then(async (res) => {
await tx.notification.createMany({ await Promise.all(
data: res.map((v) => ({ res.map((v) =>
title: "สถานะใบเสนอราคาเปลี่ยนแปลง / Quotation Status Updated", tx.notification.create({
detail: "รหัส / code : " + v.code + " Completed", data: {
receiverId: v.createdByUserId, title: "สถานะใบเสนอราคาเปลี่ยนแปลง / Quotation Status Updated",
groupReceiver: { create: { name: "document_checker" } }, detail: "รหัส / code : " + v.code + " Completed",
})), receiverId: v.createdByUserId,
}); groupReceiver: { create: { name: "document_checker" } },
},
}),
),
);
const token = await this.#getLineToken(); const token = await this.#getLineToken();

View file

@ -83,6 +83,14 @@ type CreditNoteUpdate = {
@Route("api/v1/credit-note") @Route("api/v1/credit-note")
@Tags("Credit Note") @Tags("Credit Note")
export class CreditNoteController extends Controller { export class CreditNoteController 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;
}
@Get("stats") @Get("stats")
@Security("keycloak") @Security("keycloak")
async getCreditNoteStats(@Request() req: RequestWithUser, @Query() quotationId?: string) { async getCreditNoteStats(@Request() req: RequestWithUser, @Query() quotationId?: string) {
@ -366,33 +374,92 @@ export class CreditNoteController extends Controller {
update: { value: { increment: 1 } }, update: { value: { increment: 1 } },
}); });
return await prisma.creditNote.create({ return await prisma.creditNote
include: { .create({
requestWork: { include: {
include: { requestWork: {
request: true, include: {
request: true,
},
},
quotation: {
include: {
customerBranch: {
include: {
customer: { include: { branch: { where: { userId: { not: null } } } } },
},
},
},
}, },
}, },
quotation: true, data: {
}, reason: body.reason,
data: { detail: body.detail,
reason: body.reason, remark: body.remark,
detail: body.detail, paybackType: body.paybackType,
remark: body.remark, paybackBank: body.paybackBank,
paybackType: body.paybackType, paybackAccount: body.paybackAccount,
paybackBank: body.paybackBank, paybackAccountName: body.paybackAccountName,
paybackAccount: body.paybackAccount, code: `CN${currentYear.toString().padStart(2, "0")}${currentMonth.toString().padStart(2, "0")}${last.value.toString().padStart(6, "0")}`,
paybackAccountName: body.paybackAccountName, value,
code: `CN${currentYear.toString().padStart(2, "0")}${currentMonth.toString().padStart(2, "0")}${last.value.toString().padStart(6, "0")}`, requestWork: {
value, connect: body.requestWorkId.map((v) => ({
requestWork: { id: v,
connect: body.requestWorkId.map((v) => ({ })),
id: v, },
})), quotationId: body.quotationId,
}, },
quotationId: body.quotationId, })
}, .then(async (res) => {
}); 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.quotation.customerBranch.customer.branch.forEach((item) => {
if (!dataCustomerId?.includes(item.id) && item.userId) {
dataCustomerId.push(item.id);
dataUserId.push(item.userId);
}
});
finalTextWork = `จำนวนเงิน ${res.value.toFixed(2)} บาท `;
}
textData = `${textHead}\n\n${textAlert}\n${finalTextWork}${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),
});
return res;
});
}, },
{ isolationLevel: Prisma.TransactionIsolationLevel.Serializable }, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable },
); );
@ -573,6 +640,14 @@ export class CreditNoteActionController extends Controller {
return creditNoteData; return creditNoteData;
} }
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;
}
@Post("accept") @Post("accept")
@Security("keycloak", MANAGE_ROLES) @Security("keycloak", MANAGE_ROLES)
async acceptCreditNote(@Request() req: RequestWithUser, @Path() creditNoteId: string) { async acceptCreditNote(@Request() req: RequestWithUser, @Path() creditNoteId: string) {
@ -591,23 +666,81 @@ export class CreditNoteActionController extends Controller {
@Body() body: { paybackStatus: PaybackStatus }, @Body() body: { paybackStatus: PaybackStatus },
) { ) {
await this.#checkPermission(req.user, creditNoteId); await this.#checkPermission(req.user, creditNoteId);
return await prisma.creditNote.update({ return await prisma.creditNote
where: { id: creditNoteId }, .update({
include: { where: { id: creditNoteId },
requestWork: { include: {
include: { requestWork: {
request: true, include: {
request: true,
},
},
quotation: {
include: {
customerBranch: {
include: {
customer: { include: { branch: { where: { userId: { not: null } } } } },
},
},
},
}, },
}, },
quotation: true, data: {
}, creditNoteStatus:
data: { body.paybackStatus === PaybackStatus.Done ? CreditNoteStatus.Success : undefined,
creditNoteStatus: paybackStatus: body.paybackStatus,
body.paybackStatus === PaybackStatus.Done ? CreditNoteStatus.Success : undefined, paybackDate: body.paybackStatus === PaybackStatus.Done ? new Date() : undefined,
paybackStatus: body.paybackStatus, },
paybackDate: body.paybackStatus === PaybackStatus.Done ? new Date() : undefined, })
}, .then(async (res) => {
}); 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.quotation.customerBranch.customer.branch.forEach((item) => {
if (!dataCustomerId?.includes(item.id) && item.userId) {
dataCustomerId.push(item.id);
dataUserId.push(item.userId);
}
});
finalTextWork = `จำนวนเงิน ${res.value.toFixed(2)} บาท `;
}
textData = `${textHead}\n\n${textAlert}\n${finalTextWork}${textAlert2}\n\n${textAlert3}`;
const data = {
to: dataUserId,
messages: [
{
type: "text",
text: textData,
},
],
};
body.paybackStatus === PaybackStatus.Done
? await fetch("https://api.line.me/v2/bot/message/multicast", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(data),
})
: undefined;
});
} }
} }

View file

@ -613,39 +613,22 @@ export class LineController extends Controller {
@Query() endDate?: Date, @Query() endDate?: Date,
) { ) {
const where = { const where = {
OR: OR: queryOrNot<Prisma.QuotationWhereInput[]>(query, [
query || pendingOnly { code: { contains: query, mode: "insensitive" } },
? [ { workName: { contains: query, mode: "insensitive" } },
...(queryOrNot<Prisma.QuotationWhereInput[]>(query, [ {
{ code: { contains: query, mode: "insensitive" } }, customerBranch: {
{ workName: { contains: query, mode: "insensitive" } }, OR: [
{ { code: { contains: query, mode: "insensitive" } },
customerBranch: { { registerName: { contains: query, mode: "insensitive" } },
OR: [ { firstName: { contains: query, mode: "insensitive" } },
{ code: { contains: query, mode: "insensitive" } }, { firstNameEN: { contains: query, mode: "insensitive" } },
{ registerName: { contains: query, mode: "insensitive" } }, { lastName: { contains: query, mode: "insensitive" } },
{ firstName: { contains: query, mode: "insensitive" } }, { lastNameEN: { contains: query, mode: "insensitive" } },
{ firstNameEN: { contains: query, mode: "insensitive" } }, ],
{ lastName: { contains: query, mode: "insensitive" } }, },
{ lastNameEN: { contains: query, mode: "insensitive" } }, },
], ]),
},
},
]) || []),
...(queryOrNot<Prisma.QuotationWhereInput[]>(!!pendingOnly, [
{
requestData: {
some: {
requestDataStatus: "Pending",
},
},
},
{
requestData: { none: {} },
},
]) || []),
]
: undefined,
isDebitNote: false, isDebitNote: false,
code, code,
payCondition, payCondition,
@ -667,6 +650,22 @@ export class LineController extends Controller {
}, },
} }
: undefined, : undefined,
AND: pendingOnly
? {
OR: [
{
requestData: {
some: {
requestDataStatus: "Pending",
},
},
},
{
requestData: { none: {} },
},
],
}
: undefined,
...whereDateQuery(startDate, endDate), ...whereDateQuery(startDate, endDate),
} satisfies Prisma.QuotationWhereInput; } satisfies Prisma.QuotationWhereInput;