summaryrefslogtreecommitdiff
path: root/lib/tech-vendors/repository.ts
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-07-21 07:54:26 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-07-21 07:54:26 +0000
commit14f61e24947fb92dd71ec0a7196a6e815f8e66da (patch)
tree317c501d64662d05914330628f867467fba78132 /lib/tech-vendors/repository.ts
parent194bd4bd7e6144d5c09c5e3f5476d254234dce72 (diff)
(최겸)기술영업 RFQ 담당자 초대, 요구사항 반영
Diffstat (limited to 'lib/tech-vendors/repository.ts')
-rw-r--r--lib/tech-vendors/repository.ts851
1 files changed, 462 insertions, 389 deletions
diff --git a/lib/tech-vendors/repository.ts b/lib/tech-vendors/repository.ts
index d3c6671c..a273bf50 100644
--- a/lib/tech-vendors/repository.ts
+++ b/lib/tech-vendors/repository.ts
@@ -1,389 +1,462 @@
-// src/lib/vendors/repository.ts
-
-import { eq, inArray, count, desc } from "drizzle-orm";
-import db from '@/db/db';
-import { SQL } from "drizzle-orm";
-import { techVendors, techVendorContacts, techVendorPossibleItems, techVendorItemsView, type TechVendor, type TechVendorContact, type TechVendorItem, type TechVendorWithAttachments, techVendorAttachments } from "@/db/schema/techVendors";
-import { itemShipbuilding, itemOffshoreTop, itemOffshoreHull } from "@/db/schema/items";
-
-export type NewTechVendorContact = typeof techVendorContacts.$inferInsert
-export type NewTechVendorItem = typeof techVendorPossibleItems.$inferInsert
-
-type PaginationParams = {
- offset: number;
- limit: number;
-};
-
-// 메인 벤더 목록 조회 (첨부파일 정보 포함)
-export async function selectTechVendorsWithAttachments(
- tx: any,
- params: {
- where?: SQL<unknown>;
- orderBy?: SQL<unknown>[];
- } & PaginationParams
-) {
- const query = tx
- .select({
- id: techVendors.id,
- vendorName: techVendors.vendorName,
- vendorCode: techVendors.vendorCode,
- taxId: techVendors.taxId,
- address: techVendors.address,
- country: techVendors.country,
- phone: techVendors.phone,
- email: techVendors.email,
- website: techVendors.website,
- status: techVendors.status,
- techVendorType: techVendors.techVendorType,
- representativeName: techVendors.representativeName,
- representativeEmail: techVendors.representativeEmail,
- representativePhone: techVendors.representativePhone,
- representativeBirth: techVendors.representativeBirth,
- countryEng: techVendors.countryEng,
- countryFab: techVendors.countryFab,
- agentName: techVendors.agentName,
- agentPhone: techVendors.agentPhone,
- agentEmail: techVendors.agentEmail,
- items: techVendors.items,
- createdAt: techVendors.createdAt,
- updatedAt: techVendors.updatedAt,
- })
- .from(techVendors);
-
- // where 조건이 있는 경우
- if (params.where) {
- query.where(params.where);
- }
-
- // 정렬 조건이 있는 경우
- if (params.orderBy && params.orderBy.length > 0) {
- query.orderBy(...params.orderBy);
- } else {
- // 기본 정렬: 생성일 기준 내림차순
- query.orderBy(desc(techVendors.createdAt));
- }
-
- // 페이지네이션 적용
- query.offset(params.offset).limit(params.limit);
-
- const vendors = await query;
-
- // 첨부파일 정보 가져오기
- const vendorsWithAttachments = await Promise.all(
- vendors.map(async (vendor: TechVendor) => {
- const attachments = await tx
- .select({
- id: techVendorAttachments.id,
- fileName: techVendorAttachments.fileName,
- filePath: techVendorAttachments.filePath,
- })
- .from(techVendorAttachments)
- .where(eq(techVendorAttachments.vendorId, vendor.id));
-
- // 벤더의 worktype 조회
- const workTypes = await getVendorWorkTypes(tx, vendor.id, vendor.techVendorType);
-
- return {
- ...vendor,
- hasAttachments: attachments.length > 0,
- attachmentsList: attachments,
- workTypes: workTypes.join(', '), // 콤마로 구분해서 저장
- } as TechVendorWithAttachments;
- })
- );
-
- return vendorsWithAttachments;
-}
-
-// 메인 벤더 목록 수 조회 (첨부파일 정보 포함)
-export async function countTechVendorsWithAttachments(
- tx: any,
- where?: SQL<unknown>
-) {
- const query = tx.select({ count: count() }).from(techVendors);
-
- if (where) {
- query.where(where);
- }
-
- const result = await query;
- return result[0].count;
-}
-
-// 기술영업 벤더 조회
-export async function selectTechVendors(
- tx: any,
- params: {
- where?: SQL<unknown>;
- orderBy?: SQL<unknown>[];
- } & PaginationParams
-) {
- const query = tx.select().from(techVendors);
-
- if (params.where) {
- query.where(params.where);
- }
-
- if (params.orderBy && params.orderBy.length > 0) {
- query.orderBy(...params.orderBy);
- } else {
- query.orderBy(desc(techVendors.createdAt));
- }
-
- query.offset(params.offset).limit(params.limit);
-
- return query;
-}
-
-// 기술영업 벤더 수 카운트
-export async function countTechVendors(tx: any, where?: SQL<unknown>) {
- const query = tx.select({ count: count() }).from(techVendors);
-
- if (where) {
- query.where(where);
- }
-
- const result = await query;
- return result[0].count;
-}
-
-// 벤더 상태별 카운트
-export async function groupByTechVendorStatus(tx: any) {
- const result = await tx
- .select({
- status: techVendors.status,
- count: count(),
- })
- .from(techVendors)
- .groupBy(techVendors.status);
-
- return result;
-}
-
-// 벤더 상세 정보 조회
-export async function getTechVendorById(id: number) {
- const result = await db
- .select()
- .from(techVendors)
- .where(eq(techVendors.id, id));
-
- return result.length > 0 ? result[0] : null;
-}
-
-// 벤더 연락처 정보 조회
-export async function getTechVendorContactsById(id: number) {
- const result = await db
- .select()
- .from(techVendorContacts)
- .where(eq(techVendorContacts.id, id));
-
- return result.length > 0 ? result[0] : null;
-}
-
-// 신규 벤더 생성
-export async function insertTechVendor(
- tx: any,
- data: Omit<TechVendor, "id" | "createdAt" | "updatedAt">
-) {
- return tx
- .insert(techVendors)
- .values({
- ...data,
- createdAt: new Date(),
- updatedAt: new Date(),
- })
- .returning();
-}
-
-// 벤더 정보 업데이트 (단일)
-export async function updateTechVendor(
- tx: any,
- id: string | number,
- data: Partial<TechVendor>
-) {
- return tx
- .update(techVendors)
- .set({
- ...data,
- updatedAt: new Date(),
- })
- .where(eq(techVendors.id, Number(id)))
- .returning();
-}
-
-// 벤더 정보 업데이트 (다수)
-export async function updateTechVendors(
- tx: any,
- ids: (string | number)[],
- data: Partial<TechVendor>
-) {
- return tx
- .update(techVendors)
- .set({
- ...data,
- updatedAt: new Date(),
- })
- .where(inArray(techVendors.id, ids.map(id => Number(id))))
- .returning();
-}
-
-// 벤더 연락처 조회
-export async function selectTechVendorContacts(
- tx: any,
- params: {
- where?: SQL<unknown>;
- orderBy?: SQL<unknown>[];
- } & PaginationParams
-) {
- const query = tx.select().from(techVendorContacts);
-
- if (params.where) {
- query.where(params.where);
- }
-
- if (params.orderBy && params.orderBy.length > 0) {
- query.orderBy(...params.orderBy);
- } else {
- query.orderBy(desc(techVendorContacts.createdAt));
- }
-
- query.offset(params.offset).limit(params.limit);
-
- return query;
-}
-
-// 벤더 연락처 수 카운트
-export async function countTechVendorContacts(tx: any, where?: SQL<unknown>) {
- const query = tx.select({ count: count() }).from(techVendorContacts);
-
- if (where) {
- query.where(where);
- }
-
- const result = await query;
- return result[0].count;
-}
-
-// 연락처 생성
-export async function insertTechVendorContact(
- tx: any,
- data: Omit<TechVendorContact, "id" | "createdAt" | "updatedAt">
-) {
- return tx
- .insert(techVendorContacts)
- .values({
- ...data,
- createdAt: new Date(),
- updatedAt: new Date(),
- })
- .returning();
-}
-
-// 아이템 목록 조회
-export async function selectTechVendorItems(
- tx: any,
- params: {
- where?: SQL<unknown>;
- orderBy?: SQL<unknown>[];
- } & PaginationParams
-) {
- const query = tx.select().from(techVendorItemsView);
-
- if (params.where) {
- query.where(params.where);
- }
-
- if (params.orderBy && params.orderBy.length > 0) {
- query.orderBy(...params.orderBy);
- } else {
- query.orderBy(desc(techVendorItemsView.createdAt));
- }
-
- query.offset(params.offset).limit(params.limit);
-
- return query;
-}
-
-// 아이템 수 카운트
-export async function countTechVendorItems(tx: any, where?: SQL<unknown>) {
- const query = tx.select({ count: count() }).from(techVendorItemsView);
-
- if (where) {
- query.where(where);
- }
-
- const result = await query;
- return result[0].count;
-}
-
-// 아이템 생성
-export async function insertTechVendorItem(
- tx: any,
- data: Omit<TechVendorItem, "id" | "createdAt" | "updatedAt">
-) {
- return tx
- .insert(techVendorPossibleItems)
- .values({
- ...data,
- createdAt: new Date(),
- updatedAt: new Date(),
- })
- .returning();
-}
-
-// 벤더의 worktype 조회
-export async function getVendorWorkTypes(
- tx: any,
- vendorId: number,
- vendorType: string
-): Promise<string[]> {
- try {
- // 벤더의 possible items 조회
- const possibleItems = await tx
- .select({ itemCode: techVendorPossibleItems.itemCode })
- .from(techVendorPossibleItems)
- .where(eq(techVendorPossibleItems.vendorId, vendorId));
-
- if (!possibleItems.length) {
- return [];
- }
-
- const itemCodes = possibleItems.map((item: { itemCode: string }) => item.itemCode);
- const workTypes: string[] = [];
-
- // 벤더 타입에 따라 해당하는 아이템 테이블에서 worktype 조회
- if (vendorType.includes('조선')) {
- const shipWorkTypes = await tx
- .select({ workType: itemShipbuilding.workType })
- .from(itemShipbuilding)
- .where(inArray(itemShipbuilding.itemCode, itemCodes));
-
- workTypes.push(...shipWorkTypes.map((item: { workType: string | null }) => item.workType).filter(Boolean));
- }
-
- if (vendorType.includes('해양TOP')) {
- const topWorkTypes = await tx
- .select({ workType: itemOffshoreTop.workType })
- .from(itemOffshoreTop)
- .where(inArray(itemOffshoreTop.itemCode, itemCodes));
-
- workTypes.push(...topWorkTypes.map((item: { workType: string | null }) => item.workType).filter(Boolean));
- }
-
- if (vendorType.includes('해양HULL')) {
- const hullWorkTypes = await tx
- .select({ workType: itemOffshoreHull.workType })
- .from(itemOffshoreHull)
- .where(inArray(itemOffshoreHull.itemCode, itemCodes));
-
- workTypes.push(...hullWorkTypes.map((item: { workType: string | null }) => item.workType).filter(Boolean));
- }
-
- // 중복 제거 후 반환
- const uniqueWorkTypes = [...new Set(workTypes)];
-
- return uniqueWorkTypes;
- } catch (error) {
- return [];
- }
-}
+// src/lib/vendors/repository.ts
+
+import { eq, inArray, count, desc } from "drizzle-orm";
+import db from '@/db/db';
+import { SQL } from "drizzle-orm";
+import { techVendors, techVendorContacts, techVendorPossibleItems, techVendorItemsView, type TechVendor, type TechVendorContact, type TechVendorItem, type TechVendorWithAttachments, techVendorAttachments } from "@/db/schema/techVendors";
+import { itemShipbuilding, itemOffshoreTop, itemOffshoreHull } from "@/db/schema/items";
+
+export type NewTechVendorContact = typeof techVendorContacts.$inferInsert
+export type NewTechVendorItem = typeof techVendorPossibleItems.$inferInsert
+
+type PaginationParams = {
+ offset: number;
+ limit: number;
+};
+
+// 메인 벤더 목록 조회 (첨부파일 정보 포함)
+export async function selectTechVendorsWithAttachments(
+ tx: any,
+ params: {
+ where?: SQL<unknown>;
+ orderBy?: SQL<unknown>[];
+ } & PaginationParams
+) {
+ const query = tx
+ .select({
+ id: techVendors.id,
+ vendorName: techVendors.vendorName,
+ vendorCode: techVendors.vendorCode,
+ taxId: techVendors.taxId,
+ address: techVendors.address,
+ country: techVendors.country,
+ phone: techVendors.phone,
+ email: techVendors.email,
+ website: techVendors.website,
+ status: techVendors.status,
+ techVendorType: techVendors.techVendorType,
+ representativeName: techVendors.representativeName,
+ representativeEmail: techVendors.representativeEmail,
+ representativePhone: techVendors.representativePhone,
+ representativeBirth: techVendors.representativeBirth,
+ countryEng: techVendors.countryEng,
+ countryFab: techVendors.countryFab,
+ agentName: techVendors.agentName,
+ agentPhone: techVendors.agentPhone,
+ agentEmail: techVendors.agentEmail,
+ items: techVendors.items,
+ createdAt: techVendors.createdAt,
+ updatedAt: techVendors.updatedAt,
+ })
+ .from(techVendors);
+
+ // where 조건이 있는 경우
+ if (params.where) {
+ query.where(params.where);
+ }
+
+ // 정렬 조건이 있는 경우
+ if (params.orderBy && params.orderBy.length > 0) {
+ query.orderBy(...params.orderBy);
+ } else {
+ // 기본 정렬: 생성일 기준 내림차순
+ query.orderBy(desc(techVendors.createdAt));
+ }
+
+ // 페이지네이션 적용
+ query.offset(params.offset).limit(params.limit);
+
+ const vendors = await query;
+
+ // 첨부파일 정보 가져오기
+ const vendorsWithAttachments = await Promise.all(
+ vendors.map(async (vendor: TechVendor) => {
+ const attachments = await tx
+ .select({
+ id: techVendorAttachments.id,
+ fileName: techVendorAttachments.fileName,
+ filePath: techVendorAttachments.filePath,
+ })
+ .from(techVendorAttachments)
+ .where(eq(techVendorAttachments.vendorId, vendor.id));
+
+ // 벤더의 worktype 조회
+ const workTypes = await getVendorWorkTypes(tx, vendor.id, vendor.techVendorType);
+
+ return {
+ ...vendor,
+ hasAttachments: attachments.length > 0,
+ attachmentsList: attachments,
+ workTypes: workTypes.join(', '), // 콤마로 구분해서 저장
+ } as TechVendorWithAttachments;
+ })
+ );
+
+ return vendorsWithAttachments;
+}
+
+// 메인 벤더 목록 수 조회 (첨부파일 정보 포함)
+export async function countTechVendorsWithAttachments(
+ tx: any,
+ where?: SQL<unknown>
+) {
+ const query = tx.select({ count: count() }).from(techVendors);
+
+ if (where) {
+ query.where(where);
+ }
+
+ const result = await query;
+ return result[0].count;
+}
+
+// 기술영업 벤더 조회
+export async function selectTechVendors(
+ tx: any,
+ params: {
+ where?: SQL<unknown>;
+ orderBy?: SQL<unknown>[];
+ } & PaginationParams
+) {
+ const query = tx.select().from(techVendors);
+
+ if (params.where) {
+ query.where(params.where);
+ }
+
+ if (params.orderBy && params.orderBy.length > 0) {
+ query.orderBy(...params.orderBy);
+ } else {
+ query.orderBy(desc(techVendors.createdAt));
+ }
+
+ query.offset(params.offset).limit(params.limit);
+
+ return query;
+}
+
+// 기술영업 벤더 수 카운트
+export async function countTechVendors(tx: any, where?: SQL<unknown>) {
+ const query = tx.select({ count: count() }).from(techVendors);
+
+ if (where) {
+ query.where(where);
+ }
+
+ const result = await query;
+ return result[0].count;
+}
+
+// 벤더 상태별 카운트
+export async function groupByTechVendorStatus(tx: any) {
+ const result = await tx
+ .select({
+ status: techVendors.status,
+ count: count(),
+ })
+ .from(techVendors)
+ .groupBy(techVendors.status);
+
+ return result;
+}
+
+// 벤더 상세 정보 조회
+export async function getTechVendorById(id: number) {
+ const result = await db
+ .select()
+ .from(techVendors)
+ .where(eq(techVendors.id, id));
+
+ return result.length > 0 ? result[0] : null;
+}
+
+// 벤더 연락처 정보 조회
+export async function getTechVendorContactsById(id: number) {
+ const result = await db
+ .select()
+ .from(techVendorContacts)
+ .where(eq(techVendorContacts.id, id));
+
+ return result.length > 0 ? result[0] : null;
+}
+
+// 신규 벤더 생성
+export async function insertTechVendor(
+ tx: any,
+ data: Omit<TechVendor, "id" | "createdAt" | "updatedAt">
+) {
+ return tx
+ .insert(techVendors)
+ .values({
+ ...data,
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ })
+ .returning();
+}
+
+// 벤더 정보 업데이트 (단일)
+export async function updateTechVendor(
+ tx: any,
+ id: string | number,
+ data: Partial<TechVendor>
+) {
+ return tx
+ .update(techVendors)
+ .set({
+ ...data,
+ updatedAt: new Date(),
+ })
+ .where(eq(techVendors.id, Number(id)))
+ .returning();
+}
+
+// 벤더 정보 업데이트 (다수)
+export async function updateTechVendors(
+ tx: any,
+ ids: (string | number)[],
+ data: Partial<TechVendor>
+) {
+ return tx
+ .update(techVendors)
+ .set({
+ ...data,
+ updatedAt: new Date(),
+ })
+ .where(inArray(techVendors.id, ids.map(id => Number(id))))
+ .returning();
+}
+
+// 벤더 연락처 조회
+export async function selectTechVendorContacts(
+ tx: any,
+ params: {
+ where?: SQL<unknown>;
+ orderBy?: SQL<unknown>[];
+ } & PaginationParams
+) {
+ const query = tx.select().from(techVendorContacts);
+
+ if (params.where) {
+ query.where(params.where);
+ }
+
+ if (params.orderBy && params.orderBy.length > 0) {
+ query.orderBy(...params.orderBy);
+ } else {
+ query.orderBy(desc(techVendorContacts.createdAt));
+ }
+
+ query.offset(params.offset).limit(params.limit);
+
+ return query;
+}
+
+// 벤더 연락처 수 카운트
+export async function countTechVendorContacts(tx: any, where?: SQL<unknown>) {
+ const query = tx.select({ count: count() }).from(techVendorContacts);
+
+ if (where) {
+ query.where(where);
+ }
+
+ const result = await query;
+ return result[0].count;
+}
+
+// 연락처 생성
+export async function insertTechVendorContact(
+ tx: any,
+ data: Omit<TechVendorContact, "id" | "createdAt" | "updatedAt">
+) {
+ return tx
+ .insert(techVendorContacts)
+ .values({
+ ...data,
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ })
+ .returning();
+}
+
+// 아이템 목록 조회
+export async function selectTechVendorItems(
+ tx: any,
+ params: {
+ where?: SQL<unknown>;
+ orderBy?: SQL<unknown>[];
+ } & PaginationParams
+) {
+ const query = tx.select().from(techVendorItemsView);
+
+ if (params.where) {
+ query.where(params.where);
+ }
+
+ if (params.orderBy && params.orderBy.length > 0) {
+ query.orderBy(...params.orderBy);
+ } else {
+ query.orderBy(desc(techVendorItemsView.createdAt));
+ }
+
+ query.offset(params.offset).limit(params.limit);
+
+ return query;
+}
+
+// 아이템 수 카운트
+export async function countTechVendorItems(tx: any, where?: SQL<unknown>) {
+ const query = tx.select({ count: count() }).from(techVendorItemsView);
+
+ if (where) {
+ query.where(where);
+ }
+
+ const result = await query;
+ return result[0].count;
+}
+
+// 아이템 생성
+export async function insertTechVendorItem(
+ tx: any,
+ data: Omit<TechVendorItem, "id" | "createdAt" | "updatedAt">
+) {
+ return tx
+ .insert(techVendorPossibleItems)
+ .values({
+ ...data,
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ })
+ .returning();
+}
+
+// 벤더의 worktype 조회
+export async function getVendorWorkTypes(
+ tx: any,
+ vendorId: number,
+ vendorType: string
+): Promise<string[]> {
+ try {
+ // 벤더의 possible items 조회 - 모든 필드 가져오기
+ const possibleItems = await tx
+ .select({
+ itemCode: techVendorPossibleItems.itemCode,
+ shipTypes: techVendorPossibleItems.shipTypes,
+ itemList: techVendorPossibleItems.itemList,
+ subItemList: techVendorPossibleItems.subItemList,
+ workType: techVendorPossibleItems.workType
+ })
+ .from(techVendorPossibleItems)
+ .where(eq(techVendorPossibleItems.vendorId, vendorId));
+ console.log("possibleItems", possibleItems);
+ if (!possibleItems.length) {
+ return [];
+ }
+
+ const workTypes: string[] = [];
+
+ // 벤더 타입에 따라 해당하는 아이템 테이블에서 worktype 조회
+ if (vendorType.includes('조선')) {
+ const itemCodes = possibleItems
+ .map((item: { itemCode?: string | null }) => item.itemCode)
+ .filter(Boolean);
+
+ if (itemCodes.length > 0) {
+ const shipWorkTypes = await tx
+ .select({ workType: itemShipbuilding.workType })
+ .from(itemShipbuilding)
+ .where(inArray(itemShipbuilding.itemCode, itemCodes));
+
+ workTypes.push(...shipWorkTypes.map((item: { workType: string | null }) => item.workType).filter(Boolean));
+ }
+ }
+
+ if (vendorType.includes('해양TOP')) {
+ // 1. 아이템코드가 있는 경우
+ const itemCodesTop = possibleItems
+ .map((item: { itemCode?: string | null }) => item.itemCode)
+ .filter(Boolean) as string[];
+
+ if (itemCodesTop.length > 0) {
+ const topWorkTypes = await tx
+ .select({ workType: itemOffshoreTop.workType })
+ .from(itemOffshoreTop)
+ .where(inArray(itemOffshoreTop.itemCode, itemCodesTop));
+
+ workTypes.push(
+ ...topWorkTypes
+ .map((item: { workType: string | null }) => item.workType)
+ .filter(Boolean) as string[]
+ );
+ }
+
+ // 2. 아이템코드가 없는 경우 서브아이템리스트로 매칭
+ const itemsWithoutCodeTop = possibleItems.filter(
+ (item: { itemCode?: string | null; subItemList?: string | null }) =>
+ !item.itemCode && item.subItemList
+ );
+ if (itemsWithoutCodeTop.length > 0) {
+ const subItemListsTop = itemsWithoutCodeTop
+ .map((item: { subItemList?: string | null }) => item.subItemList)
+ .filter(Boolean) as string[];
+
+ if (subItemListsTop.length > 0) {
+ const topWorkTypesBySubItem = await tx
+ .select({ workType: itemOffshoreTop.workType })
+ .from(itemOffshoreTop)
+ .where(inArray(itemOffshoreTop.subItemList, subItemListsTop));
+
+ workTypes.push(
+ ...topWorkTypesBySubItem
+ .map((item: { workType: string | null }) => item.workType)
+ .filter(Boolean) as string[]
+ );
+ }
+ }
+ }
+ if (vendorType.includes('해양HULL')) {
+ // 1. 아이템코드가 있는 경우
+ const itemCodes = possibleItems
+ .map((item: { itemCode?: string | null }) => item.itemCode)
+ .filter(Boolean);
+
+ if (itemCodes.length > 0) {
+ const hullWorkTypes = await tx
+ .select({ workType: itemOffshoreHull.workType })
+ .from(itemOffshoreHull)
+ .where(inArray(itemOffshoreHull.itemCode, itemCodes));
+
+ workTypes.push(...hullWorkTypes.map((item: { workType: string | null }) => item.workType).filter(Boolean));
+ }
+
+ // 2. 아이템코드가 없는 경우 서브아이템리스트로 매칭
+ const itemsWithoutCodeHull = possibleItems.filter(
+ (item: { itemCode?: string | null; subItemList?: string | null }) =>
+ !item.itemCode && item.subItemList
+ );
+
+ if (itemsWithoutCodeHull.length > 0) {
+ const subItemListsHull = itemsWithoutCodeHull
+ .map((item: { subItemList?: string | null }) => item.subItemList)
+ .filter(Boolean) as string[];
+
+ if (subItemListsHull.length > 0) {
+ const hullWorkTypesBySubItem = await tx
+ .select({ workType: itemOffshoreHull.workType })
+ .from(itemOffshoreHull)
+ .where(inArray(itemOffshoreHull.subItemList, subItemListsHull));
+
+ workTypes.push(...hullWorkTypesBySubItem.map((item: { workType: string | null }) => item.workType).filter(Boolean));
+ }
+ }
+ }
+ // 중복 제거 후 반환
+ const uniqueWorkTypes = [...new Set(workTypes)];
+
+ return uniqueWorkTypes;
+ } catch (error) {
+ console.error('getVendorWorkTypes 오류:', error);
+ return [];
+ }
+}