summaryrefslogtreecommitdiff
path: root/lib/avl/snapshot-utils.ts
blob: 0f5d9240eb9af1ccdaebaf1c33a366e0142a26ea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/**
 * AVL Vendor Info 스냅샷 관련 유틸리티 함수들
 */

import db from "@/db/db"
import { avlVendorInfo } from "@/db/schema/avl/avl"
import { inArray } from "drizzle-orm"

/**
 * AVL Vendor Info 스냅샷 데이터 타입
 */
export interface VendorInfoSnapshot {
  id: number
  // 설계 정보
  equipBulkDivision: string | null
  disciplineCode: string | null
  disciplineName: string | null
  // 자재 정보
  materialNameCustomerSide: string | null
  packageCode: string | null
  packageName: string | null
  materialGroupCode: string | null
  materialGroupName: string | null
  // 협력업체 정보
  vendorId: number | null
  vendorName: string | null
  vendorCode: string | null
  avlVendorName: string | null
  tier: string | null
  // FA 정보
  faTarget: boolean
  faStatus: string | null
  // Agent 정보
  isAgent: boolean
  // 계약 서명주체
  contractSignerId: number | null
  contractSignerName: string | null
  contractSignerCode: string | null
  // 위치 정보
  headquarterLocation: string | null
  manufacturingLocation: string | null
  // SHI Qualification
  hasAvl: boolean
  isBlacklist: boolean
  isBcc: boolean
  // 기술영업 견적결과
  techQuoteNumber: string | null
  quoteCode: string | null
  quoteVendorId: number | null
  quoteVendorName: string | null
  quoteVendorCode: string | null
  quoteCountry: string | null
  quoteTotalAmount: string | null // 숫자를 문자열로 변환
  quoteReceivedDate: string | null
  // 업체 실적 현황
  recentQuoteDate: string | null
  recentQuoteNumber: string | null
  recentOrderDate: string | null
  recentOrderNumber: string | null
  // 기타
  remark: string | null
  // 타임스탬프
  createdAt: string
  updatedAt: string
}

/**
 * AVL Vendor Info ID 목록으로부터 스냅샷 데이터를 생성합니다.
 * 
 * @param vendorInfoIds - 스냅샷을 생성할 AVL Vendor Info ID 목록
 * @returns 스냅샷 데이터 배열
 */
export async function createVendorInfoSnapshot(vendorInfoIds: number[]): Promise<VendorInfoSnapshot[]> {
  if (vendorInfoIds.length === 0) {
    console.log('[SNAPSHOT] 빈 vendorInfoIds 배열, 빈 스냅샷 반환')
    return []
  }

  try {
    console.log('[SNAPSHOT] 스냅샷 생성 시작', { vendorInfoIds, count: vendorInfoIds.length })
    
    // AVL Vendor Info 데이터 조회
    const vendorInfoList = await db
      .select()
      .from(avlVendorInfo)
      .where(inArray(avlVendorInfo.id, vendorInfoIds))

    console.log('[SNAPSHOT] DB 조회 완료', { 
      requestedIds: vendorInfoIds,
      foundCount: vendorInfoList.length,
      foundIds: vendorInfoList.map(v => v.id)
    })

    // 스냅샷 데이터로 변환
    const snapshot: VendorInfoSnapshot[] = vendorInfoList.map(info => ({
      id: info.id,
      // 설계 정보
      equipBulkDivision: info.equipBulkDivision,
      disciplineCode: info.disciplineCode,
      disciplineName: info.disciplineName,
      // 자재 정보
      materialNameCustomerSide: info.materialNameCustomerSide,
      packageCode: info.packageCode,
      packageName: info.packageName,
      materialGroupCode: info.materialGroupCode,
      materialGroupName: info.materialGroupName,
      // 협력업체 정보
      vendorId: info.vendorId,
      vendorName: info.vendorName,
      vendorCode: info.vendorCode,
      avlVendorName: info.avlVendorName,
      tier: info.tier,
      // FA 정보
      faTarget: info.faTarget ?? false,
      faStatus: info.faStatus,
      // Agent 정보
      isAgent: info.isAgent ?? false,
      // 계약 서명주체
      contractSignerId: info.contractSignerId,
      contractSignerName: info.contractSignerName,
      contractSignerCode: info.contractSignerCode,
      // 위치 정보
      headquarterLocation: info.headquarterLocation,
      manufacturingLocation: info.manufacturingLocation,
      // SHI Qualification
      hasAvl: info.hasAvl ?? false,
      isBlacklist: info.isBlacklist ?? false,
      isBcc: info.isBcc ?? false,
      // 기술영업 견적결과
      techQuoteNumber: info.techQuoteNumber,
      quoteCode: info.quoteCode,
      quoteVendorId: info.quoteVendorId,
      quoteVendorName: info.quoteVendorName,
      quoteVendorCode: info.quoteVendorCode,
      quoteCountry: info.quoteCountry,
      quoteTotalAmount: info.quoteTotalAmount?.toString() || null,
      quoteReceivedDate: info.quoteReceivedDate,
      // 업체 실적 현황
      recentQuoteDate: info.recentQuoteDate,
      recentQuoteNumber: info.recentQuoteNumber,
      recentOrderDate: info.recentOrderDate,
      recentOrderNumber: info.recentOrderNumber,
      // 기타
      remark: info.remark,
      // 타임스탬프 (ISO 문자열로 변환)
      createdAt: info.createdAt?.toISOString() || new Date().toISOString(),
      updatedAt: info.updatedAt?.toISOString() || new Date().toISOString(),
    }))

    console.log('[SNAPSHOT] 스냅샷 변환 완료', { 
      snapshotCount: snapshot.length,
      sampleData: snapshot.slice(0, 2) // 처음 2개 항목만 로깅
    })

    return snapshot

  } catch (error) {
    console.error('[SNAPSHOT] Vendor Info 스냅샷 생성 실패:', error)
    throw new Error('Vendor Info 스냅샷 생성 중 오류가 발생했습니다.')
  }
}

/**
 * 스냅샷 데이터의 통계 정보를 생성합니다.
 * 
 * @param snapshot - 스냅샷 데이터 배열
 * @returns 통계 정보 객체
 */
export function getSnapshotStatistics(snapshot: VendorInfoSnapshot[]) {
  return {
    totalCount: snapshot.length,
    avlCount: snapshot.filter(item => item.hasAvl).length,
    blacklistCount: snapshot.filter(item => item.isBlacklist).length,
    bccCount: snapshot.filter(item => item.isBcc).length,
    faTargetCount: snapshot.filter(item => item.faTarget).length,
    agentCount: snapshot.filter(item => item.isAgent).length,
    tierDistribution: {
      'Tier 1': snapshot.filter(item => item.tier === 'Tier 1').length,
      'Tier 2': snapshot.filter(item => item.tier === 'Tier 2').length,
      'Tier 3': snapshot.filter(item => item.tier === 'Tier 3').length,
      'Other': snapshot.filter(item => item.tier && !['Tier 1', 'Tier 2', 'Tier 3'].includes(item.tier)).length,
      'Unspecified': snapshot.filter(item => !item.tier).length,
    },
    byDiscipline: snapshot.reduce((acc, item) => {
      const discipline = item.disciplineName || 'Unknown'
      acc[discipline] = (acc[discipline] || 0) + 1
      return acc
    }, {} as Record<string, number>),
  }
}