diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-24 11:06:32 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-24 11:06:32 +0000 |
| commit | 1dc24d48e52f2e490f5603ceb02842586ecae533 (patch) | |
| tree | 8fca2c5b5b52cc10557b5ba6e55b937ae3c57cf6 /lib/basic-contract/service.ts | |
| parent | ed0d6fcc98f671280c2ccde797b50693da88152e (diff) | |
(대표님) 정기평가 피드백 반영, 설계 피드백 반영, (최겸) 기술영업 피드백 반영
Diffstat (limited to 'lib/basic-contract/service.ts')
| -rw-r--r-- | lib/basic-contract/service.ts | 230 |
1 files changed, 185 insertions, 45 deletions
diff --git a/lib/basic-contract/service.ts b/lib/basic-contract/service.ts index 87a861e1..014f32ab 100644 --- a/lib/basic-contract/service.ts +++ b/lib/basic-contract/service.ts @@ -49,8 +49,6 @@ export async function addTemplate( if (templateData instanceof FormData) {
const templateName = templateData.get("templateName") as string;
// 문자열을 숫자로 변환 (FormData의 get은 항상 string|File을 반환)
- const validityPeriodStr = templateData.get("validityPeriod") as string;
- const validityPeriod = validityPeriodStr ? parseInt(validityPeriodStr, 10) : 12; // 기본값 12개월
const status = templateData.get("status") as "ACTIVE" | "INACTIVE";
const file = templateData.get("file") as File;
@@ -63,12 +61,6 @@ export async function addTemplate( return { success: false, error: "파일은 필수입니다." };
}
- if (isNaN(validityPeriod) || validityPeriod < 1 || validityPeriod > 120) {
- return {
- success: false,
- error: "유효기간은 1~120개월 사이의 유효한 값이어야 합니다."
- };
- }
const saveResult = await saveFile({file, directory:"basicContract/template" });
if (!saveResult.success) {
@@ -79,7 +71,6 @@ export async function addTemplate( const formattedData = {
templateName,
status,
- validityPeriod, // 숫자로 변환된 유효기간
fileName: file.name,
filePath: saveResult.publicPath!
};
@@ -196,26 +187,31 @@ export async function getBasicContractTemplates( // 템플릿 생성 (서버 액션)
-export async function createBasicContractTemplate(
- input: CreateBasicContractTemplateSchema
-) {
+export async function createBasicContractTemplate(input: CreateBasicContractTemplateSchema) {
unstable_noStore();
+
try {
const newTemplate = await db.transaction(async (tx) => {
- const [newTemplate] = await insertBasicContractTemplate(tx, {
+ const [row] = await insertBasicContractTemplate(tx, {
templateName: input.templateName,
- validityPeriod: input.validityPeriod,
+ revision: 1,
+ legalReviewRequired: input.legalReviewRequired,
+ shipBuildingApplicable: input.shipBuildingApplicable,
+ windApplicable: input.windApplicable,
+ pcApplicable: input.pcApplicable,
+ nbApplicable: input.nbApplicable,
+ rcApplicable: input.rcApplicable,
+ gyApplicable: input.gyApplicable,
+ sysApplicable: input.sysApplicable,
+ infraApplicable: input.infraApplicable,
status: input.status,
fileName: input.fileName,
filePath: input.filePath,
+ // 필요하면 createdAt/updatedAt 등도 여기서
});
- return newTemplate;
+ return row;
});
- // 캐시 무효화
- revalidateTag("basic-contract-templates");
- revalidateTag("template-status-counts");
-
return { data: newTemplate, error: null };
} catch (error) {
return { data: null, error: getErrorMessage(error) };
@@ -350,6 +346,23 @@ interface UpdateTemplateParams { formData: FormData;
}
+const SCOPE_KEYS = [
+ "shipBuildingApplicable",
+ "windApplicable",
+ "pcApplicable",
+ "nbApplicable",
+ "rcApplicable",
+ "gyApplicable",
+ "sysApplicable",
+ "infraApplicable",
+] as const;
+
+function getBool(fd: FormData, key: string, defaultValue = false) {
+ const v = fd.get(key);
+ if (v === null) return defaultValue;
+ return v === "true";
+}
+
export async function updateTemplate({
id,
formData
@@ -357,51 +370,76 @@ export async function updateTemplate({ unstable_noStore();
try {
- const templateName = formData.get("templateName") as string;
- const validityPeriodStr = formData.get("validityPeriod") as string;
- const validityPeriod = validityPeriodStr ? parseInt(validityPeriodStr, 10) : 12;
- const status = formData.get("status") as "ACTIVE" | "INACTIVE";
- const file = formData.get("file") as File | null;
-
+ // 필수값
+ const templateName = formData.get("templateName") as string | null;
if (!templateName) {
return { error: "템플릿 이름은 필수입니다." };
}
- // 기본 업데이트 데이터
- const updateData: Record<string, any> = {
- templateName,
- status,
- validityPeriod,
- updatedAt: new Date(),
- };
+ // 선택/추가 필드 파싱
+ const revisionStr = formData.get("revision")?.toString() ?? "1";
+ const revision = Number(revisionStr) || 1;
+
+ const legalReviewRequired = getBool(formData, "legalReviewRequired", false);
+
+ // status는 프런트에서 ACTIVE만 넣고 있으나, 없으면 기존값 유지 or 기본값 설정
+ const status = (formData.get("status") as "ACTIVE" | "INACTIVE" | null) ?? "ACTIVE";
+ // validityPeriod가 이제 필요없다면 제거하시고, 사용한다면 파싱 그대로
+ const validityPeriodStr = formData.get("validityPeriod")?.toString();
+ const validityPeriod = validityPeriodStr ? Number(validityPeriodStr) : undefined;
+
+ // Scope booleans
+ const scopeData: Record<string, boolean> = {};
+ for (const key of SCOPE_KEYS) {
+ scopeData[key] = getBool(formData, key, false);
+ }
+
+ // 파일 처리
+ const file = formData.get("file") as File | null;
+ let fileName: string | undefined = undefined;
+ let filePath: string | undefined = undefined;
- // 파일이 있는 경우 처리
if (file) {
- const saveResult = await saveFile({file,directory:"basicContract/template"});
+ // 1) 새 파일 저장
+ const saveResult = await saveFile({ file, directory: "basicContract/template" });
if (!saveResult.success) {
return { success: false, error: saveResult.error };
}
+ fileName = file.name;
+ filePath = saveResult.publicPath;
- // 기존 파일 정보 가져오기
+ // 2) 기존 파일 삭제
const existingTemplate = await db.query.basicContractTemplates.findFirst({
- where: eq(basicContractTemplates.id, id)
+ where: eq(basicContractTemplates.id, id),
});
- // 기존 파일이 있다면 삭제
if (existingTemplate?.filePath) {
-
const deleted = await deleteFile(existingTemplate.filePath);
if (deleted) {
- console.log(`✅ 파일 삭제됨: ${existingTemplate.filePath}`);
+ console.log(`✅ 기존 파일 삭제됨: ${existingTemplate.filePath}`);
} else {
- console.log(`⚠️ 파일 삭제 실패: ${existingTemplate.filePath}`);
+ console.log(`⚠️ 기존 파일 삭제 실패: ${existingTemplate.filePath}`);
}
}
+ }
- // 업데이트 데이터에 파일 정보 추가
- updateData.fileName = file.name;
- updateData.filePath = saveResult.publicPath;
+ // 업데이트할 데이터 구성
+ const updateData: Record<string, any> = {
+ templateName,
+ revision,
+ legalReviewRequired,
+ status,
+ updatedAt: new Date(),
+ ...scopeData,
+ };
+
+ if (validityPeriod !== undefined) {
+ updateData.validityPeriod = validityPeriod;
+ }
+ if (fileName && filePath) {
+ updateData.fileName = fileName;
+ updateData.filePath = filePath;
}
// DB 업데이트
@@ -412,7 +450,7 @@ export async function updateTemplate({ .where(eq(basicContractTemplates.id, id));
});
- // 캐시 무효화 (다양한 방법 시도)
+ // 캐시 무효화
revalidateTag("basic-contract-templates");
revalidateTag("template-status-counts");
revalidateTag("templates");
@@ -423,7 +461,7 @@ export async function updateTemplate({ return {
error: error instanceof Error
? error.message
- : "템플릿 업데이트 중 오류가 발생했습니다."
+ : "템플릿 업데이트 중 오류가 발생했습니다.",
};
}
}
@@ -987,4 +1025,106 @@ export async function saveTemplateFile(templateId: number, formData: FormData) { export async function refreshTemplatePage(templateId: string) {
revalidatePath(`/evcp/basic-contract-template/${templateId}`);
revalidateTag("basic-contract-templates");
+}
+
+// 새 리비전 생성 함수
+export async function createBasicContractTemplateRevision(input: CreateRevisionSchema) {
+ unstable_noStore();
+
+ try {
+ // 기본 템플릿 존재 확인
+ const baseTemplate = await db
+ .select()
+ .from(basicContractTemplates)
+ .where(eq(basicContractTemplates.id, input.baseTemplateId))
+ .limit(1);
+
+ if (baseTemplate.length === 0) {
+ return { data: null, error: "기본 템플릿을 찾을 수 없습니다." };
+ }
+
+ // 같은 템플릿 이름에 해당 리비전이 이미 존재하는지 확인
+ const existingRevision = await db
+ .select()
+ .from(basicContractTemplates)
+ .where(
+ and(
+ eq(basicContractTemplates.templateName, input.templateName),
+ eq(basicContractTemplates.revision, input.revision)
+ )
+ )
+ .limit(1);
+
+ if (existingRevision.length > 0) {
+ return {
+ data: null,
+ error: `${input.templateName} v${input.revision} 리비전이 이미 존재합니다.`
+ };
+ }
+
+ // 새 리비전이 기존 리비전들보다 큰 번호인지 확인
+ const maxRevision = await db
+ .select({ maxRev: basicContractTemplates.revision })
+ .from(basicContractTemplates)
+ .where(eq(basicContractTemplates.templateName, input.templateName))
+ .orderBy(desc(basicContractTemplates.revision))
+ .limit(1);
+
+ if (maxRevision.length > 0 && input.revision <= maxRevision[0].maxRev) {
+ return {
+ data: null,
+ error: `새 리비전 번호는 현재 최대 리비전(v${maxRevision[0].maxRev})보다 커야 합니다.`
+ };
+ }
+
+ const newRevision = await db.transaction(async (tx) => {
+ const [row] = await insertBasicContractTemplate(tx, {
+ templateName: input.templateName,
+ revision: input.revision,
+ legalReviewRequired: input.legalReviewRequired,
+ shipBuildingApplicable: input.shipBuildingApplicable,
+ windApplicable: input.windApplicable,
+ pcApplicable: input.pcApplicable,
+ nbApplicable: input.nbApplicable,
+ rcApplicable: input.rcApplicable,
+ gyApplicable: input.gyApplicable,
+ sysApplicable: input.sysApplicable,
+ infraApplicable: input.infraApplicable,
+ status: "ACTIVE",
+ fileName: input.fileName,
+ filePath: input.filePath,
+ validityPeriod: null,
+ });
+ return row;
+ });
+
+ return { data: newRevision, error: null };
+ } catch (error) {
+ return { data: null, error: getErrorMessage(error) };
+ }
+}
+
+
+
+
+// 1) 전체 basicContractTemplates 조회
+export async function getALLBasicContractTemplates() {
+ return db
+ .select()
+ .from(basicContractTemplates)
+ .where(eq(basicContractTemplates.status,"ACTIVE"))
+ .orderBy(desc(basicContractTemplates.createdAt));
+}
+
+// 2) 등록된 templateName만 중복 없이 가져오기
+export async function getExistingTemplateNames(): Promise<string[]> {
+ const rows = await db
+ .select({
+ templateName: basicContractTemplates.templateName,
+ })
+ .from(basicContractTemplates)
+ .where(eq(basicContractTemplates.status,"ACTIVE"))
+ .groupBy(basicContractTemplates.templateName);
+
+ return rows.map((r) => r.templateName);
}
\ No newline at end of file |
