diff options
| author | joonhoekim <26rote@gmail.com> | 2025-10-14 14:25:28 +0900 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-10-14 14:25:28 +0900 |
| commit | 40250c61031263606dd073ce7056a3e8e27f18d0 (patch) | |
| tree | 0ea566507b3b341825e9825f9cee43f470957292 /lib/avl/service.ts | |
| parent | 6d3752d34dfdf2c3870b9f6ffe431cfa98e302c9 (diff) | |
(김준회) AVL 구매요구사항 수정
- AVL 상세 엑셀 익스포트 추가
- 레코드 이동 멀티선택 추가
- 최종확정처리 오류 수정
- 프로젝트 AVL에 H/T 구분 추가
Diffstat (limited to 'lib/avl/service.ts')
| -rw-r--r-- | lib/avl/service.ts | 167 |
1 files changed, 144 insertions, 23 deletions
diff --git a/lib/avl/service.ts b/lib/avl/service.ts index 1f781486..5d7c2418 100644 --- a/lib/avl/service.ts +++ b/lib/avl/service.ts @@ -941,9 +941,71 @@ export async function deleteAvlVendorInfo(id: number): Promise<boolean> { } /** + * 프로젝트 AVL 벤더 정보 건수 조회 + */ +export async function getProjectAvlVendorInfoCount( + projectCode: string, + htDivision?: string +): Promise<number> { + try { + const conditions = [ + eq(avlVendorInfo.projectCode, projectCode), + eq(avlVendorInfo.isTemplate, false) + ]; + + // H/T 구분이 있으면 조건에 추가 + if (htDivision) { + conditions.push(eq(avlVendorInfo.htDivision, htDivision)); + } + + const result = await db + .select({ count: count() }) + .from(avlVendorInfo) + .where(and(...conditions)); + + return result[0]?.count || 0; + } catch (error) { + console.error("Error in getProjectAvlVendorInfoCount:", error); + return 0; + } +} + +/** + * 표준 AVL 벤더 정보 건수 조회 + */ +export async function getStandardAvlVendorInfoCount( + standardAvlInfo: { + constructionSector: string; + shipType: string; + avlKind: string; + htDivision: string; + } +): Promise<number> { + try { + const result = await db + .select({ count: count() }) + .from(avlVendorInfo) + .where( + and( + eq(avlVendorInfo.isTemplate, true), + eq(avlVendorInfo.constructionSector, standardAvlInfo.constructionSector), + eq(avlVendorInfo.shipType, standardAvlInfo.shipType), + eq(avlVendorInfo.avlKind, standardAvlInfo.avlKind), + eq(avlVendorInfo.htDivision, standardAvlInfo.htDivision) + ) + ); + + return result[0]?.count || 0; + } catch (error) { + console.error("Error in getStandardAvlVendorInfoCount:", error); + return 0; + } +} + +/** * 프로젝트 AVL 최종 확정 * 1. 주어진 프로젝트 정보로 avlList에 레코드를 생성한다. - * 2. 현재 avlVendorInfo 레코드들의 avlListId를 새로 생성된 AVL 리스트 ID로 업데이트한다. + * 2. 해당 프로젝트 코드의 모든 avlVendorInfo 레코드들의 avlListId를 새로 생성된 AVL 리스트 ID로 업데이트한다. */ export async function finalizeProjectAvl( projectCode: string, @@ -953,23 +1015,48 @@ export async function finalizeProjectAvl( shipType: string; htDivision: string; }, - avlVendorInfoIds: number[], currentUser?: string ): Promise<{ success: boolean; avlListId?: number; message: string }> { try { debugLog('프로젝트 AVL 최종 확정 시작', { projectCode, projectInfo, - avlVendorInfoIds: avlVendorInfoIds.length, currentUser }); - // 1. 기존 AVL 리스트의 최고 revision 확인 + // 1. DB에서 해당 프로젝트 코드와 H/T 구분의 모든 avlVendorInfo 레코드 ID 조회 + const allVendorInfoRecords = await db + .select({ id: avlVendorInfo.id }) + .from(avlVendorInfo) + .where( + and( + eq(avlVendorInfo.projectCode, projectCode), + eq(avlVendorInfo.htDivision, projectInfo.htDivision), + eq(avlVendorInfo.isTemplate, false) + ) + ); + + const avlVendorInfoIds = allVendorInfoRecords.map(record => record.id); + + if (avlVendorInfoIds.length === 0) { + return { + success: false, + message: "해당 프로젝트의 AVL 벤더 정보가 없습니다." + }; + } + + debugLog('프로젝트 AVL 벤더 정보 조회 완료', { + projectCode, + totalVendorInfoCount: avlVendorInfoIds.length + }); + + // 2. 기존 AVL 리스트의 최고 revision 확인 (프로젝트 코드 + H/T 구분별로) const existingAvlLists = await db .select({ rev: avlList.rev }) .from(avlList) .where(and( eq(avlList.projectCode, projectCode), + eq(avlList.htDivision, projectInfo.htDivision), eq(avlList.isTemplate, false) )) .orderBy(desc(avlList.rev)) @@ -983,7 +1070,7 @@ export async function finalizeProjectAvl( nextRevision }); - // 2. AVL 리스트 생성을 위한 데이터 준비 + // 3. AVL 리스트 생성을 위한 데이터 준비 const createAvlListData: CreateAvlListInput = { isTemplate: false, // 프로젝트 AVL이므로 false constructionSector: projectInfo.constructionSector, @@ -998,7 +1085,7 @@ export async function finalizeProjectAvl( debugLog('AVL 리스트 생성 데이터', { createAvlListData }); - // 2. AVL Vendor Info 스냅샷 생성 (AVL 리스트 생성 전에 현재 상태 저장) + // 4. AVL Vendor Info 스냅샷 생성 (AVL 리스트 생성 전에 현재 상태 저장) debugLog('AVL Vendor Info 스냅샷 생성 시작', { vendorInfoIdsCount: avlVendorInfoIds.length, vendorInfoIds: avlVendorInfoIds @@ -1017,7 +1104,7 @@ export async function finalizeProjectAvl( snapshotLength: createAvlListData.vendorInfoSnapshot?.length }); - // 3. AVL 리스트 생성 + // 5. AVL 리스트 생성 const newAvlList = await createAvlList(createAvlListData); if (!newAvlList) { @@ -1026,7 +1113,7 @@ export async function finalizeProjectAvl( debugSuccess('AVL 리스트 생성 완료', { avlListId: newAvlList.id }); - // 3. avlVendorInfo 레코드들의 avlListId 업데이트 + // 6. avlVendorInfo 레코드들의 avlListId 업데이트 if (avlVendorInfoIds.length > 0) { debugLog('AVL Vendor Info 업데이트 시작', { count: avlVendorInfoIds.length, @@ -1071,7 +1158,7 @@ export async function finalizeProjectAvl( } } - // 4. 캐시 무효화 + // 7. 캐시 무효화 revalidateTag('avl-list'); revalidateTag('avl-vendor-info'); @@ -1123,6 +1210,11 @@ export const getProjectAvlVendorInfo = async (input: GetProjectAvlSchema) => { whereConditions.push(ilike(avlVendorInfo.projectCode, `%${input.projectCode}%`)); } + // 필수 필터: H/T 구분 (avlVendorInfo에서 직접 필터링) + if (input.htDivision) { + whereConditions.push(eq(avlVendorInfo.htDivision, input.htDivision)); + } + // 검색어 기반 필터링 if (input.search) { const searchTerm = `%${input.search}%`; @@ -1513,11 +1605,12 @@ export const getStandardAvlVendorInfo = async (input: GetStandardAvlSchema) => { export const copyToProjectAvl = async ( selectedIds: number[], targetProjectCode: string, + targetHtDivision: string, targetAvlListId: number, userName: string ): Promise<ActionResult> => { try { - debugLog('선종별표준AVL → 프로젝트AVL 복사 시작', { selectedIds, targetProjectCode, targetAvlListId }); + debugLog('선종별표준AVL → 프로젝트AVL 복사 시작', { selectedIds, targetProjectCode, targetHtDivision, targetAvlListId }); if (!selectedIds.length) { return { success: false, message: "복사할 항목을 선택해주세요." }; @@ -1544,12 +1637,12 @@ export const copyToProjectAvl = async ( id: undefined, // 새 ID 생성 isTemplate: false, // 프로젝트 AVL로 변경 projectCode: targetProjectCode, // 대상 프로젝트 코드 + htDivision: targetHtDivision, // 프로젝트 AVL에서 선택한 H/T 구분 적용 avlListId: targetAvlListId, // 대상 AVL 리스트 ID // 표준 AVL 필드들은 null로 설정 (프로젝트 AVL에서는 사용하지 않음) constructionSector: null, shipType: null, avlKind: null, - htDivision: null, createdAt: undefined, updatedAt: undefined, })); @@ -1887,11 +1980,12 @@ export const copyToVendorPool = async ( export const copyFromVendorPoolToProjectAvl = async ( selectedIds: number[], targetProjectCode: string, + targetHtDivision: string, targetAvlListId: number, userName: string ): Promise<ActionResult> => { try { - debugLog('벤더풀 → 프로젝트AVL 복사 시작', { selectedIds, targetProjectCode, targetAvlListId }); + debugLog('벤더풀 → 프로젝트AVL 복사 시작', { selectedIds, targetProjectCode, targetHtDivision, targetAvlListId }); if (!selectedIds.length) { return { success: false, message: "복사할 항목을 선택해주세요." }; @@ -1913,6 +2007,7 @@ export const copyFromVendorPoolToProjectAvl = async ( const recordsToInsert: NewAvlVendorInfo[] = selectedRecords.map(record => ({ // 프로젝트 AVL용 필드들 projectCode: targetProjectCode, + htDivision: targetHtDivision, // 프로젝트 AVL에서 선택한 H/T 구분 적용 avlListId: targetAvlListId, isTemplate: false, @@ -1921,9 +2016,8 @@ export const copyFromVendorPoolToProjectAvl = async ( vendorName: record.vendorName, vendorCode: record.vendorCode, - // 기본 정보 (벤더풀의 데이터 활용) - constructionSector: record.constructionSector, - htDivision: record.htDivision, + // 기본 정보 (프로젝트 AVL에서는 constructionSector 사용 안함) + constructionSector: null, // 자재그룹 정보 materialGroupCode: record.materialGroupCode, @@ -2332,6 +2426,7 @@ export const copyFromStandardAvlToVendorPool = async ( /** * 표준 AVL 최종 확정 * 표준 AVL을 최종 확정하여 AVL 리스트에 등록합니다. + * DB에서 해당 조건에 맞는 모든 avlVendorInfo 레코드를 조회하여 확정합니다. */ export async function finalizeStandardAvl( standardAvlInfo: { @@ -2340,17 +2435,43 @@ export async function finalizeStandardAvl( avlKind: string; htDivision: string; }, - avlVendorInfoIds: number[], currentUser?: string ): Promise<{ success: boolean; avlListId?: number; message: string }> { try { debugLog('표준 AVL 최종 확정 시작', { standardAvlInfo, - avlVendorInfoIds: avlVendorInfoIds.length, currentUser }); - // 1. 기존 표준 AVL 리스트의 최고 revision 확인 + // 1. DB에서 해당 조건의 모든 avlVendorInfo 레코드 ID 조회 + const allVendorInfoRecords = await db + .select({ id: avlVendorInfo.id }) + .from(avlVendorInfo) + .where( + and( + eq(avlVendorInfo.isTemplate, true), + eq(avlVendorInfo.constructionSector, standardAvlInfo.constructionSector), + eq(avlVendorInfo.shipType, standardAvlInfo.shipType), + eq(avlVendorInfo.avlKind, standardAvlInfo.avlKind), + eq(avlVendorInfo.htDivision, standardAvlInfo.htDivision) + ) + ); + + const avlVendorInfoIds = allVendorInfoRecords.map(record => record.id); + + if (avlVendorInfoIds.length === 0) { + return { + success: false, + message: "해당 조건의 표준 AVL 벤더 정보가 없습니다." + }; + } + + debugLog('표준 AVL 벤더 정보 조회 완료', { + standardAvlInfo, + totalVendorInfoCount: avlVendorInfoIds.length + }); + + // 2. 기존 표준 AVL 리스트의 최고 revision 확인 const existingAvlLists = await db .select({ rev: avlList.rev }) .from(avlList) @@ -2372,7 +2493,7 @@ export async function finalizeStandardAvl( nextRevision }); - // 2. AVL 리스트 생성을 위한 데이터 준비 + // 3. AVL 리스트 생성을 위한 데이터 준비 const createAvlListData: CreateAvlListInput = { isTemplate: true, // 표준 AVL이므로 true constructionSector: standardAvlInfo.constructionSector, @@ -2387,7 +2508,7 @@ export async function finalizeStandardAvl( debugLog('표준 AVL 리스트 생성 데이터', { createAvlListData }); - // 2-1. AVL Vendor Info 스냅샷 생성 (AVL 리스트 생성 전에 현재 상태 저장) + // 4. AVL Vendor Info 스냅샷 생성 (AVL 리스트 생성 전에 현재 상태 저장) debugLog('표준 AVL Vendor Info 스냅샷 생성 시작', { vendorInfoIdsCount: avlVendorInfoIds.length, vendorInfoIds: avlVendorInfoIds @@ -2406,7 +2527,7 @@ export async function finalizeStandardAvl( snapshotLength: createAvlListData.vendorInfoSnapshot?.length }); - // 3. AVL 리스트 생성 + // 5. AVL 리스트 생성 const newAvlList = await createAvlList(createAvlListData); if (!newAvlList) { @@ -2415,7 +2536,7 @@ export async function finalizeStandardAvl( debugSuccess('표준 AVL 리스트 생성 완료', { avlListId: newAvlList.id }); - // 4. avlVendorInfo 레코드들의 avlListId 업데이트 + // 6. avlVendorInfo 레코드들의 avlListId 업데이트 if (avlVendorInfoIds.length > 0) { debugLog('표준 AVL Vendor Info 업데이트 시작', { count: avlVendorInfoIds.length, @@ -2459,7 +2580,7 @@ export async function finalizeStandardAvl( } } - // 5. 캐시 무효화 + // 7. 캐시 무효화 revalidateTag('avl-list'); revalidateTag('avl-vendor-info'); |
