summaryrefslogtreecommitdiff
path: root/lib/rfq-last/service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rfq-last/service.ts')
-rw-r--r--lib/rfq-last/service.ts134
1 files changed, 122 insertions, 12 deletions
diff --git a/lib/rfq-last/service.ts b/lib/rfq-last/service.ts
index 723a69fe..85db1ea7 100644
--- a/lib/rfq-last/service.ts
+++ b/lib/rfq-last/service.ts
@@ -16,6 +16,7 @@ import { addDays, format } from "date-fns"
import { ko, enUS } from "date-fns/locale"
import { generateBasicContractsForVendor } from "../basic-contract/gen-service";
import { writeFile, mkdir } from "fs/promises";
+import { generateItbRfqCode } from "../itb/service";
export async function getRfqs(input: GetRfqsSchema) {
@@ -37,17 +38,14 @@ export async function getRfqs(input: GetRfqsSchema) {
break;
case "itb":
// ITB: projectCompany가 있는 경우
- typeFilter = and(
- isNotNull(rfqsLastView.projectCompany),
- ne(rfqsLastView.projectCompany, '')
- );
+ typeFilter =
+ like(rfqsLastView.rfqCode,'I%')
+
+ ;
break;
case "rfq":
// RFQ: prNumber가 있는 경우
- typeFilter = and(
- isNotNull(rfqsLastView.prNumber),
- ne(rfqsLastView.prNumber, '')
- );
+ typeFilter = like(rfqsLastView.rfqCode,'R%');
break;
}
}
@@ -1854,6 +1852,26 @@ export async function getRfqWithDetails(rfqId: number) {
)
.orderBy(desc(rfqLastDetailsView.detailId));
+ const tbeSessionsData = await db
+ .select({
+ vendorId: rfqLastTbeSessions.vendorId,
+ sessionCode: rfqLastTbeSessions.sessionCode,
+ status: rfqLastTbeSessions.status,
+ evaluationResult: rfqLastTbeSessions.evaluationResult,
+ conditionalRequirements: rfqLastTbeSessions.conditionalRequirements,
+ conditionsFulfilled: rfqLastTbeSessions.conditionsFulfilled,
+ plannedStartDate: rfqLastTbeSessions.plannedStartDate,
+ actualStartDate: rfqLastTbeSessions.actualStartDate,
+ actualEndDate: rfqLastTbeSessions.actualEndDate,
+ })
+ .from(rfqLastTbeSessions)
+ .where(eq(rfqLastTbeSessions.rfqsLastId, rfqId));
+
+ const tbeByVendor = tbeSessionsData.reduce((acc, tbe) => {
+ acc[tbe.vendorId] = tbe;
+ return acc;
+ }, {} as Record<number, typeof tbeSessionsData[0]>);
+
return {
success: true,
data: {
@@ -1990,6 +2008,12 @@ export async function getRfqWithDetails(rfqId: number) {
emailResentCount: d.emailResentCount,
lastEmailSentAt: d.lastEmailSentAt,
emailStatus: d.emailStatus,
+
+ // TBE 정보 추가
+ tbeSession: d.vendorId ? tbeByVendor[d.vendorId] : null,
+ tbeStatus: d.vendorId ? tbeByVendor[d.vendorId]?.status : null,
+ tbeEvaluationResult: d.vendorId ? tbeByVendor[d.vendorId]?.evaluationResult : null,
+ tbeSessionCode: d.vendorId ? tbeByVendor[d.vendorId]?.sessionCode : null,
})),
}
};
@@ -2987,19 +3011,25 @@ async function prepareEmailAttachments(rfqId: number, attachmentIds: number[]) {
for (const { attachment, revision } of attachments) {
if (revision?.filePath) {
+
+ const cleanPath = revision.filePath.startsWith('/api/files')
+ ? revision.filePath.slice('/api/files'.length)
+ : revision.filePath;
+
try {
const isProduction = process.env.NODE_ENV === "production";
- const fullPath = isProduction
+
+ const fullPath = !isProduction
? path.join(
process.cwd(),
`public`,
- revision.filePath
+ cleanPath
)
: path.join(
`${process.env.NAS_PATH}`,
- revision.filePath
+ cleanPath
);
const fileBuffer = await fs.readFile(fullPath);
@@ -3009,7 +3039,8 @@ async function prepareEmailAttachments(rfqId: number, attachmentIds: number[]) {
contentType: revision.fileType || 'application/octet-stream'
});
} catch (error) {
- console.error(`첨부파일 읽기 실패: ${revision.filePath}`, error);
+
+ console.error(`첨부파일 읽기 실패: ${cleanPath}`, error);
}
}
}
@@ -4320,4 +4351,83 @@ export async function updateShortList(
console.error("Short List 업데이트 실패:", error);
throw new Error("Short List 업데이트에 실패했습니다.");
}
+}
+
+interface AssignPicParams {
+ rfqIds: number[];
+ picUserId: number;
+}
+
+export async function assignPicToRfqs({ rfqIds, picUserId }: AssignPicParams) {
+ try {
+ const session = await getServerSession(authOptions);
+ if (!session?.user) {
+ throw new Error("인증이 필요합니다.");
+ }
+
+ // 선택된 담당자 정보 조회
+ const picUser = await db.query.users.findFirst({
+ where: eq(users.id, picUserId),
+ });
+
+ if (!picUser) {
+ throw new Error("선택한 담당자를 찾을 수 없습니다.");
+ }
+
+ // RFQ 코드가 "I"로 시작하는 것들만 필터링 (추가 검증)
+ const targetRfqs = await db.query.rfqsLast.findMany({
+ where: inArray(rfqsLast.id, rfqIds),
+ });
+
+ // "I"로 시작하는 RFQ만 필터링
+ const validRfqs = targetRfqs.filter(rfq => rfq.rfqCode?.startsWith("I"));
+
+ if (validRfqs.length === 0) {
+ throw new Error("담당자를 지정할 수 있는 ITB가 없습니다.");
+ }
+
+ // 트랜잭션으로 처리하여 동시성 문제 방지
+ const updatedCount = await db.transaction(async (tx) => {
+ let successCount = 0;
+
+ for (const rfq of validRfqs) {
+ // 각 RFQ에 대해 새로운 코드 생성
+ const newRfqCode = await generateItbRfqCode(picUser.id);
+
+ // RFQ 업데이트
+ const result = await tx.update(rfqsLast)
+ .set({
+ rfqCode: newRfqCode, // 새로운 RFQ 코드로 업데이트
+ pic: picUser.id,
+ picCode: picUser.userCode || undefined,
+ picName: picUser.name,
+ status: "구매담당지정", // 상태도 업데이트
+ updatedBy: parseInt(session.user.id),
+ updatedAt: new Date(),
+ })
+ .where(eq(rfqsLast.id, rfq.id));
+
+ if (result) {
+ successCount++;
+ console.log(`RFQ ${rfq.rfqCode} -> ${newRfqCode} 업데이트 완료`);
+ }
+ }
+
+ return successCount;
+ });
+
+ revalidatePath("/evcp/rfq-last");
+
+ return {
+ success: true,
+ message: `${updatedCount}건의 ITB에 담당자가 지정되고 코드가 재발급되었습니다.`,
+ updatedCount
+ };
+ } catch (error) {
+ console.error("담당자 지정 오류:", error);
+ return {
+ success: false,
+ message: error instanceof Error ? error.message : "담당자 지정 중 오류가 발생했습니다."
+ };
+ }
} \ No newline at end of file