summaryrefslogtreecommitdiff
path: root/lib/docu-list-rule/document-class/service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/docu-list-rule/document-class/service.ts')
-rw-r--r--lib/docu-list-rule/document-class/service.ts462
1 files changed, 462 insertions, 0 deletions
diff --git a/lib/docu-list-rule/document-class/service.ts b/lib/docu-list-rule/document-class/service.ts
new file mode 100644
index 00000000..04dfa50e
--- /dev/null
+++ b/lib/docu-list-rule/document-class/service.ts
@@ -0,0 +1,462 @@
+"use server"
+
+import { revalidatePath } from "next/cache"
+import db from "@/db/db"
+import { documentClasses, documentClassOptions, codeGroups } from "@/db/schema/docu-list-rule"
+import { eq, desc, asc, sql } from "drizzle-orm"
+
+// Document Class 목록 조회 (A Class, B Class 등)
+export async function getDocumentClassCodeGroups(input: {
+ page: number
+ perPage: number
+ search?: string
+ sort?: Array<{ id: string; desc: boolean }>
+ filters?: Array<{ id: string; value: string }>
+ joinOperator?: "and" | "or"
+ flags?: string[]
+ classId?: string
+ description?: string
+ isActive?: string
+}) {
+ try {
+ const { page, perPage, sort, search } = input
+ const offset = (page - 1) * perPage
+
+ // 기본 조건
+ let whereConditions = sql`${documentClasses.isActive} = true`
+
+ // 검색 조건
+ if (search) {
+ const searchTerm = `%${search}%`
+ whereConditions = sql`${whereConditions} AND (
+ ${documentClasses.code} ILIKE ${searchTerm} OR
+ ${documentClasses.value} ILIKE ${searchTerm} OR
+ ${documentClasses.description} ILIKE ${searchTerm}
+ )`
+ }
+
+ // 정렬
+ let orderBy = sql`${documentClasses.createdAt} DESC`
+ if (sort && sort.length > 0) {
+ const sortField = sort[0]
+ const direction = sortField.desc ? sql`DESC` : sql`ASC`
+
+ switch (sortField.id) {
+ case "code":
+ orderBy = sql`${documentClasses.code} ${direction}`
+ break
+ case "value":
+ orderBy = sql`${documentClasses.value} ${direction}`
+ break
+ case "description":
+ orderBy = sql`${documentClasses.description} ${direction}`
+ break
+ case "createdAt":
+ orderBy = sql`${documentClasses.createdAt} ${direction}`
+ break
+ default:
+ orderBy = sql`${documentClasses.createdAt} DESC`
+ }
+ }
+
+ // 데이터 조회
+ const data = await db
+ .select({
+ id: documentClasses.id,
+ code: documentClasses.code,
+ value: documentClasses.value,
+ description: documentClasses.description,
+ isActive: documentClasses.isActive,
+ createdAt: documentClasses.createdAt,
+ updatedAt: documentClasses.updatedAt,
+ })
+ .from(documentClasses)
+ .where(whereConditions)
+ .orderBy(orderBy)
+ .limit(perPage)
+ .offset(offset)
+
+ // 총 개수 조회
+ const [{ count: total }] = await db
+ .select({ count: sql`count(*)` })
+ .from(documentClasses)
+ .where(whereConditions)
+
+ const pageCount = Math.ceil(Number(total) / perPage)
+
+ return {
+ success: true,
+ data,
+ pageCount,
+ }
+ } catch (error) {
+ console.error("Error fetching document classes:", error)
+ return {
+ success: false,
+ error: "Failed to fetch document classes",
+ data: [],
+ pageCount: 0,
+ }
+ }
+}
+
+// Document Class 생성
+export async function createDocumentClassCodeGroup(input: {
+ value: string
+ description?: string
+}) {
+ try {
+ // Value 자동 변환: "A", "AB", "A Class", "A CLASS" 등을 "A Class", "AB Class" 형태로 변환
+ const formatValue = (value: string): string => {
+ // 공백 제거 및 대소문자 정규화
+ const cleaned = value.trim().toLowerCase()
+
+ // "class"가 포함되어 있으면 제거
+ const withoutClass = cleaned.replace(/\s*class\s*/g, '')
+
+ // 알파벳과 숫자만 추출
+ const letters = withoutClass.replace(/[^a-z0-9]/g, '')
+
+ if (letters.length === 0) {
+ return value.trim() // 변환할 수 없으면 원본 반환
+ }
+
+ // 첫 글자를 대문자로 변환하고 "Class" 추가
+ return letters.charAt(0).toUpperCase() + letters.slice(1) + " Class"
+ }
+
+ const formattedValue = formatValue(input.value)
+
+ // 자동으로 code 생성 (예: "DOC_CLASS_001", "DOC_CLASS_002" 등)
+ const existingClasses = await db
+ .select({ code: documentClasses.code })
+ .from(documentClasses)
+ .orderBy(desc(documentClasses.code))
+
+ let newCode = "DOC_CLASS_001"
+ if (existingClasses.length > 0) {
+ const lastClass = existingClasses[0]
+ if (lastClass.code) {
+ const lastNumber = parseInt(lastClass.code.replace("DOC_CLASS_", "")) || 0
+ newCode = `DOC_CLASS_${String(lastNumber + 1).padStart(3, '0')}`
+ }
+ }
+
+ // Code Group이 존재하는지 확인
+ const existingCodeGroup = await db
+ .select({ id: codeGroups.id })
+ .from(codeGroups)
+ .where(eq(codeGroups.groupId, 'DOC_CLASS'))
+ .limit(1)
+
+ let codeGroupId: number | null = null
+
+ if (existingCodeGroup.length === 0) {
+ // Code Group이 없으면 자동으로 생성
+ const [newCodeGroup] = await db
+ .insert(codeGroups)
+ .values({
+ groupId: 'DOC_CLASS',
+ description: 'Document Class',
+ codeFormat: 'DOC_CLASS_###',
+ expressions: '^DOC_CLASS_\\d{3}$',
+ controlType: 'Combobox',
+ isActive: true,
+ })
+ .returning({ id: codeGroups.id })
+
+ codeGroupId = newCodeGroup.id
+ } else {
+ codeGroupId = existingCodeGroup[0].id
+ }
+
+ const [newDocumentClass] = await db
+ .insert(documentClasses)
+ .values({
+ code: newCode,
+ value: formattedValue,
+ description: input.description || "",
+ codeGroupId: codeGroupId,
+ isActive: true,
+ })
+ .returning({ id: documentClasses.id })
+
+ revalidatePath("/evcp/docu-list-rule/document-class")
+ revalidatePath("/evcp/docu-list-rule/code-groups")
+
+ return {
+ success: true,
+ data: newDocumentClass,
+ message: "Document Class created successfully"
+ }
+ } catch (error) {
+ console.error("Error creating document class:", error)
+ return {
+ success: false,
+ error: "Failed to create document class"
+ }
+ }
+}
+
+// Document Class 수정
+export async function updateDocumentClassCodeGroup(input: {
+ id: number
+ value: string
+ description?: string
+}) {
+ try {
+ // Value 자동 변환: "A", "AB", "A Class", "A CLASS" 등을 "A Class", "AB Class" 형태로 변환
+ const formatValue = (value: string): string => {
+ // 공백 제거 및 대소문자 정규화
+ const cleaned = value.trim().toLowerCase()
+
+ // "class"가 포함되어 있으면 제거
+ const withoutClass = cleaned.replace(/\s*class\s*/g, '')
+
+ // 알파벳과 숫자만 추출
+ const letters = withoutClass.replace(/[^a-z0-9]/g, '')
+
+ if (letters.length === 0) {
+ return value.trim() // 변환할 수 없으면 원본 반환
+ }
+
+ // 첫 글자를 대문자로 변환하고 "Class" 추가
+ return letters.charAt(0).toUpperCase() + letters.slice(1) + " Class"
+ }
+
+ const formattedValue = formatValue(input.value)
+
+ const [updatedDocumentClass] = await db
+ .update(documentClasses)
+ .set({
+ value: formattedValue,
+ description: input.description || "",
+ updatedAt: new Date(),
+ })
+ .where(eq(documentClasses.id, input.id))
+ .returning({ id: documentClasses.id })
+
+ revalidatePath("/evcp/docu-list-rule/document-class")
+
+ return {
+ success: true,
+ data: updatedDocumentClass,
+ message: "Document Class updated successfully"
+ }
+ } catch (error) {
+ console.error("Error updating document class:", error)
+ return {
+ success: false,
+ error: "Failed to update document class"
+ }
+ }
+}
+
+// Document Class 삭제
+export async function deleteDocumentClassCodeGroup(id: number) {
+ try {
+ // 삭제할 Document Class의 codeGroupId 확인
+ const documentClassToDelete = await db
+ .select({ codeGroupId: documentClasses.codeGroupId })
+ .from(documentClasses)
+ .where(eq(documentClasses.id, id))
+ .limit(1)
+
+ const [deletedDocumentClass] = await db
+ .delete(documentClasses)
+ .where(eq(documentClasses.id, id))
+ .returning({ id: documentClasses.id })
+
+ // 같은 codeGroupId를 가진 다른 Document Class가 있는지 확인
+ if (documentClassToDelete.length > 0 && documentClassToDelete[0].codeGroupId) {
+ const remainingClasses = await db
+ .select({ id: documentClasses.id })
+ .from(documentClasses)
+ .where(eq(documentClasses.codeGroupId, documentClassToDelete[0].codeGroupId))
+ .limit(1)
+
+ // 더 이상 Document Class가 없으면 Code Group도 삭제
+ if (remainingClasses.length === 0) {
+ await db
+ .delete(codeGroups)
+ .where(eq(codeGroups.id, documentClassToDelete[0].codeGroupId))
+ }
+ }
+
+ revalidatePath("/evcp/docu-list-rule/document-class")
+ revalidatePath("/evcp/docu-list-rule/code-groups")
+
+ return {
+ success: true,
+ data: deletedDocumentClass,
+ message: "Document Class deleted successfully"
+ }
+ } catch (error) {
+ console.error("Error deleting document class:", error)
+ return {
+ success: false,
+ error: "Failed to delete document class"
+ }
+ }
+}
+
+// Document Class 옵션 목록 조회
+export async function getDocumentClassSubOptions(documentClassId: number) {
+ try {
+ const data = await db
+ .select({
+ id: documentClassOptions.id,
+ documentClassId: documentClassOptions.documentClassId,
+ optionValue: documentClassOptions.optionValue,
+ optionCode: documentClassOptions.optionCode,
+ sortOrder: documentClassOptions.sortOrder,
+ isActive: documentClassOptions.isActive,
+ createdAt: documentClassOptions.createdAt,
+ updatedAt: documentClassOptions.updatedAt,
+ })
+ .from(documentClassOptions)
+ .where(eq(documentClassOptions.documentClassId, documentClassId))
+ .orderBy(asc(documentClassOptions.sortOrder), asc(documentClassOptions.optionValue))
+
+ return {
+ success: true,
+ data,
+ }
+ } catch (error) {
+ console.error("Error fetching document class options:", error)
+ return {
+ success: false,
+ error: "Failed to fetch document class options",
+ data: [],
+ }
+ }
+}
+
+// Document Class 옵션 생성
+export async function createDocumentClassOptionItem(input: {
+ documentClassId: number
+ optionValue: string
+}) {
+ try {
+ // Document Class 정보 조회하여 Value 가져오기
+ const documentClass = await db
+ .select({ value: documentClasses.value })
+ .from(documentClasses)
+ .where(eq(documentClasses.id, input.documentClassId))
+ .limit(1)
+
+ if (!documentClass.length) {
+ return {
+ success: false,
+ error: "Document Class not found"
+ }
+ }
+
+ // Value에서 클래스명 추출 (예: "A Class" → "A")
+ const classValue = documentClass[0].value
+ const className = classValue.split(' ')[0] // "A Class"에서 "A" 추출
+
+ // 자동으로 optionCode 생성 (예: "A_OP_01", "A_OP_02" 등)
+ const existingOptions = await db
+ .select({ optionCode: documentClassOptions.optionCode })
+ .from(documentClassOptions)
+ .where(eq(documentClassOptions.documentClassId, input.documentClassId))
+ .orderBy(desc(documentClassOptions.optionCode))
+
+ let newOptionCode = `${className}_OP_01`
+ if (existingOptions.length > 0) {
+ const lastOption = existingOptions[0]
+ if (lastOption.optionCode) {
+ // "A_OP_01" 형태에서 숫자 추출
+ const match = lastOption.optionCode.match(/_OP_(\d+)$/)
+ if (match) {
+ const lastNumber = parseInt(match[1]) || 0
+ newOptionCode = `${className}_OP_${String(lastNumber + 1).padStart(2, '0')}`
+ } else {
+ // 기존 형식이 다른 경우 01부터 시작
+ newOptionCode = `${className}_OP_01`
+ }
+ }
+ }
+
+ const [newOption] = await db
+ .insert(documentClassOptions)
+ .values({
+ documentClassId: input.documentClassId,
+ optionValue: input.optionValue,
+ optionCode: newOptionCode,
+ sortOrder: 0,
+ isActive: true,
+ })
+ .returning({ id: documentClassOptions.id })
+
+ revalidatePath("/evcp/docu-list-rule/document-class")
+
+ return {
+ success: true,
+ data: newOption,
+ message: "Document Class option created successfully"
+ }
+ } catch (error) {
+ console.error("Error creating document class option:", error)
+ return {
+ success: false,
+ error: "Failed to create document class option"
+ }
+ }
+}
+
+// Document Class 옵션 수정
+export async function updateDocumentClassOption(input: {
+ id: number
+ optionValue: string
+}) {
+ try {
+ const [updatedOption] = await db
+ .update(documentClassOptions)
+ .set({
+ optionValue: input.optionValue,
+ updatedAt: new Date(),
+ })
+ .where(eq(documentClassOptions.id, input.id))
+ .returning({ id: documentClassOptions.id })
+
+ revalidatePath("/evcp/docu-list-rule/document-class")
+
+ return {
+ success: true,
+ data: updatedOption,
+ message: "Document Class option updated successfully"
+ }
+ } catch (error) {
+ console.error("Error updating document class option:", error)
+ return {
+ success: false,
+ error: "Failed to update document class option"
+ }
+ }
+}
+
+// Document Class 옵션 삭제
+export async function deleteDocumentClassOption(id: number) {
+ try {
+ const [deletedOption] = await db
+ .delete(documentClassOptions)
+ .where(eq(documentClassOptions.id, id))
+ .returning({ id: documentClassOptions.id })
+
+ revalidatePath("/evcp/docu-list-rule/document-class")
+
+ return {
+ success: true,
+ data: deletedOption,
+ message: "Document Class option deleted successfully"
+ }
+ } catch (error) {
+ console.error("Error deleting document class option:", error)
+ return {
+ success: false,
+ error: "Failed to delete document class option"
+ }
+ }
+} \ No newline at end of file