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
.updateManyAndReturn({
where: { where: {
taskList: { taskList: {
every: { taskStatus: TaskStatus.Canceled }, 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({
data: {
title: "สถานะใบเสนอราคาเปลี่ยนแปลง / Quotation Status Updated",
detail: "รหัส / code : " + v.code + " Completed", detail: "รหัส / code : " + v.code + " Completed",
receiverId: v.quotation.createdByUserId, receiverId: v.quotation.createdByUserId,
groupReceiver: { create: { name: "document_checker" } }, 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) =>
tx.notification.create({
data: {
title: "สถานะใบเสนอราคาเปลี่ยนแปลง / Quotation Status Updated", title: "สถานะใบเสนอราคาเปลี่ยนแปลง / Quotation Status Updated",
detail: "รหัส / code : " + v.code + " Completed", detail: "รหัส / code : " + v.code + " Completed",
receiverId: v.createdByUserId, receiverId: v.createdByUserId,
groupReceiver: { create: { name: "document_checker" } }, 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,14 +374,23 @@ export class CreditNoteController extends Controller {
update: { value: { increment: 1 } }, update: { value: { increment: 1 } },
}); });
return await prisma.creditNote.create({ return await prisma.creditNote
.create({
include: { include: {
requestWork: { requestWork: {
include: { include: {
request: true, request: true,
}, },
}, },
quotation: true, quotation: {
include: {
customerBranch: {
include: {
customer: { include: { branch: { where: { userId: { not: null } } } } },
},
},
},
},
}, },
data: { data: {
reason: body.reason, reason: body.reason,
@ -392,6 +409,56 @@ export class CreditNoteController extends Controller {
}, },
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,7 +666,8 @@ 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
.update({
where: { id: creditNoteId }, where: { id: creditNoteId },
include: { include: {
requestWork: { requestWork: {
@ -599,7 +675,15 @@ export class CreditNoteActionController extends Controller {
request: true, request: true,
}, },
}, },
quotation: true, quotation: {
include: {
customerBranch: {
include: {
customer: { include: { branch: { where: { userId: { not: null } } } } },
},
},
},
},
}, },
data: { data: {
creditNoteStatus: creditNoteStatus:
@ -607,6 +691,55 @@ export class CreditNoteActionController extends Controller {
paybackStatus: body.paybackStatus, paybackStatus: body.paybackStatus,
paybackDate: body.paybackStatus === PaybackStatus.Done ? new Date() : undefined, 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,10 +613,7 @@ export class LineController extends Controller {
@Query() endDate?: Date, @Query() endDate?: Date,
) { ) {
const where = { const where = {
OR: OR: queryOrNot<Prisma.QuotationWhereInput[]>(query, [
query || pendingOnly
? [
...(queryOrNot<Prisma.QuotationWhereInput[]>(query, [
{ code: { contains: query, mode: "insensitive" } }, { code: { contains: query, mode: "insensitive" } },
{ workName: { contains: query, mode: "insensitive" } }, { workName: { contains: query, mode: "insensitive" } },
{ {
@ -631,21 +628,7 @@ export class LineController extends Controller {
], ],
}, },
}, },
]) || []), ]),
...(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;