diff options
Diffstat (limited to 'lib/techsales-rfq/service.ts')
| -rw-r--r-- | lib/techsales-rfq/service.ts | 169 |
1 files changed, 168 insertions, 1 deletions
diff --git a/lib/techsales-rfq/service.ts b/lib/techsales-rfq/service.ts index cf4d02e2..13d0bbce 100644 --- a/lib/techsales-rfq/service.ts +++ b/lib/techsales-rfq/service.ts @@ -3662,7 +3662,8 @@ export async function getTechSalesVendorQuotationAttachments(quotationId: number updatedAt: techSalesVendorQuotationAttachments.updatedAt,
})
.from(techSalesVendorQuotationAttachments)
- .where(eq(techSalesVendorQuotationAttachments.quotationId, quotationId))
+ .where(and(eq(techSalesVendorQuotationAttachments.quotationId, quotationId),
+ eq(techSalesVendorQuotationAttachments.isVendorUpload, true)))
.orderBy(desc(techSalesVendorQuotationAttachments.createdAt));
return { data: attachments };
@@ -3680,6 +3681,172 @@ export async function getTechSalesVendorQuotationAttachments(quotationId: number }
/**
+ * 기술영업 RFQ 기준 벤더 견적서 요약 목록 조회 (eml 첨부 전용)
+ */
+export async function getTechSalesVendorQuotationsForRfq(rfqId: number) {
+ unstable_noStore();
+ try {
+ const quotations = await db
+ .select({
+ id: techSalesVendorQuotations.id,
+ vendorId: techSalesVendorQuotations.vendorId,
+ vendorName: techVendors.vendorName,
+ vendorCode: techVendors.vendorCode,
+ quotationVersion: techSalesVendorQuotations.quotationVersion,
+ status: techSalesVendorQuotations.status,
+ })
+ .from(techSalesVendorQuotations)
+ .leftJoin(techVendors, eq(techSalesVendorQuotations.vendorId, techVendors.id))
+ .where(eq(techSalesVendorQuotations.rfqId, rfqId))
+ .orderBy(
+ asc(techVendors.vendorName),
+ asc(techSalesVendorQuotations.id)
+ );
+
+ return { data: quotations, error: null };
+ } catch (error) {
+ console.error("기술영업 RFQ 벤더 견적서 목록 조회 오류:", error);
+ return { data: [], error: getErrorMessage(error) };
+ }
+}
+
+/**
+ * 기술영업 벤더 견적서 eml 첨부파일 조회 (isVendorUpload = false)
+ */
+export async function getTechSalesVendorQuotationEmlAttachments(quotationId: number) {
+ unstable_noStore();
+ try {
+ const attachments = await db
+ .select({
+ id: techSalesVendorQuotationAttachments.id,
+ quotationId: techSalesVendorQuotationAttachments.quotationId,
+ revisionId: techSalesVendorQuotationAttachments.revisionId,
+ fileName: techSalesVendorQuotationAttachments.fileName,
+ originalFileName: techSalesVendorQuotationAttachments.originalFileName,
+ fileSize: techSalesVendorQuotationAttachments.fileSize,
+ fileType: techSalesVendorQuotationAttachments.fileType,
+ filePath: techSalesVendorQuotationAttachments.filePath,
+ description: techSalesVendorQuotationAttachments.description,
+ uploadedBy: techSalesVendorQuotationAttachments.uploadedBy,
+ vendorId: techSalesVendorQuotationAttachments.vendorId,
+ isVendorUpload: techSalesVendorQuotationAttachments.isVendorUpload,
+ createdAt: techSalesVendorQuotationAttachments.createdAt,
+ updatedAt: techSalesVendorQuotationAttachments.updatedAt,
+ })
+ .from(techSalesVendorQuotationAttachments)
+ .where(
+ and(
+ eq(techSalesVendorQuotationAttachments.quotationId, quotationId),
+ eq(techSalesVendorQuotationAttachments.isVendorUpload, false)
+ )
+ )
+ .orderBy(desc(techSalesVendorQuotationAttachments.createdAt));
+
+ return { data: attachments, error: null };
+ } catch (error) {
+ console.error("기술영업 벤더 견적서 eml 첨부파일 조회 오류:", error);
+ return { data: [], error: getErrorMessage(error) };
+ }
+}
+
+/**
+ * 기술영업 벤더 견적서 eml 첨부파일 업로드/삭제 처리
+ * - isVendorUpload = false 로 저장 (메일 등 별도 전달 문서 보관용)
+ */
+export async function processTechSalesVendorQuotationEmlAttachments(params: {
+ quotationId: number;
+ newFiles?: { file: File; description?: string }[];
+ deleteAttachmentIds?: number[];
+ uploadedBy: number;
+ revisionId?: number;
+}) {
+ unstable_noStore();
+ const { quotationId, newFiles = [], deleteAttachmentIds = [], uploadedBy, revisionId } = params;
+
+ try {
+ // 견적서 확인
+ const quotation = await db.query.techSalesVendorQuotations.findFirst({
+ where: eq(techSalesVendorQuotations.id, quotationId),
+ columns: { id: true, rfqId: true, quotationVersion: true },
+ });
+
+ if (!quotation) {
+ return { data: null, error: "견적서를 찾을 수 없습니다." };
+ }
+
+ const targetRevisionId = revisionId ?? quotation.quotationVersion ?? 0;
+
+ await db.transaction(async (tx) => {
+ // 삭제 처리 (벤더 업로드 파일은 삭제하지 않음)
+ if (deleteAttachmentIds.length > 0) {
+ const deletable = await tx.query.techSalesVendorQuotationAttachments.findMany({
+ where: inArray(techSalesVendorQuotationAttachments.id, deleteAttachmentIds),
+ });
+
+ for (const attachment of deletable) {
+ if (attachment.isVendorUpload) {
+ throw new Error("벤더가 업로드한 파일은 여기서 삭제할 수 없습니다.");
+ }
+
+ await tx
+ .delete(techSalesVendorQuotationAttachments)
+ .where(eq(techSalesVendorQuotationAttachments.id, attachment.id));
+
+ try {
+ deleteFile(attachment.filePath);
+ } catch (fileError) {
+ console.warn("eml 첨부파일 삭제 중 파일 시스템 오류:", fileError);
+ }
+ }
+ }
+
+ // 업로드 처리
+ if (newFiles.length > 0) {
+ for (const { file, description } of newFiles) {
+ const saveResult = await saveFile({
+ file,
+ directory: `techsales-quotations/${quotationId}/eml`,
+ userId: uploadedBy.toString(),
+ });
+
+ if (!saveResult.success) {
+ throw new Error(saveResult.error || "파일 저장에 실패했습니다.");
+ }
+
+ await tx.insert(techSalesVendorQuotationAttachments).values({
+ quotationId,
+ revisionId: targetRevisionId,
+ fileName: saveResult.fileName!,
+ originalFileName: saveResult.originalName || file.name,
+ fileSize: file.size,
+ fileType: file.type || undefined,
+ filePath: saveResult.publicPath!,
+ description: description || null,
+ uploadedBy,
+ isVendorUpload: false,
+ });
+ }
+ }
+ });
+
+ // 캐시 무효화
+ revalidateTag(`quotation-${quotationId}`);
+ revalidateTag("quotation-attachments");
+ revalidateTag("techSalesVendorQuotations");
+ if (quotation.rfqId) {
+ revalidateTag(`techSalesRfq-${quotation.rfqId}`);
+ }
+ revalidateTag("techSalesRfqs");
+
+ const refreshed = await getTechSalesVendorQuotationEmlAttachments(quotationId);
+ return { data: refreshed.data, error: refreshed.error };
+ } catch (error) {
+ console.error("기술영업 벤더 견적서 eml 첨부파일 처리 오류:", error);
+ return { data: null, error: getErrorMessage(error) };
+ }
+}
+
+/**
* 특정 리비전의 견적서 첨부파일 조회
*/
export async function getTechSalesVendorQuotationAttachmentsByRevision(quotationId: number, revisionId: number) {
|
