diff options
| author | 0-Zz-ang <s1998319@gmail.com> | 2025-08-08 17:15:22 +0900 |
|---|---|---|
| committer | 0-Zz-ang <s1998319@gmail.com> | 2025-08-08 17:15:22 +0900 |
| commit | 7cdedf2cf8e807eeea9134888dc9bd1586978ea8 (patch) | |
| tree | e6b2eefda385ecbb6ae17494b9e05ccd8985e24d | |
| parent | a6335760fc8e56d192f002eb0c3f26d1210d07a2 (diff) | |
(박서영)combo box 옵션 및 number type config Add에러 수정
8 files changed, 118 insertions, 31 deletions
diff --git a/db/migrations/0262_remove_project_id_from_combo_box_settings.sql b/db/migrations/0262_remove_project_id_from_combo_box_settings.sql new file mode 100644 index 00000000..804b471a --- /dev/null +++ b/db/migrations/0262_remove_project_id_from_combo_box_settings.sql @@ -0,0 +1,14 @@ +-- Remove projectId column and related constraints from combo_box_settings table +-- Since codeGroupId already references codeGroups which has projectId, this is redundant + +-- First, drop the unique constraint that includes projectId +DROP INDEX IF EXISTS "unique_project_code_group_code"; + +-- Then drop the foreign key constraint for projectId +ALTER TABLE "combo_box_settings" DROP CONSTRAINT IF EXISTS "combo_box_settings_project_id_projects_id_fk"; + +-- Finally, drop the projectId column +ALTER TABLE "combo_box_settings" DROP COLUMN IF EXISTS "project_id"; + +-- Add a new unique constraint without projectId (since codeGroupId + code should be unique within a code group) +ALTER TABLE "combo_box_settings" ADD CONSTRAINT "unique_code_group_code" UNIQUE ("code_group_id", "code");
\ No newline at end of file diff --git a/db/migrations/0263_remove_project_id_from_document_number_type_configs.sql b/db/migrations/0263_remove_project_id_from_document_number_type_configs.sql new file mode 100644 index 00000000..27522555 --- /dev/null +++ b/db/migrations/0263_remove_project_id_from_document_number_type_configs.sql @@ -0,0 +1,14 @@ +-- Remove projectId column and related constraints from document_number_type_configs table +-- Since documentNumberTypeId already references documentNumberTypes which has projectId, this is redundant + +-- First, drop the unique constraint that includes projectId +DROP INDEX IF EXISTS "unique_project_number_type_sdq"; + +-- Then drop the foreign key constraint for projectId +ALTER TABLE "document_number_type_configs" DROP CONSTRAINT IF EXISTS "document_number_type_configs_project_id_projects_id_fk"; + +-- Finally, drop the projectId column +ALTER TABLE "document_number_type_configs" DROP COLUMN IF EXISTS "project_id"; + +-- Add a new unique constraint without projectId (since documentNumberTypeId + sdq should be unique within a document number type) +ALTER TABLE "document_number_type_configs" ADD CONSTRAINT "unique_document_number_type_sdq" UNIQUE ("document_number_type_id", "sdq");
\ No newline at end of file diff --git a/db/migrations/0264_fix_duplicate_document_number_type_configs.sql b/db/migrations/0264_fix_duplicate_document_number_type_configs.sql new file mode 100644 index 00000000..45269a5e --- /dev/null +++ b/db/migrations/0264_fix_duplicate_document_number_type_configs.sql @@ -0,0 +1,16 @@ +-- Fix duplicate document_number_type_configs data before applying unique constraint +-- Remove duplicates keeping only the most recent one for each document_number_type_id + sdq combination + +-- First, identify and remove duplicates +DELETE FROM "document_number_type_configs" +WHERE id NOT IN ( + SELECT MAX(id) + FROM "document_number_type_configs" + GROUP BY "document_number_type_id", "sdq" +); + +-- Now we can safely apply the unique constraint +-- (This should already be applied by the previous migration, but just in case) +ALTER TABLE "document_number_type_configs" +ADD CONSTRAINT "unique_document_number_type_sdq" +UNIQUE ("document_number_type_id", "sdq");
\ No newline at end of file diff --git a/db/schema/docu-list-rule.ts b/db/schema/docu-list-rule.ts index ec20ab7a..505b1c0d 100644 --- a/db/schema/docu-list-rule.ts +++ b/db/schema/docu-list-rule.ts @@ -72,7 +72,6 @@ export const documentClassOptions = pgTable("document_class_options_new", { // ===== ComboBox Settings 테이블 ===== export const comboBoxSettings = pgTable("combo_box_settings", { id: serial("id").primaryKey(), - projectId: integer("project_id").notNull().references(() => projects.id), // 프로젝트 ID 참조 codeGroupId: integer("code_group_id").notNull().references(() => codeGroups.id), // Code Group과 연결 code: varchar("code", { length: 50 }).notNull(), // CODE (예: 100, 201, 202) description: varchar("description", { length: 200 }).notNull(), // Description (예: General, Feed Gas Reveive) @@ -81,9 +80,8 @@ export const comboBoxSettings = pgTable("combo_box_settings", { updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(), }, (table) => { return { - // 같은 프로젝트 내에서 codeGroupId와 code 조합은 유니크해야 함 - uniqueProjectCodeGroupCode: unique("unique_project_code_group_code").on( - table.projectId, + // 같은 codeGroupId 내에서 code는 유니크해야 함 + uniqueCodeGroupCode: unique("unique_code_group_code").on( table.codeGroupId, table.code ), @@ -112,7 +110,6 @@ export const documentNumberTypes = pgTable("document_number_types", { // ===== Document Number Type Configs 테이블 ===== export const documentNumberTypeConfigs = pgTable("document_number_type_configs", { id: serial("id").primaryKey(), - projectId: integer("project_id").notNull().references(() => projects.id), // 프로젝트 ID 참조 documentNumberTypeId: integer("document_number_type_id").notNull().references(() => documentNumberTypes.id), codeGroupId: integer("code_group_id").references(() => codeGroups.id), // Code Group 참조 sdq: integer("sdq").notNull(), // 순서 번호 (1, 2, 3, 4, 5, 6) @@ -123,9 +120,8 @@ export const documentNumberTypeConfigs = pgTable("document_number_type_configs", updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(), }, (table) => { return { - // 같은 프로젝트 내에서 documentNumberTypeId와 sdq 조합은 유니크해야 함 - uniqueProjectNumberTypeSdq: unique("unique_project_number_type_sdq").on( - table.projectId, + // 같은 documentNumberTypeId 내에서 sdq는 유니크해야 함 + uniqueDocumentNumberTypeSdq: unique("unique_document_number_type_sdq").on( table.documentNumberTypeId, table.sdq ), @@ -138,9 +134,7 @@ export const documentNumberTypeConfigs = pgTable("document_number_type_configs", export const projectsRelations = relations(projects, ({ many }) => ({ codeGroups: many(codeGroups), documentClasses: many(documentClasses), - comboBoxSettings: many(comboBoxSettings), documentNumberTypes: many(documentNumberTypes), - documentNumberTypeConfigs: many(documentNumberTypeConfigs), })) // Code Groups 관계 @@ -177,10 +171,6 @@ export const documentClassOptionsRelations = relations(documentClassOptions, ({ // ComboBox Settings 관계 export const comboBoxSettingsRelations = relations(comboBoxSettings, ({ one }) => ({ - project: one(projects, { - fields: [comboBoxSettings.projectId], - references: [projects.id], - }), codeGroup: one(codeGroups, { fields: [comboBoxSettings.codeGroupId], references: [codeGroups.id], @@ -198,10 +188,6 @@ export const documentNumberTypesRelations = relations(documentNumberTypes, ({ ma // Document Number Type Configs 관계 export const documentNumberTypeConfigsRelations = relations(documentNumberTypeConfigs, ({ one }) => ({ - project: one(projects, { - fields: [documentNumberTypeConfigs.projectId], - references: [projects.id], - }), documentNumberType: one(documentNumberTypes, { fields: [documentNumberTypeConfigs.documentNumberTypeId], references: [documentNumberTypes.id], diff --git a/lib/docu-list-rule/combo-box-settings/service.ts b/lib/docu-list-rule/combo-box-settings/service.ts index 96daefe4..7a003327 100644 --- a/lib/docu-list-rule/combo-box-settings/service.ts +++ b/lib/docu-list-rule/combo-box-settings/service.ts @@ -4,7 +4,7 @@ import { revalidatePath } from "next/cache" import db from "@/db/db" import { codeGroups, comboBoxSettings } from "@/db/schema/docu-list-rule" import { projects } from "@/db/schema/projects" -import { eq, sql, count } from "drizzle-orm" +import { eq, sql } from "drizzle-orm" import { unstable_noStore } from "next/cache" // Control Type이 combobox인 Code Groups 목록 조회 @@ -153,7 +153,7 @@ export async function getComboBoxOptions(codeGroupId: number, input?: { const { page = 1, perPage = 10, sort, search } = input || {} const offset = (page - 1) * perPage - // 기본 조건: codeGroupId + // 기본 조건: codeGroupId만으로 조회 (projectId는 codeGroups를 통해 간접 참조) let whereConditions = eq(comboBoxSettings.codeGroupId, codeGroupId) // 검색 조건 @@ -195,12 +195,12 @@ export async function getComboBoxOptions(codeGroupId: number, input?: { remark: comboBoxSettings.remark, createdAt: comboBoxSettings.createdAt, updatedAt: comboBoxSettings.updatedAt, - projectId: comboBoxSettings.projectId, projectCode: projects.code, projectName: projects.name, }) .from(comboBoxSettings) - .leftJoin(projects, eq(comboBoxSettings.projectId, projects.id)) + .leftJoin(codeGroups, eq(comboBoxSettings.codeGroupId, codeGroups.id)) + .leftJoin(projects, eq(codeGroups.projectId, projects.id)) .where(whereConditions) .orderBy(orderBy) .limit(perPage) @@ -210,12 +210,14 @@ export async function getComboBoxOptions(codeGroupId: number, input?: { const totalCountResult = await db .select({ count: sql<number>`count(*)` }) .from(comboBoxSettings) - .leftJoin(projects, eq(comboBoxSettings.projectId, projects.id)) + .leftJoin(codeGroups, eq(comboBoxSettings.codeGroupId, codeGroups.id)) + .leftJoin(projects, eq(codeGroups.projectId, projects.id)) .where(whereConditions) const totalCount = totalCountResult[0]?.count || 0 return { + success: true, data, totalCount, pageCount: Math.ceil(totalCount / perPage), @@ -223,9 +225,11 @@ export async function getComboBoxOptions(codeGroupId: number, input?: { } catch (error) { console.error("Error fetching combo box options:", error) return { + success: false, data: [], totalCount: 0, pageCount: 0, + error: "Failed to fetch combo box options" } } } @@ -240,7 +244,9 @@ export async function createComboBoxOption(input: { try { // 해당 Code Group의 정보 가져오기 const codeGroup = await db - .select({ description: codeGroups.description }) + .select({ + description: codeGroups.description + }) .from(codeGroups) .where(eq(codeGroups.id, input.codeGroupId)) .limit(1) diff --git a/lib/docu-list-rule/combo-box-settings/table/combo-box-options-add-dialog.tsx b/lib/docu-list-rule/combo-box-settings/table/combo-box-options-add-dialog.tsx index a0535b43..9a3f8a86 100644 --- a/lib/docu-list-rule/combo-box-settings/table/combo-box-options-add-dialog.tsx +++ b/lib/docu-list-rule/combo-box-settings/table/combo-box-options-add-dialog.tsx @@ -59,6 +59,7 @@ export function ComboBoxOptionsAddDialog({ codeGroupId, onSuccess }: ComboBoxOpt const handleCodeChange = (value: string) => { form.setValue("code", value) form.setValue("description", value) // 코드값을 description에도 자동 설정 + form.trigger() // 폼 유효성 검사 트리거 } const handleSubmit = (data: CreateOptionSchema) => { @@ -155,7 +156,10 @@ export function ComboBoxOptionsAddDialog({ codeGroupId, onSuccess }: ComboBoxOpt <Button type="button" variant="outline" onClick={handleCancel}> 취소 </Button> - <Button type="submit" disabled={isPending || !form.formState.isValid}> + <Button + type="submit" + disabled={isPending || !form.formState.isValid || !form.watch("code")} + > 추가 </Button> </DialogFooter> diff --git a/lib/docu-list-rule/combo-box-settings/table/combo-box-options-detail-sheet.tsx b/lib/docu-list-rule/combo-box-settings/table/combo-box-options-detail-sheet.tsx index 22806ae8..286acfbf 100644 --- a/lib/docu-list-rule/combo-box-settings/table/combo-box-options-detail-sheet.tsx +++ b/lib/docu-list-rule/combo-box-settings/table/combo-box-options-detail-sheet.tsx @@ -61,16 +61,20 @@ export function ComboBoxOptionsDetailSheet({ filters: [], joinOperator: "and", }) + console.log("getComboBoxOptions result:", result) if (result.success && result.data) { // isActive 필드가 없는 경우 기본값 true로 설정 const optionsWithIsActive = result.data.map(option => ({ ...option, isActive: (option as any).isActive ?? true })) + console.log("Processed data:", optionsWithIsActive) setRawData({ data: optionsWithIsActive, pageCount: result.pageCount || 1 }) + } else { + console.log("No data or error:", result) } } catch (error) { console.error("Error refreshing data:", error) diff --git a/lib/docu-list-rule/number-type-configs/service.ts b/lib/docu-list-rule/number-type-configs/service.ts index ef25aecb..14cfc2f0 100644 --- a/lib/docu-list-rule/number-type-configs/service.ts +++ b/lib/docu-list-rule/number-type-configs/service.ts @@ -3,7 +3,7 @@ import { revalidatePath } from "next/cache" import db from "@/db/db" import { unstable_noStore } from "next/cache" -import { documentNumberTypeConfigs, codeGroups } from "@/db/schema/docu-list-rule" +import { documentNumberTypeConfigs, codeGroups, documentNumberTypes } from "@/db/schema/docu-list-rule" import { projects } from "@/db/schema/projects" import { asc, eq, sql, and } from "drizzle-orm" import { GetNumberTypeConfigsSchema } from "./validation" @@ -122,13 +122,12 @@ export async function getNumberTypeConfigs(input: GetNumberTypeConfigsSchema) { updatedAt: documentNumberTypeConfigs.updatedAt, codeGroupName: codeGroups.description, codeGroupControlType: codeGroups.controlType, - projectId: documentNumberTypeConfigs.projectId, projectCode: projects.code, projectName: projects.name, }) .from(documentNumberTypeConfigs) .leftJoin(codeGroups, eq(documentNumberTypeConfigs.codeGroupId, codeGroups.id)) - .leftJoin(projects, eq(documentNumberTypeConfigs.projectId, projects.id)) + .leftJoin(projects, eq(codeGroups.projectId, projects.id)) .where(whereConditions) .orderBy(orderBy) .limit(perPage) @@ -139,7 +138,7 @@ export async function getNumberTypeConfigs(input: GetNumberTypeConfigsSchema) { .select({ count: sql<number>`count(*)` }) .from(documentNumberTypeConfigs) .leftJoin(codeGroups, eq(documentNumberTypeConfigs.codeGroupId, codeGroups.id)) - .leftJoin(projects, eq(documentNumberTypeConfigs.projectId, projects.id)) + .leftJoin(projects, eq(codeGroups.projectId, projects.id)) .where(whereConditions) const totalCount = totalCountResult[0]?.count || 0 @@ -166,9 +165,24 @@ export async function createNumberTypeConfig(input: { sdq: number description?: string remark?: string - projectId: number }) { try { + // 중복 체크: 같은 documentNumberTypeId와 sdq 조합이 이미 존재하는지 확인 + const existingConfig = await db + .select({ id: documentNumberTypeConfigs.id }) + .from(documentNumberTypeConfigs) + .where( + sql`${documentNumberTypeConfigs.documentNumberTypeId} = ${input.documentNumberTypeId} AND ${documentNumberTypeConfigs.sdq} = ${input.sdq}` + ) + .limit(1) + + if (existingConfig.length > 0) { + return { + success: false, + error: "이미 존재하는 순서 번호입니다." + } + } + const [result] = await db .insert(documentNumberTypeConfigs) .values({ @@ -177,7 +191,6 @@ export async function createNumberTypeConfig(input: { sdq: input.sdq, description: input.description, remark: input.remark, - projectId: input.projectId, }) .returning({ id: documentNumberTypeConfigs.id }) @@ -206,6 +219,36 @@ export async function updateNumberTypeConfig(input: { remark?: string }) { try { + // 현재 수정 중인 항목의 documentNumberTypeId 가져오기 + const currentConfig = await db + .select({ documentNumberTypeId: documentNumberTypeConfigs.documentNumberTypeId }) + .from(documentNumberTypeConfigs) + .where(eq(documentNumberTypeConfigs.id, input.id)) + .limit(1) + + if (currentConfig.length === 0) { + return { + success: false, + error: "Config not found" + } + } + + // 중복 체크: 같은 documentNumberTypeId와 sdq 조합이 이미 존재하는지 확인 (현재 수정 중인 항목 제외) + const existingConfig = await db + .select({ id: documentNumberTypeConfigs.id }) + .from(documentNumberTypeConfigs) + .where( + sql`${documentNumberTypeConfigs.documentNumberTypeId} = ${currentConfig[0].documentNumberTypeId} AND ${documentNumberTypeConfigs.sdq} = ${input.sdq} AND ${documentNumberTypeConfigs.id} != ${input.id}` + ) + .limit(1) + + if (existingConfig.length > 0) { + return { + success: false, + error: "이미 존재하는 순서 번호입니다." + } + } + const [result] = await db .update(documentNumberTypeConfigs) .set({ |
