summaryrefslogtreecommitdiff
path: root/lib/bidding/manage/export-bidding-items-to-excel.ts
blob: 814648a7251f760d043ba6dd9345180c68853d55 (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
import ExcelJS from "exceljs"
import { PRItemInfo } from "@/components/bidding/manage/bidding-items-editor"
import { getProjectCodesByIds } from "./project-utils"

/**
 * 입찰품목 목록을 Excel로 내보내기
 */
export async function exportBiddingItemsToExcel(
  items: PRItemInfo[],
  {
    filename = "입찰품목목록",
  }: {
    filename?: string
  } = {}
): Promise<void> {
  // 프로젝트 ID 목록 수집
  const projectIds = items
    .map((item) => item.projectId)
    .filter((id): id is number => id != null && id > 0)

  // 프로젝트 코드 맵 조회
  const projectCodeMap = await getProjectCodesByIds(projectIds)

  // 헤더 정의
  const headers = [
    "프로젝트코드",
    "프로젝트명",
    "자재그룹코드",
    "자재그룹명",
    "자재코드",
    "자재명",
    "수량",
    "수량단위",
    "중량",
    "중량단위",
    "납품요청일",
    "가격단위",
    "구매단위",
    "자재순중량",
    "내정단가",
    "내정금액",
    "내정통화",
    "예산금액",
    "예산통화",
    "실적금액",
    "실적통화",
    "WBS코드",
    "WBS명",
    "코스트센터코드",
    "코스트센터명",
    "GL계정코드",
    "GL계정명",
    "PR번호",
  ]

  // 데이터 행 생성
  const dataRows = items.map((item) => {
    // 프로젝트 코드 조회
    const projectCode = item.projectId
      ? projectCodeMap.get(item.projectId) || ""
      : ""

    return [
      projectCode,
      item.projectInfo || "",
      item.materialGroupNumber || "",
      item.materialGroupInfo || "",
      item.materialNumber || "",
      item.materialInfo || "",
      item.quantity || "",
      item.quantityUnit || "",
      item.totalWeight || "",
      item.weightUnit || "",
      item.requestedDeliveryDate || "",
      item.priceUnit || "",
      item.purchaseUnit || "",
      item.materialWeight || "",
      item.targetUnitPrice || "",
      item.targetAmount || "",
      item.targetCurrency || "KRW",
      item.budgetAmount || "",
      item.budgetCurrency || "KRW",
      item.actualAmount || "",
      item.actualCurrency || "KRW",
      item.wbsCode || "",
      item.wbsName || "",
      item.costCenterCode || "",
      item.costCenterName || "",
      item.glAccountCode || "",
      item.glAccountName || "",
      item.prNumber || "",
    ]
  })

  // 최종 sheetData
  const sheetData = [headers, ...dataRows]

  // ExcelJS로 파일 생성 및 다운로드
  await createAndDownloadExcel(sheetData, headers.length, filename)
}

/**
 * Excel 파일 생성 및 다운로드
 */
async function createAndDownloadExcel(
  sheetData: any[][],
  columnCount: number,
  filename: string
): Promise<void> {
  // ExcelJS 워크북/시트 생성
  const workbook = new ExcelJS.Workbook()
  const worksheet = workbook.addWorksheet("Sheet1")

  // 칼럼별 최대 길이 추적
  const maxColumnLengths = Array(columnCount).fill(0)
  sheetData.forEach((row) => {
    row.forEach((cellValue, colIdx) => {
      const cellText = cellValue?.toString() ?? ""
      if (cellText.length > maxColumnLengths[colIdx]) {
        maxColumnLengths[colIdx] = cellText.length
      }
    })
  })

  // 시트에 데이터 추가 + 헤더 스타일
  sheetData.forEach((arr, idx) => {
    const row = worksheet.addRow(arr)

    // 헤더 스타일 적용 (첫 번째 행)
    if (idx === 0) {
      row.font = { bold: true }
      row.alignment = { horizontal: "center" }
      row.eachCell((cell) => {
        cell.fill = {
          type: "pattern",
          pattern: "solid",
          fgColor: { argb: "FFCCCCCC" },
        }
      })
    }
  })

  // 칼럼 너비 자동 조정
  maxColumnLengths.forEach((len, idx) => {
    // 최소 너비 10, +2 여백
    worksheet.getColumn(idx + 1).width = Math.max(len + 2, 10)
  })

  // 최종 파일 다운로드
  const buffer = await workbook.xlsx.writeBuffer()
  const blob = new Blob([buffer], {
    type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  })
  const url = URL.createObjectURL(blob)
  const link = document.createElement("a")
  link.href = url
  link.download = `${filename}.xlsx`
  link.click()
  URL.revokeObjectURL(url)
}