Initial commit

This commit is contained in:
2026-04-13 14:17:43 +09:00
commit 6bca7beb8e
37 changed files with 4318 additions and 0 deletions

324
src/excelHandler.ts Normal file
View File

@@ -0,0 +1,324 @@
import * as XLSX from 'xlsx';
export interface HardwareAsset {
id: string;
type: string; // '개인PC', '서버', '스토리지', '전산비품'
법인: string;
자산코드: string;
명칭: string;
위치: string;
관리자: string;
IP주소: string;
MACaddress: string;
HW사양: string;
OS: string;
사용자?: string;
CPU?: string;
GPU?: string;
RAM?: string;
SSD1?: string;
SSD2?: string;
HDD1?: string;
HDD2?: string;
storage유형?: string;
비품유형?: string;
모델명?: string;
용량?: string;
담당자_정?: string;
담당자_부?: string;
구매일?: string;
금액?: string;
납품업체?: string;
품의서명?: string;
}
export interface SoftwareAsset {
id: string;
type: string; // '구독SW', '영구SW'
법인: string;
제품명: string;
구매일: string;
구독일?: string;
유지보수여부?: boolean;
금액: string;
수량: number;
계정명: string;
납품업체: string;
비고: string;
}
export interface SWUser {
id: string;
swId: string;
법인: string;
부서: string;
: string;
직위: string;
이름: string;
사용기간: string;
신청서명: string;
}
export interface MasterAssetData {
hw: HardwareAsset[];
sw: SoftwareAsset[];
swUsers: SWUser[];
}
const HW_TABS = ['개인PC', '서버', '스토리지', '전산비품'];
const SW_TABS = ['구독SW', '영구SW'];
const HW_HEADERS = ['법인', '자산코드', '명칭', '위치', '관리자', 'IP주소', 'MACaddress', 'HW사양', 'OS', '구매일', '금액', '납품업체', '품의서명'];
const PC_HEADERS = ['법인', '자산코드', '사용자', '위치', 'CPU', 'GPU', 'RAM', 'SSD1', 'SSD2', 'HDD1', 'HDD2', '구매일', '금액', '납품업체', '품의서명'];
const STORAGE_HEADERS = ['법인', '유형', '자산코드', '명칭', '위치', '모델명', '용량', '담당자(정)', '담당자(부)', 'IP주소', 'MAC주소', '구매일', '금액', '납품업체', '품의서명'];
const SUB_SW_HEADERS = ['ID', '법인', '제품명', '구매일', '구독일', '금액', '수량', '계정명', '납품업체', '비고'];
const PERM_SW_HEADERS = ['ID', '법인', '제품명', '구매일', '유지보수여부', '금액', '수량', '계정명', '납품업체', '비고'];
const SW_USER_HEADERS = ['id', 'swId', '법인', '부서', '팀', '직위', '이름', '사용기간', '신청서명'];
/**
* 템플릿 엑셀 다중 시트로 다운로드
*/
export function downloadTemplate() {
const wb = XLSX.utils.book_new();
// HW 탭들 생성
HW_TABS.forEach(tab => {
if (tab === '개인PC') {
const ws = XLSX.utils.aoa_to_sheet([PC_HEADERS]);
ws['!cols'] = [{wch:15}, {wch:25}, {wch:15}, {wch:20}, {wch:20}, {wch:20}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:20}, {wch:25}];
XLSX.utils.book_append_sheet(wb, ws, tab);
} else if (tab === '스토리지') {
const ws = XLSX.utils.aoa_to_sheet([STORAGE_HEADERS]);
ws['!cols'] = [{wch:15}, {wch:15}, {wch:25}, {wch:25}, {wch:20}, {wch:25}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:25}];
XLSX.utils.book_append_sheet(wb, ws, tab);
} else {
const ws = XLSX.utils.aoa_to_sheet([HW_HEADERS]);
ws['!cols'] = [{wch:15}, {wch:20}, {wch:25}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:40}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:25}];
XLSX.utils.book_append_sheet(wb, ws, tab);
}
});
// SW 탭들 생성
SW_TABS.forEach(tab => {
let hd = tab === '구독SW' ? SUB_SW_HEADERS : PERM_SW_HEADERS;
const ws = XLSX.utils.aoa_to_sheet([hd]);
ws['!cols'] = [{wch:15}, {wch:15}, {wch:30}, {wch:15}, {wch:20}, {wch:15}, {wch:10}, {wch:20}, {wch:20}, {wch:30}];
XLSX.utils.book_append_sheet(wb, ws, tab);
});
const swUserWs = XLSX.utils.aoa_to_sheet([SW_USER_HEADERS]);
swUserWs['!cols'] = [{wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:20}, {wch:25}];
XLSX.utils.book_append_sheet(wb, swUserWs, 'SW_사용자');
XLSX.writeFile(wb, 'itam_assets_template.xlsx');
}
/**
* 마스터 데이터를 여러 시트로 쪼개서 내보내기
*/
export function exportToExcel(masterData: MasterAssetData) {
const wb = XLSX.utils.book_new();
// HW
HW_TABS.forEach(tab => {
const targetAssets = masterData.hw.filter(a => a.type === tab);
let wsData;
let colsConfig;
if (tab === '개인PC') {
wsData = [
PC_HEADERS,
...targetAssets.map(a => [a., a., a., a., a.CPU, a.GPU, a.RAM, a.SSD1, a.SSD2, a.HDD1, a.HDD2, a., a., a., a.])
];
colsConfig = [{wch:15}, {wch:25}, {wch:15}, {wch:20}, {wch:20}, {wch:20}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:20}, {wch:25}];
} else if (tab === '스토리지') {
wsData = [
STORAGE_HEADERS,
...targetAssets.map(a => [a., a.storage유형, a., a., a., a., a., a._정, a._부, a.IP주소, a.MACaddress, a., a., a., a.])
];
colsConfig = [{wch:15}, {wch:15}, {wch:25}, {wch:25}, {wch:20}, {wch:25}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:25}];
} else {
wsData = [
HW_HEADERS,
...targetAssets.map(a => [a., a., a., a., a., a.IP주소, a.MACaddress, a.HW사양, a.OS, a., a., a., a.])
];
colsConfig = [{wch:15}, {wch:20}, {wch:25}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:40}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:25}];
}
const ws = XLSX.utils.aoa_to_sheet(wsData);
ws['!cols'] = colsConfig;
XLSX.utils.book_append_sheet(wb, ws, tab);
});
// SW
SW_TABS.forEach(tab => {
const targetAssets = masterData.sw.filter(a => a.type === tab);
let wsData;
if (tab === '구독SW') {
wsData = [
SUB_SW_HEADERS,
...targetAssets.map(a => [a.id, a., a., a., a., a., a., a., a., a.])
];
} else {
wsData = [
PERM_SW_HEADERS,
...targetAssets.map(a => [a.id, a., a., a., a. ? 'Y' : 'N', a., a., a., a., a.])
];
}
const ws = XLSX.utils.aoa_to_sheet(wsData);
ws['!cols'] = [{wch:15}, {wch:15}, {wch:30}, {wch:15}, {wch:20}, {wch:15}, {wch:10}, {wch:20}, {wch:20}, {wch:30}];
XLSX.utils.book_append_sheet(wb, ws, tab);
});
// SW_사용자
const swUserWsData = [
SW_USER_HEADERS,
...masterData.swUsers.map(u => [u.id, u.swId, u., u., u., u., u., u., u.])
];
const swUserWs = XLSX.utils.aoa_to_sheet(swUserWsData);
swUserWs['!cols'] = [{wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:20}, {wch:25}];
XLSX.utils.book_append_sheet(wb, swUserWs, 'SW_사용자');
const dateStr = new Date().toISOString().split('T')[0];
XLSX.writeFile(wb, `itam_assets_master_${dateStr}.xlsx`);
}
/**
* 업로드된 다중 시트 엑셀을 파싱하여 Master Data 구성
*/
export async function parseExcel(file: File): Promise<MasterAssetData> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
try {
const data = e.target?.result;
const workbook = XLSX.read(data, { type: 'binary' });
const hwAssets: HardwareAsset[] = [];
const swAssets: SoftwareAsset[] = [];
const swUsers: SWUser[] = [];
workbook.SheetNames.forEach(sheetName => {
const worksheet = workbook.Sheets[sheetName];
const json = XLSX.utils.sheet_to_json(worksheet) as any[];
if (HW_TABS.includes(sheetName)) {
json.forEach(row => {
if (sheetName === '개인PC') {
hwAssets.push({
id: Math.random().toString(36).substring(2, 9),
type: sheetName,
법인: row['법인'] || '',
자산코드: row['자산코드'] || '',
: '', // 개인PC는 명칭 미사용
위치: row['위치'] || '',
사용자: row['사용자'] || '',
: '',
IP주소: '',
MACaddress: '',
HW사양: '',
OS: '',
CPU: row['CPU'] || '',
GPU: row['GPU'] || '',
RAM: row['RAM'] || '',
SSD1: row['SSD1'] || '',
SSD2: row['SSD2'] || '',
HDD1: row['HDD1'] || '',
HDD2: row['HDD2'] || '',
구매일: row['구매일'] || '',
금액: row['금액'] ? String(row['금액']) : '',
납품업체: row['납품업체'] || '',
품의서명: row['품의서명'] || '',
});
} else if (sheetName === '스토리지') {
hwAssets.push({
id: Math.random().toString(36).substring(2, 9),
type: sheetName,
법인: row['법인'] || '',
자산코드: row['자산코드'] || '',
명칭: row['명칭'] || '',
위치: row['위치'] || '',
: '',
IP주소: row['IP주소'] || '',
MACaddress: row['MAC주소'] || row['MACaddress'] || row['MAC address'] || '',
HW사양: '',
OS: '',
storage유형: row['유형'] || '',
모델명: row['모델명'] || '',
용량: row['용량'] || '',
담당자_정: row['담당자(정)'] || '',
담당자_부: row['담당자(부)'] || '',
구매일: row['구매일'] || '',
금액: row['금액'] ? String(row['금액']) : '',
납품업체: row['납품업체'] || '',
품의서명: row['품의서명'] || '',
});
} else {
hwAssets.push({
id: Math.random().toString(36).substring(2, 9),
type: sheetName,
법인: row['법인'] || '',
자산코드: row['자산코드'] || '',
명칭: row['명칭'] || '',
위치: row['위치'] || '',
관리자: row['관리자'] || '',
IP주소: row['IP주소'] || '',
MACaddress: row['MACaddress'] || row['MAC address'] || '',
HW사양: row['HW사양'] || row['H/W 사양'] || '',
OS: row['OS'] || '',
구매일: row['구매일'] || '',
금액: row['금액'] ? String(row['금액']) : '',
납품업체: row['납품업체'] || '',
품의서명: row['품의서명'] || '',
});
}
});
}
if (SW_TABS.includes(sheetName)) {
json.forEach(row => {
swAssets.push({
id: row['ID'] ? String(row['ID']) : Math.random().toString(36).substring(2, 9),
type: sheetName,
법인: row['법인'] || '',
제품명: row['제품명'] || '',
구매일: row['구매일'] || '',
구독일: row['구독일'] || '',
유지보수여부: row['유지보수여부'] === 'Y' || row['유지보수여부'] === true,
금액: row['금액'] ? String(row['금액']) : '',
수량: parseInt(row['수량'] || '1', 10),
계정명: row['계정명'] || '',
납품업체: row['납품업체'] || '',
비고: row['비고'] || '',
});
});
}
if (sheetName === 'SW_사용자') {
json.forEach(row => {
swUsers.push({
id: row['id'] ? String(row['id']) : Math.random().toString(36).substring(2, 9),
swId: row['swId'] ? String(row['swId']) : '',
법인: row['법인'] || '',
부서: row['부서'] || '',
: row['팀'] || '',
직위: row['직위'] || '',
이름: row['이름'] || '',
사용기간: row['사용기간'] || '',
신청서명: row['신청서명'] || '',
});
});
}
});
resolve({ hw: hwAssets, sw: swAssets, swUsers });
} catch (err) {
reject(err);
}
};
reader.onerror = (err) => reject(err);
reader.readAsBinaryString(file);
});
}