summaryrefslogtreecommitdiff
path: root/lib/vendors/service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vendors/service.ts')
-rw-r--r--lib/vendors/service.ts173
1 files changed, 86 insertions, 87 deletions
diff --git a/lib/vendors/service.ts b/lib/vendors/service.ts
index 853b3701..e3309786 100644
--- a/lib/vendors/service.ts
+++ b/lib/vendors/service.ts
@@ -1654,14 +1654,20 @@ export async function generatePQNumber(isProject: boolean = false) {
}
}
-export async function requestPQVendors(input: ApproveVendorsInput & { userId: number }) {
+export async function requestPQVendors(input: ApproveVendorsInput & {
+ userId: number,
+ agreements?: Record<string, boolean>,
+ dueDate?: string | null,
+ type?: "GENERAL" | "PROJECT" | "NON_INSPECTION",
+ extraNote?: string,
+ pqItems?: string
+}) {
unstable_noStore();
-
+
const session = await getServerSession(authOptions);
const requesterId = session?.user?.id ? Number(session.user.id) : null;
try {
- // 프로젝트 정보 가져오기 (projectId가 있는 경우)
let projectInfo = null;
if (input.projectId) {
const project = await db
@@ -1673,95 +1679,72 @@ export async function requestPQVendors(input: ApproveVendorsInput & { userId: nu
.from(projects)
.where(eq(projects.id, input.projectId))
.limit(1);
-
+
if (project.length > 0) {
projectInfo = project[0];
}
}
-
- // 트랜잭션 내에서 협력업체 상태 업데이트 및 이메일 발송
+
const result = await db.transaction(async (tx) => {
- // 0. 업데이트 전 협력업체 상태 조회
const vendorsBeforeUpdate = await tx
- .select({
- id: vendors.id,
- status: vendors.status,
- })
+ .select({ id: vendors.id, status: vendors.status })
.from(vendors)
.where(inArray(vendors.id, input.ids));
-
- // 1. 협력업체 상태 업데이트
+
const [updated] = await tx
.update(vendors)
- .set({
- status: "IN_PQ",
- updatedAt: new Date()
- })
+ .set({ status: "IN_PQ", updatedAt: new Date() })
.where(inArray(vendors.id, input.ids))
.returning();
-
- // 2. 업데이트된 협력업체 정보 조회
+
const updatedVendors = await tx
- .select({
- id: vendors.id,
- vendorName: vendors.vendorName,
- email: vendors.email,
- })
+ .select({ id: vendors.id, vendorName: vendors.vendorName, email: vendors.email })
.from(vendors)
.where(inArray(vendors.id, input.ids));
-
- // 3. vendorPQSubmissions 테이블에 레코드 추가 (프로젝트 PQ와 일반 PQ 모두)
- const pqType = input.projectId ? "PROJECT" : "GENERAL";
+
+ const pqType = input.type;
const currentDate = new Date();
-
- // 기존 PQ 요청이 있는지 확인 (중복 방지)
+
const existingSubmissions = await tx
- .select({
- vendorId: vendorPQSubmissions.vendorId,
- projectId: vendorPQSubmissions.projectId,
- type: vendorPQSubmissions.type
- })
+ .select({ vendorId: vendorPQSubmissions.vendorId })
.from(vendorPQSubmissions)
.where(
and(
inArray(vendorPQSubmissions.vendorId, input.ids),
- eq(vendorPQSubmissions.type, pqType),
- input.projectId
+ pqType ? eq(vendorPQSubmissions.type, pqType) : undefined,
+ input.projectId
? eq(vendorPQSubmissions.projectId, input.projectId)
: isNull(vendorPQSubmissions.projectId)
)
);
-
- // 중복되지 않는 벤더에 대해서만 새 PQ 요청 생성
- const existingVendorIds = new Set(existingSubmissions.map(s => s.vendorId));
- const newVendorIds = input.ids.filter(id => !existingVendorIds.has(id));
-
+
+ const existingVendorIds = new Set(existingSubmissions.map((s) => s.vendorId));
+ const newVendorIds = input.ids.filter((id) => !existingVendorIds.has(id));
+
if (newVendorIds.length > 0) {
- // 각 벤더별로 유니크한 PQ 번호 생성 및 저장
const vendorPQDataPromises = newVendorIds.map(async (vendorId) => {
- // PQ 번호 생성 (프로젝트 PQ인지 여부 전달)
const pqNumber = await generatePQNumber(pqType === "PROJECT");
-
- return {
- vendorId,
- pqNumber, // 생성된 PQ 번호 저장
- projectId: input.projectId || null,
- type: pqType,
- status: "REQUESTED",
- requesterId: input.userId || requesterId, // 요청자 ID 저장
- createdAt: currentDate,
- updatedAt: currentDate,
- };
+
+ return {
+ vendorId,
+ pqNumber,
+ projectId: input.projectId || null,
+ type: pqType,
+ status: "REQUESTED",
+ requesterId: input.userId || requesterId,
+ dueDate: input.dueDate ? new Date(input.dueDate) : null,
+ agreements: input.agreements ?? {},
+ pqItems: input.pqItems || null,
+ createdAt: currentDate,
+ updatedAt: currentDate,
+ };
});
-
- // 모든 PQ 번호 생성 완료 대기
+
const vendorPQData = await Promise.all(vendorPQDataPromises);
-
- // 트랜잭션 내에서 데이터 삽입
+
await tx.insert(vendorPQSubmissions).values(vendorPQData);
}
- // 4. 로그 기록
await Promise.all(
vendorsBeforeUpdate.map(async (vendorBefore) => {
await tx.insert(vendorsLogs).values({
@@ -1770,25 +1753,23 @@ export async function requestPQVendors(input: ApproveVendorsInput & { userId: nu
action: "status_change",
oldStatus: vendorBefore.status,
newStatus: "IN_PQ",
- comment: input.projectId
- ? `Project PQ requested (Project: ${projectInfo?.projectCode || input.projectId})`
+ comment: input.projectId
+ ? `Project PQ requested (Project: ${projectInfo?.projectCode || input.projectId})`
: "General PQ requested",
});
})
);
const headersList = await headers();
- const host = headersList.get('host') || 'localhost:3000';
+ const host = headersList.get("host") || "localhost:3000";
- // 5. 각 벤더에게 이메일 발송
await Promise.all(
updatedVendors.map(async (vendor) => {
- if (!vendor.email) return; // 이메일이 없으면 스킵
-
+ if (!vendor.email) return;
+
try {
- const userLang = "en"; // 기본값, 필요시 협력업체 언어 설정에서 가져오기
-
- // PQ 번호 조회 (이메일에 포함하기 위해)
+ const userLang = "en";
+
const vendorPQ = await tx
.select({ pqNumber: vendorPQSubmissions.pqNumber })
.from(vendorPQSubmissions)
@@ -1796,59 +1777,76 @@ export async function requestPQVendors(input: ApproveVendorsInput & { userId: nu
and(
eq(vendorPQSubmissions.vendorId, vendor.id),
eq(vendorPQSubmissions.type, pqType),
- input.projectId
+ input.projectId
? eq(vendorPQSubmissions.projectId, input.projectId)
: isNull(vendorPQSubmissions.projectId)
)
)
.limit(1)
- .then(rows => rows[0]);
-
- // 프로젝트 PQ인지 일반 PQ인지에 따라 제목 변경
+ .then((rows) => rows[0]);
+
const subject = input.projectId
- ? `[eVCP] You are invited to submit Project PQ ${vendorPQ?.pqNumber || ''} for ${projectInfo?.projectCode || 'a project'}`
- : `[eVCP] You are invited to submit PQ ${vendorPQ?.pqNumber || ''}`;
-
- // 로그인 URL에 프로젝트 ID 추가 (프로젝트 PQ인 경우)
+ ? `[eVCP] You are invited to submit Project PQ ${vendorPQ?.pqNumber || ""} for ${projectInfo?.projectCode || "a project"}`
+ : input.type === "NON_INSPECTION"
+ ? `[eVCP] You are invited to submit Non-Inspection PQ ${vendorPQ?.pqNumber || ""}`
+ : `[eVCP] You are invited to submit PQ ${vendorPQ?.pqNumber || ""}`;
+
const baseLoginUrl = `${host}/partners/pq`;
const loginUrl = input.projectId
? `${baseLoginUrl}?projectId=${input.projectId}`
: baseLoginUrl;
-
+
+ // 체크된 계약 항목 배열 생성
+ const contracts = input.agreements
+ ? Object.entries(input.agreements)
+ .filter(([_, checked]) => checked)
+ .map(([name, _]) => name)
+ : [];
+
+ // PQ 대상 품목
+ const pqItems = input.pqItems || " - ";
+
await sendEmail({
to: vendor.email,
subject,
- template: input.projectId ? "project-pq" : "pq", // 프로젝트별 템플릿 사용
+ template: input.projectId ? "project-pq" : input.type === "NON_INSPECTION" ? "non-inspection-pq" : "pq",
context: {
vendorName: vendor.vendorName,
+ vendorContact: "", // 담당자 정보가 없으므로 빈 문자열
+ pqNumber: vendorPQ?.pqNumber || "",
+ senderName: session?.user?.name || "eVCP",
+ senderEmail: session?.user?.email || "noreply@evcp.com",
+ dueDate: input.dueDate ? new Date(input.dueDate).toLocaleDateString('ko-KR') : "",
+ pqItems,
+ contracts,
+ extraNote: input.extraNote || "",
+ currentYear: new Date().getFullYear().toString(),
loginUrl,
language: userLang,
- projectCode: projectInfo?.projectCode || '',
- projectName: projectInfo?.projectName || '',
+ projectCode: projectInfo?.projectCode || "",
+ projectName: projectInfo?.projectName || "",
hasProject: !!input.projectId,
- pqNumber: vendorPQ?.pqNumber || '', // PQ 번호 추가
+ pqType: input.type || "GENERAL",
},
});
} catch (emailError) {
console.error(`Failed to send email to vendor ${vendor.id}:`, emailError);
- // 이메일 전송 실패는 전체 트랜잭션을 실패시키지 않음
}
})
);
-
+
return updated;
});
-
- // 캐시 무효화
+
revalidateTag("vendors");
revalidateTag("vendor-status-counts");
revalidateTag("vendor-pq-submissions");
-
+
if (input.projectId) {
revalidateTag(`project-${input.projectId}`);
revalidateTag(`project-pq-submissions-${input.projectId}`);
}
-
+
return { data: result, error: null };
} catch (err) {
console.error("Error requesting PQ from vendors:", err);
@@ -1856,6 +1854,7 @@ export async function requestPQVendors(input: ApproveVendorsInput & { userId: nu
}
}
+
interface SendVendorsInput {
ids: number[];
}