From fb276ed3db86fe4fc0c0fcd870fd3d085b034be0 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Thu, 17 Jul 2025 10:50:28 +0000 Subject: (대표님) 벤더데이터 S-EDP 변경사항 대응(seperator), 정기평가 점수오류, dim 준비 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/items-tech/service.ts | 236 ++++++++++++++++++++++++++++++---------------- 1 file changed, 157 insertions(+), 79 deletions(-) (limited to 'lib/items-tech/service.ts') diff --git a/lib/items-tech/service.ts b/lib/items-tech/service.ts index 0cc08d23..bf2684d7 100644 --- a/lib/items-tech/service.ts +++ b/lib/items-tech/service.ts @@ -13,9 +13,9 @@ import { GetShipbuildingSchema, GetOffshoreTopSchema, GetOffshoreHullSchema, Shi import { itemShipbuilding, itemOffshoreTop, itemOffshoreHull } from "@/db/schema/items"; // 타입 정의 추가 -export type ShipbuildingWorkType = '기장' | '전장' | '선실' | '배관' | '철의'; +export type ShipbuildingWorkType = '기장' | '전장' | '선실' | '배관' | '철의' | '선체'; export type OffshoreTopWorkType = 'TM' | 'TS' | 'TE' | 'TP'; -export type OffshoreHullWorkType = 'HA' | 'HE' | 'HH' | 'HM' | 'NC'; +export type OffshoreHullWorkType = 'HA' | 'HE' | 'HH' | 'HM' | 'HO' | 'HP' | 'NC'; export interface ShipbuildingItem { id: number; @@ -62,7 +62,7 @@ export async function getShipbuildingItems(input: GetShipbuildingSchema) { try { const offset = (input.page - 1) * input.perPage; - // advancedTable 모드면 filterColumns()로 where 절 구성 + // advancedTable 모드면 filterColumns()로 where 절 구성 (기존 필터) const advancedWhere = filterColumns({ table: itemShipbuilding, filters: input.filters.filter(filter => { @@ -73,6 +73,17 @@ export async function getShipbuildingItems(input: GetShipbuildingSchema) { joinOperator: input.joinOperator, }); + // 필터 시트에서 온 shipFilters 처리 + const shipFilterWhere = filterColumns({ + table: itemShipbuilding, + filters: input.shipFilters?.filter(filter => { + // enum 필드에 대한 isEmpty/isNotEmpty는 제외 + return !((filter.id === 'workType' || filter.id === 'shipTypes') && + (filter.operator === 'isEmpty' || filter.operator === 'isNotEmpty')) + }) || [], + joinOperator: input.shipJoinOperator, + }); + let globalWhere; if (input.search) { const s = `%${input.search}%`; @@ -82,7 +93,7 @@ export async function getShipbuildingItems(input: GetShipbuildingSchema) { ); } - // enum 필드에 대한 isEmpty/isNotEmpty 처리 + // enum 필드에 대한 isEmpty/isNotEmpty 처리 (기존 필터) const enumConditions = input.filters .filter(filter => (filter.id === 'workType' || filter.id === 'shipTypes') && (filter.operator === 'isEmpty' || filter.operator === 'isNotEmpty')) @@ -91,10 +102,21 @@ export async function getShipbuildingItems(input: GetShipbuildingSchema) { return filter.operator === 'isEmpty' ? sql`${column} is null` : sql`${column} is not null`; }); + // enum 필드에 대한 isEmpty/isNotEmpty 처리 (필터 시트) + const shipEnumConditions = input.shipFilters + ?.filter(filter => (filter.id === 'workType' || filter.id === 'shipTypes') && + (filter.operator === 'isEmpty' || filter.operator === 'isNotEmpty')) + .map(filter => { + const column = itemShipbuilding[filter.id]; + return filter.operator === 'isEmpty' ? sql`${column} is null` : sql`${column} is not null`; + }) || []; + const finalWhere = and( advancedWhere, + shipFilterWhere, globalWhere, - ...enumConditions + ...enumConditions, + ...shipEnumConditions ); const where = finalWhere; @@ -152,7 +174,7 @@ export async function getOffshoreTopItems(input: GetOffshoreTopSchema) { try { const offset = (input.page - 1) * input.perPage; - // advancedTable 모드면 filterColumns()로 where 절 구성 + // advancedTable 모드면 filterColumns()로 where 절 구성 (기존 필터) const advancedWhere = filterColumns({ table: itemOffshoreTop, filters: input.filters.filter(filter => { @@ -163,6 +185,17 @@ export async function getOffshoreTopItems(input: GetOffshoreTopSchema) { joinOperator: input.joinOperator, }); + // 필터 시트에서 온 topFilters 처리 + const topFilterWhere = filterColumns({ + table: itemOffshoreTop, + filters: input.topFilters?.filter(filter => { + // enum 필드에 대한 isEmpty/isNotEmpty는 제외 + return !((filter.id === 'workType') && + (filter.operator === 'isEmpty' || filter.operator === 'isNotEmpty')) + }) || [], + joinOperator: input.topJoinOperator, + }); + let globalWhere; if (input.search) { const s = `%${input.search}%`; @@ -173,7 +206,7 @@ export async function getOffshoreTopItems(input: GetOffshoreTopSchema) { ); } - // enum 필드에 대한 isEmpty/isNotEmpty 처리 + // enum 필드에 대한 isEmpty/isNotEmpty 처리 (기존 필터) const enumConditions = input.filters .filter(filter => (filter.id === 'workType') && (filter.operator === 'isEmpty' || filter.operator === 'isNotEmpty')) @@ -182,10 +215,21 @@ export async function getOffshoreTopItems(input: GetOffshoreTopSchema) { return filter.operator === 'isEmpty' ? sql`${column} is null` : sql`${column} is not null`; }); + // enum 필드에 대한 isEmpty/isNotEmpty 처리 (필터 시트) + const topEnumConditions = input.topFilters + ?.filter(filter => (filter.id === 'workType') && + (filter.operator === 'isEmpty' || filter.operator === 'isNotEmpty')) + .map(filter => { + const column = itemOffshoreTop.workType; + return filter.operator === 'isEmpty' ? sql`${column} is null` : sql`${column} is not null`; + }) || []; + const finalWhere = and( advancedWhere, + topFilterWhere, globalWhere, - ...enumConditions + ...enumConditions, + ...topEnumConditions ); const where = finalWhere; @@ -243,7 +287,7 @@ export async function getOffshoreHullItems(input: GetOffshoreHullSchema) { try { const offset = (input.page - 1) * input.perPage; - // advancedTable 모드면 filterColumns()로 where 절 구성 + // advancedTable 모드면 filterColumns()로 where 절 구성 (기존 필터) const advancedWhere = filterColumns({ table: itemOffshoreHull, filters: input.filters.filter(filter => { @@ -254,6 +298,17 @@ export async function getOffshoreHullItems(input: GetOffshoreHullSchema) { joinOperator: input.joinOperator, }); + // 필터 시트에서 온 hullFilters 처리 + const hullFilterWhere = filterColumns({ + table: itemOffshoreHull, + filters: input.hullFilters?.filter(filter => { + // enum 필드에 대한 isEmpty/isNotEmpty는 제외 + return !((filter.id === 'workType') && + (filter.operator === 'isEmpty' || filter.operator === 'isNotEmpty')) + }) || [], + joinOperator: input.hullJoinOperator, + }); + let globalWhere; if (input.search) { const s = `%${input.search}%`; @@ -264,7 +319,7 @@ export async function getOffshoreHullItems(input: GetOffshoreHullSchema) { ); } - // enum 필드에 대한 isEmpty/isNotEmpty 처리 + // enum 필드에 대한 isEmpty/isNotEmpty 처리 (기존 필터) const enumConditions = input.filters .filter(filter => (filter.id === 'workType') && (filter.operator === 'isEmpty' || filter.operator === 'isNotEmpty')) @@ -273,10 +328,21 @@ export async function getOffshoreHullItems(input: GetOffshoreHullSchema) { return filter.operator === 'isEmpty' ? sql`${column} is null` : sql`${column} is not null`; }); + // enum 필드에 대한 isEmpty/isNotEmpty 처리 (필터 시트) + const hullEnumConditions = input.hullFilters + ?.filter(filter => (filter.id === 'workType') && + (filter.operator === 'isEmpty' || filter.operator === 'isNotEmpty')) + .map(filter => { + const column = itemOffshoreHull.workType; + return filter.operator === 'isEmpty' ? sql`${column} is null` : sql`${column} is not null`; + }) || []; + const finalWhere = and( advancedWhere, + hullFilterWhere, globalWhere, - ...enumConditions + ...enumConditions, + ...hullEnumConditions ); const where = finalWhere; @@ -339,19 +405,12 @@ export async function createShipbuildingItem(input: TypedItemCreateData) { unstable_noStore() try { - if (!input.itemCode) { - return { - success: false, - message: "아이템 코드는 필수입니다", - data: null, - error: "필수 필드 누락" - } - } + // itemCode는 nullable하게 변경 const shipData = input as ShipbuildingItemCreateData; const result = await db.insert(itemShipbuilding).values({ - itemCode: input.itemCode, - workType: shipData.workType ? (shipData.workType as '기장' | '전장' | '선실' | '배관' | '철의') : '기장', + itemCode: input.itemCode || "", + workType: shipData.workType ? (shipData.workType as '기장' | '전장' | '선실' | '배관' | '철의' | '선체') : '기장', shipTypes: shipData.shipTypes || '', itemList: shipData.itemList || null, createdAt: new Date(), @@ -391,8 +450,8 @@ export async function createShipbuildingItem(input: TypedItemCreateData) { * 하나의 아이템 코드에 대해 여러 선종을 처리 (1:N 관계) */ export async function createShipbuildingImportItem(input: { - itemCode: string; - workType: '기장' | '전장' | '선실' | '배관' | '철의'; + itemCode?: string | null; + workType: '기장' | '전장' | '선실' | '배관' | '철의' | '선체'; itemList?: string | null; subItemList?: string | null; shipTypes?: string | null; @@ -400,35 +459,30 @@ export async function createShipbuildingImportItem(input: { unstable_noStore(); try { - if (!input.itemCode) { - return { - success: false, - message: "아이템 코드는 필수입니다", - data: null, - error: "필수 필드 누락" - } - } + // itemCode는 nullable하게 변경 - // 기존 아이템 및 선종 확인 - const existingItem = await db.select().from(itemShipbuilding) - .where( - and( - eq(itemShipbuilding.itemCode, input.itemCode), - eq(itemShipbuilding.shipTypes, input.shipTypes || '') - ) - ); - - if (existingItem.length > 0) { - return { - success: false, - message: "이미 존재하는 아이템 코드 및 선종입니다", - data: null, - error: "중복 키 오류" + // 기존 아이템 및 선종 확인 (itemCode가 있을 경우에만) + if (input.itemCode) { + const existingItem = await db.select().from(itemShipbuilding) + .where( + and( + eq(itemShipbuilding.itemCode, input.itemCode), + eq(itemShipbuilding.shipTypes, input.shipTypes || '') + ) + ); + + if (existingItem.length > 0) { + return { + success: false, + message: "이미 존재하는 아이템 코드 및 선종입니다", + data: null, + error: "중복 키 오류" + } } } const result = await db.insert(itemShipbuilding).values({ - itemCode: input.itemCode, + itemCode: input.itemCode || "", workType: input.workType, shipTypes: input.shipTypes || '', itemList: input.itemList || '', @@ -464,21 +518,32 @@ export async function createShipbuildingImportItem(input: { } } +/** + * 해양 TOP 아이템 생성 (중복 아이템코드 방지) + */ export async function createOffshoreTopItem(data: OffshoreTopItemCreateData) { - unstable_noStore() - + unstable_noStore(); + try { - if (!data.itemCode) { - return { - success: false, - message: "아이템 코드는 필수입니다", - data: null, - error: "필수 필드 누락" + // itemCode가 있는 경우 중복 체크 + if (data.itemCode && data.itemCode.trim() !== "") { + const existingItem = await db + .select({ id: itemOffshoreTop.id }) + .from(itemOffshoreTop) + .where(eq(itemOffshoreTop.itemCode, data.itemCode.trim())); + + if (existingItem.length > 0) { + return { + success: false, + message: "이미 존재하는 아이템 코드입니다", + data: null, + error: "중복 키 오류" + }; } } - + const result = await db.insert(itemOffshoreTop).values({ - itemCode: data.itemCode, + itemCode: data.itemCode || "", workType: data.workType, itemList: data.itemList, subItemList: data.subItemList, @@ -486,23 +551,23 @@ export async function createOffshoreTopItem(data: OffshoreTopItemCreateData) { updatedAt: new Date() }).returning(); - revalidateTag("items") + revalidateTag("items"); return { success: true, data: result[0], error: null - } + }; } catch (err) { - console.error("아이템 생성 오류:", err) - + console.error("해양 TOP 아이템 생성 오류:", err); + if (err instanceof Error && err.message.includes("unique constraint")) { return { success: false, message: "이미 존재하는 아이템 코드입니다", data: null, error: "중복 키 오류" - } + }; } return { @@ -510,25 +575,36 @@ export async function createOffshoreTopItem(data: OffshoreTopItemCreateData) { message: getErrorMessage(err), data: null, error: getErrorMessage(err) - } + }; } } +/** + * 해양 HULL 아이템 생성 (중복 아이템코드 방지) + */ export async function createOffshoreHullItem(data: OffshoreHullItemCreateData) { - unstable_noStore() - + unstable_noStore(); + try { - if (!data.itemCode) { - return { - success: false, - message: "아이템 코드는 필수입니다", - data: null, - error: "필수 필드 누락" + // itemCode가 있는 경우 중복 체크 + if (data.itemCode && data.itemCode.trim() !== "") { + const existingItem = await db + .select({ id: itemOffshoreHull.id }) + .from(itemOffshoreHull) + .where(eq(itemOffshoreHull.itemCode, data.itemCode.trim())); + + if (existingItem.length > 0) { + return { + success: false, + message: "이미 존재하는 아이템 코드입니다", + data: null, + error: "중복 키 오류" + }; } } - + const result = await db.insert(itemOffshoreHull).values({ - itemCode: data.itemCode, + itemCode: data.itemCode || "", workType: data.workType, itemList: data.itemList, subItemList: data.subItemList, @@ -536,23 +612,23 @@ export async function createOffshoreHullItem(data: OffshoreHullItemCreateData) { updatedAt: new Date() }).returning(); - revalidateTag("items") + revalidateTag("items"); return { success: true, data: result[0], error: null - } + }; } catch (err) { - console.error("아이템 생성 오류:", err) - + console.error("해양 HULL 아이템 생성 오류:", err); + if (err instanceof Error && err.message.includes("unique constraint")) { return { success: false, message: "이미 존재하는 아이템 코드입니다", data: null, error: "중복 키 오류" - } + }; } return { @@ -560,7 +636,7 @@ export async function createOffshoreHullItem(data: OffshoreHullItemCreateData) { message: getErrorMessage(err), data: null, error: getErrorMessage(err) - } + }; } } @@ -1126,6 +1202,8 @@ export async function getOffshoreHullWorkTypes() { { code: 'HE' as OffshoreHullWorkType, name: 'HE'}, { code: 'HH' as OffshoreHullWorkType, name: 'HH'}, { code: 'HM' as OffshoreHullWorkType, name: 'HM'}, + { code: 'HO' as OffshoreHullWorkType, name: 'HO'}, + { code: 'HP' as OffshoreHullWorkType, name: 'HP'}, { code: 'NC' as OffshoreHullWorkType, name: 'NC'}, ] } -- cgit v1.2.3