summaryrefslogtreecommitdiff
path: root/lib/b-rfq/validations.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/b-rfq/validations.ts')
-rw-r--r--lib/b-rfq/validations.ts139
1 files changed, 139 insertions, 0 deletions
diff --git a/lib/b-rfq/validations.ts b/lib/b-rfq/validations.ts
index 15cc9425..f9473656 100644
--- a/lib/b-rfq/validations.ts
+++ b/lib/b-rfq/validations.ts
@@ -7,6 +7,7 @@ import { createSearchParamsCache,
import * as z from "zod"
import { getFiltersStateParser, getSortingStateParser } from "@/lib/parsers"
+import { VendorAttachmentResponse } from "@/db/schema";
export const searchParamsRFQDashboardCache = createSearchParamsCache({
// 공통 플래그
@@ -265,3 +266,141 @@ export const searchParamsInitialRfqDetailCache = createSearchParamsCache({
export type GetInitialRfqDetailSchema = Awaited<ReturnType<typeof searchParamsInitialRfqDetailCache.parse>>;
+
+export const updateInitialRfqSchema = z.object({
+ initialRfqStatus: z.enum(["DRAFT", "Init. RFQ Sent", "S/L Decline", "Init. RFQ Answered"]),
+ dueDate: z.date({
+ required_error: "마감일을 선택해주세요.",
+ }),
+ validDate: z.date().optional(),
+ incotermsCode: z.string().max(20, "Incoterms 코드는 20자 이하여야 합니다.").optional(),
+ classification: z.string().max(255, "분류는 255자 이하여야 합니다.").optional(),
+ sparepart: z.string().max(255, "예비부품은 255자 이하여야 합니다.").optional(),
+ shortList: z.boolean().default(false),
+ returnYn: z.boolean().default(false),
+ cpRequestYn: z.boolean().default(false),
+ prjectGtcYn: z.boolean().default(false),
+ rfqRevision: z.number().int().min(0, "RFQ 리비전은 0 이상이어야 합니다.").default(0),
+})
+
+export const removeInitialRfqsSchema = z.object({
+ ids: z.array(z.number()).min(1, "최소 하나의 항목을 선택해주세요."),
+})
+
+export type UpdateInitialRfqSchema = z.infer<typeof updateInitialRfqSchema>
+export type RemoveInitialRfqsSchema = z.infer<typeof removeInitialRfqsSchema>
+
+// 벌크 이메일 발송 스키마
+export const bulkEmailSchema = z.object({
+ initialRfqIds: z.array(z.number()).min(1, "최소 하나의 초기 RFQ를 선택해주세요."),
+ language: z.enum(["en", "ko"]).default("en"),
+})
+
+export type BulkEmailInput = z.infer<typeof bulkEmailSchema>
+
+// 검색 파라미터 캐시 설정
+
+export type ResponseStatus = "NOT_RESPONDED" | "RESPONDED" | "REVISION_REQUESTED" | "WAIVED";
+export type RfqType = "INITIAL" | "FINAL";
+
+
+export type VendorRfqResponseColumns = {
+ id: string;
+ vendorId: number;
+ rfqRecordId: number;
+ rfqType: RfqType;
+ overallStatus: ResponseStatus;
+ totalAttachments: number;
+ respondedCount: number;
+ pendingCount: number;
+ responseRate: number;
+ completionRate: number;
+ requestedAt: Date;
+ lastRespondedAt: Date | null;
+};
+
+// 검색 파라미터 캐시 설정
+export const searchParamsVendorResponseCache = createSearchParamsCache({
+ flags: parseAsArrayOf(z.enum(["advancedTable", "floatingBar"])).withDefault([]),
+ page: parseAsInteger.withDefault(1),
+ perPage: parseAsInteger.withDefault(10),
+ sort: getSortingStateParser<VendorRfqResponseColumns>().withDefault([
+ { id: "requestedAt", desc: true },
+ ]),
+
+ // 고급 필터
+ filters: getFiltersStateParser().withDefault([]),
+ joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"),
+
+ // 기본 필터
+ basicFilters: getFiltersStateParser().withDefault([]),
+ basicJoinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"),
+
+ // 검색 및 필터
+ search: parseAsString.withDefault(""),
+ rfqType: parseAsStringEnum(["INITIAL", "FINAL", "ALL"]).withDefault("ALL"),
+ responseStatus: parseAsStringEnum(["NOT_RESPONDED", "RESPONDED", "REVISION_REQUESTED", "WAIVED", "ALL"]).withDefault("ALL"),
+
+ // 날짜 범위
+ from: parseAsString.withDefault(""),
+ to: parseAsString.withDefault(""),
+});
+
+export type GetVendorResponsesSchema = Awaited<ReturnType<typeof searchParamsVendorResponseCache.parse>>;
+
+// vendorId + rfqRecordId로 그룹핑된 응답 요약 타입
+export type VendorRfqResponseSummary = {
+ id: string; // vendorId + rfqRecordId + rfqType 조합으로 생성된 고유 ID
+ vendorId: number;
+ rfqRecordId: number;
+ rfqType: RfqType;
+
+ // RFQ 정보
+ rfq: {
+ id: number;
+ rfqCode: string | null;
+ description: string | null;
+ status: string;
+ dueDate: Date;
+ } | null;
+
+ // 벤더 정보
+ vendor: {
+ id: number;
+ vendorCode: string;
+ vendorName: string;
+ country: string | null;
+ businessSize: string | null;
+ } | null;
+
+ // 응답 통계
+ totalAttachments: number;
+ respondedCount: number;
+ pendingCount: number;
+ revisionRequestedCount: number;
+ waivedCount: number;
+ responseRate: number;
+ completionRate: number;
+ overallStatus: ResponseStatus; // 전체적인 상태
+
+ // 날짜 정보
+ requestedAt: Date;
+ lastRespondedAt: Date | null;
+
+ // 기타
+ hasComments: boolean;
+};
+
+
+// 수정 요청 스키마
+export const requestRevisionSchema = z.object({
+ responseId: z.number().positive(),
+ revisionReason: z.string().min(10, "수정 요청 사유를 최소 10자 이상 입력해주세요").max(500),
+});
+
+// 수정 요청 결과 타입
+export type RequestRevisionResult = {
+ success: boolean;
+ message: string;
+ error?: string;
+}; \ No newline at end of file