summaryrefslogtreecommitdiff
path: root/lib/itb/service.ts
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-09-25 03:28:27 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-09-25 03:28:27 +0000
commit4c2d4c235bd80368e31cae9c375e9a585f6a6844 (patch)
tree7fd1847e1e30ef2052281453bfb7a1c45ac6627a /lib/itb/service.ts
parentf69e125f1a0b47bbc22e2784208bf829bcdd24f8 (diff)
(대표님) archiver 추가, 데이터룸구현
Diffstat (limited to 'lib/itb/service.ts')
-rw-r--r--lib/itb/service.ts417
1 files changed, 210 insertions, 207 deletions
diff --git a/lib/itb/service.ts b/lib/itb/service.ts
index f649bdf5..181285cc 100644
--- a/lib/itb/service.ts
+++ b/lib/itb/service.ts
@@ -3,7 +3,7 @@
import db from "@/db/db";
import { purchaseRequestsView, purchaseRequests, purchaseRequestAttachments, rfqsLast, rfqLastAttachments, rfqLastAttachmentRevisions, rfqPrItems, users } from "@/db/schema";
-import { eq, and, desc, ilike, or, sql, asc, inArray ,like} from "drizzle-orm";
+import { eq, and, desc, ilike, or, sql, asc, inArray, like } from "drizzle-orm";
import { revalidatePath, revalidateTag } from "next/cache";
import { getServerSession } from 'next-auth/next'
import { authOptions } from '@/app/api/auth/[...nextauth]/route'
@@ -293,7 +293,7 @@ export async function approvePurchaseRequestAndCreateRfq(
.where(eq(purchaseRequestAttachments.requestId, requestId));
- const rfqCode = await generateItbRfqCode(purchasePicId);
+ const rfqCode = await generateItbRfqCode(purchasePicId);
const [rfq] = await tx.insert(rfqsLast).values({
rfqCode,
@@ -547,10 +547,10 @@ export async function getPurchaseRequestAttachments(requestId: number) {
}
} catch (error) {
console.error("Get attachments error:", error)
- return {
+ return {
success: false,
error: "첨부파일 조회 중 오류가 발생했습니다.",
- data: []
+ data: []
}
}
}
@@ -558,223 +558,226 @@ export async function getPurchaseRequestAttachments(requestId: number) {
export async function generateItbRfqCode(purchasePicId?: number): Promise<string> {
try {
- let userCode = "???";
-
- // purchasePicId가 있으면 users 테이블에서 userCode 조회
- if (purchasePicId) {
- const [user] = await db
- .select({ userCode: users.userCode })
- .from(users)
- .where(eq(users.id, purchasePicId))
- .limit(1);
-
- if (user?.userCode) {
- userCode = user.userCode;
+ let userCode = "???";
+
+ // purchasePicId가 있으면 users 테이블에서 userCode 조회
+ if (purchasePicId) {
+ const [user] = await db
+ .select({ userCode: users.userCode })
+ .from(users)
+ .where(eq(users.id, purchasePicId))
+ .limit(1);
+
+ if (user?.userCode) {
+ userCode = user.userCode;
+ }
}
- }
-
- // 동일한 userCode로 시작하는 마지막 RFQ 조회
- const lastRfq = await db
- .select({ rfqCode: rfqsLast.rfqCode })
- .from(rfqsLast)
- .where(like(rfqsLast.rfqCode, `I${userCode}%`))
- .orderBy(desc(rfqsLast.createdAt))
- .limit(1);
-
- let nextNumber = 1;
-
- if (lastRfq.length > 0 && lastRfq[0].rfqCode) {
- const rfqCode = lastRfq[0].rfqCode;
- const serialNumber = rfqCode.slice(-5); // 마지막 5자리
-
- if (/^\d{5}$/.test(serialNumber)) {
- nextNumber = parseInt(serialNumber) + 1;
+
+ // 동일한 userCode로 시작하는 마지막 RFQ 조회
+ const lastRfq = await db
+ .select({ rfqCode: rfqsLast.rfqCode })
+ .from(rfqsLast)
+ .where(like(rfqsLast.rfqCode, `I${userCode}%`))
+ .orderBy(desc(rfqsLast.createdAt))
+ .limit(1);
+
+ let nextNumber = 1;
+
+ if (lastRfq.length > 0 && lastRfq[0].rfqCode) {
+ const rfqCode = lastRfq[0].rfqCode;
+ const serialNumber = rfqCode.slice(-5); // 마지막 5자리
+
+ if (/^\d{5}$/.test(serialNumber)) {
+ nextNumber = parseInt(serialNumber) + 1;
+ }
}
- }
-
- const paddedNumber = String(nextNumber).padStart(5, "0");
-
- return `I${userCode}${paddedNumber}`;
+
+ const paddedNumber = String(nextNumber).padStart(5, "0");
+
+ return `I${userCode}${paddedNumber}`;
} catch (error) {
- console.error("Error generating ITB RFQ code:", error);
- const fallback = Date.now().toString().slice(-5);
- return `I???${fallback}`;
+ console.error("Error generating ITB RFQ code:", error);
+ const fallback = Date.now().toString().slice(-5);
+ return `I???${fallback}`;
}
- }
-
+}
+
- // lib/purchase-requests/service.ts에 추가
+// lib/purchase-requests/service.ts에 추가
// 여러 구매 요청 승인 및 RFQ 생성
export async function approvePurchaseRequestsAndCreateRfqs(
requestIds: number[],
purchasePicId?: number
- ) {
+) {
try {
- const session = await getServerSession(authOptions)
- if (!session?.user?.id) throw new Error("Unauthorized");
- const userId = Number(session.user.id)
-
- const results = []
-
- for (const requestId of requestIds) {
- try {
- const result = await db.transaction(async (tx) => {
- // 구매 요청 조회
- const [request] = await tx
- .select()
- .from(purchaseRequests)
- .where(eq(purchaseRequests.id, requestId))
-
- if (!request) {
- throw new Error(`구매 요청 ${requestId}를 찾을 수 없습니다.`)
- }
-
- if (request.status === "RFQ생성완료") {
- return { skipped: true, requestId, message: "이미 RFQ가 생성되었습니다." }
- }
-
- const attachments = await tx
- .select()
- .from(purchaseRequestAttachments)
- .where(eq(purchaseRequestAttachments.requestId, requestId))
-
- const rfqCode = await generateItbRfqCode(purchasePicId)
-
- // 마감일 기본값 설정 (입력값 없으면 생성일 + 7일)
- const defaultDueDate = getDefaultDueDate();
-
- const [rfq] = await tx
- .insert(rfqsLast)
- .values({
- rfqCode,
- projectId: request.projectId,
- itemCode: request.items?.[0]?.itemCode,
- itemName: request.items?.[0]?.itemName,
- packageNo: request.packageNo,
- packageName: request.packageName,
- EngPicName: request.engPicName,
- pic: purchasePicId || null,
- status: "RFQ 생성",
- dueDate: defaultDueDate, // 마감일 기본값 설정
- projectCompany: request.projectCompany,
- projectSite: request.projectSite,
- smCode: request.smCode,
- createdBy: userId,
- updatedBy: userId,
- })
- .returning()
-
- // 첨부파일 이관
- for (const [index, attachment] of attachments.entries()) {
- const [rfqAttachment] = await tx
- .insert(rfqLastAttachments)
- .values({
- attachmentType: "설계",
- serialNo: `ENG-${String(index + 1).padStart(3, "0")}`,
- rfqId: rfq.id,
- description:
- attachment.description ||
- `설계문서 - ${attachment.originalFileName}`,
- currentRevision: "Rev.0",
- createdBy: userId,
- })
- .returning()
-
- const [revision] = await tx
- .insert(rfqLastAttachmentRevisions)
- .values({
- attachmentId: rfqAttachment.id,
- revisionNo: "Rev.0",
- revisionComment: "구매 요청에서 이관된 설계 문서",
- isLatest: true,
- fileName: attachment.fileName,
- originalFileName: attachment.originalFileName,
- filePath: attachment.filePath,
- fileSize: attachment.fileSize,
- fileType: attachment.fileType,
- createdBy: userId,
+ const session = await getServerSession(authOptions)
+ if (!session?.user?.id) throw new Error("Unauthorized");
+ const userId = Number(session.user.id)
+
+ const results = []
+
+ for (const requestId of requestIds) {
+ try {
+ const result = await db.transaction(async (tx) => {
+ // 구매 요청 조회
+ const [request] = await tx
+ .select()
+ .from(purchaseRequests)
+ .where(eq(purchaseRequests.id, requestId))
+
+ if (!request) {
+ throw new Error(`구매 요청 ${requestId}를 찾을 수 없습니다.`)
+ }
+
+ if (request.status === "RFQ생성완료") {
+ return { skipped: true, requestId, message: "이미 RFQ가 생성되었습니다." }
+ }
+
+ const attachments = await tx
+ .select()
+ .from(purchaseRequestAttachments)
+ .where(eq(purchaseRequestAttachments.requestId, requestId))
+
+ const rfqCode = await generateItbRfqCode(purchasePicId)
+
+ const defaultDueDate = (() => {
+ const d = new Date();
+ d.setDate(d.getDate() + 15);
+ return d;
+ })();
+
+
+ const [rfq] = await tx
+ .insert(rfqsLast)
+ .values({
+ rfqCode,
+ projectId: request.projectId,
+ itemCode: request.items?.[0]?.itemCode,
+ itemName: request.items?.[0]?.itemName,
+ packageNo: request.packageNo,
+ packageName: request.packageName,
+ EngPicName: request.engPicName,
+ pic: purchasePicId || null,
+ status: "RFQ 생성",
+ dueDate: defaultDueDate, // 마감일 기본값 설정
+ projectCompany: request.projectCompany,
+ projectSite: request.projectSite,
+ smCode: request.smCode,
+ createdBy: userId,
+ updatedBy: userId,
+ })
+ .returning()
+
+ // 첨부파일 이관
+ for (const [index, attachment] of attachments.entries()) {
+ const [rfqAttachment] = await tx
+ .insert(rfqLastAttachments)
+ .values({
+ attachmentType: "설계",
+ serialNo: `ENG-${String(index + 1).padStart(3, "0")}`,
+ rfqId: rfq.id,
+ description:
+ attachment.description ||
+ `설계문서 - ${attachment.originalFileName}`,
+ currentRevision: "Rev.0",
+ createdBy: userId,
+ })
+ .returning()
+
+ const [revision] = await tx
+ .insert(rfqLastAttachmentRevisions)
+ .values({
+ attachmentId: rfqAttachment.id,
+ revisionNo: "Rev.0",
+ revisionComment: "구매 요청에서 이관된 설계 문서",
+ isLatest: true,
+ fileName: attachment.fileName,
+ originalFileName: attachment.originalFileName,
+ filePath: attachment.filePath,
+ fileSize: attachment.fileSize,
+ fileType: attachment.fileType,
+ createdBy: userId,
+ })
+ .returning()
+
+ await tx
+ .update(rfqLastAttachments)
+ .set({ latestRevisionId: revision.id })
+ .where(eq(rfqLastAttachments.id, rfqAttachment.id))
+ }
+
+ // 품목 이관
+ if (request.items && request.items.length > 0) {
+ console.log("🚀 품목 이관 시작:", {
+ requestId,
+ itemsCount: request.items.length,
+ items: request.items
+ });
+
+ const prItemsData = request.items.map((item, index) => ({
+ rfqsLastId: rfq.id,
+ rfqItem: `${index + 1}`.padStart(3, '0'),
+ prItem: `${index + 1}`.padStart(3, '0'),
+ prNo: rfqCode,
+ materialCategory: request.majorItemMaterialCategory,
+ materialCode: item.itemCode,
+ materialDescription: item.itemName,
+ quantity: item.quantity,
+ uom: item.unit,
+ majorYn: index === 0,
+ remark: item.remarks || null,
+ }));
+
+ console.log("🔍 삽입할 데이터:", prItemsData);
+
+ const insertedItems = await tx.insert(rfqPrItems).values(prItemsData).returning();
+ console.log("✅ 품목 이관 완료:", insertedItems);
+ } else {
+ console.log("❌ 품목이 없음:", {
+ requestId,
+ hasItems: !!request.items,
+ itemsLength: request.items?.length || 0
+ });
+ }
+
+ // 구매 요청 상태 업데이트
+ await tx
+ .update(purchaseRequests)
+ .set({
+ status: "RFQ생성완료",
+ rfqId: rfq.id,
+ rfqCode: rfq.rfqCode,
+ rfqCreatedAt: new Date(),
+ purchasePicId,
+ updatedBy: userId,
+ updatedAt: new Date(),
+ })
+ .where(eq(purchaseRequests.id, requestId))
+
+ return { success: true, rfq, requestId }
})
- .returning()
-
- await tx
- .update(rfqLastAttachments)
- .set({ latestRevisionId: revision.id })
- .where(eq(rfqLastAttachments.id, rfqAttachment.id))
- }
-
- // 품목 이관
- if (request.items && request.items.length > 0) {
- console.log("🚀 품목 이관 시작:", {
- requestId,
- itemsCount: request.items.length,
- items: request.items
- });
-
- const prItemsData = request.items.map((item, index) => ({
- rfqsLastId: rfq.id,
- rfqItem: `${index + 1}`.padStart(3, '0'),
- prItem: `${index + 1}`.padStart(3, '0'),
- prNo: rfqCode,
- materialCategory:request.majorItemMaterialCategory,
- materialCode: item.itemCode,
- materialDescription: item.itemName,
- quantity: item.quantity,
- uom: item.unit,
- majorYn: index === 0,
- remark: item.remarks || null,
- }));
-
- console.log("🔍 삽입할 데이터:", prItemsData);
-
- const insertedItems = await tx.insert(rfqPrItems).values(prItemsData).returning();
- console.log("✅ 품목 이관 완료:", insertedItems);
- } else {
- console.log("❌ 품목이 없음:", {
+
+ results.push(result)
+ } catch (err: any) {
+ console.error(`구매 요청 ${requestId} 처리 중 오류:`, err)
+ results.push({
+ success: false,
requestId,
- hasItems: !!request.items,
- itemsLength: request.items?.length || 0
- });
+ error: err.message || "알 수 없는 오류 발생",
+ })
}
-
- // 구매 요청 상태 업데이트
- await tx
- .update(purchaseRequests)
- .set({
- status: "RFQ생성완료",
- rfqId: rfq.id,
- rfqCode: rfq.rfqCode,
- rfqCreatedAt: new Date(),
- purchasePicId,
- updatedBy: userId,
- updatedAt: new Date(),
- })
- .where(eq(purchaseRequests.id, requestId))
-
- return { success: true, rfq, requestId }
- })
-
- results.push(result)
- } catch (err: any) {
- console.error(`구매 요청 ${requestId} 처리 중 오류:`, err)
- results.push({
- success: false,
- requestId,
- error: err.message || "알 수 없는 오류 발생",
- })
}
- }
-
- // 캐시 무효화
- revalidateTag("purchase-requests")
- revalidateTag( "purchase-request-stats")
-
- revalidateTag("rfqs")
-
- return results
+
+ // 캐시 무효화
+ revalidateTag("purchase-requests")
+ revalidateTag("purchase-request-stats")
+
+ revalidateTag("rfqs")
+
+ return results
} catch (err: any) {
- console.error("approvePurchaseRequestsAndCreateRfqs 실행 오류:", err)
- throw new Error(err.message || "구매 요청 처리 중 오류가 발생했습니다.")
+ console.error("approvePurchaseRequestsAndCreateRfqs 실행 오류:", err)
+ throw new Error(err.message || "구매 요청 처리 중 오류가 발생했습니다.")
}
- }
- \ No newline at end of file
+}