import { parse } from "csv-parse"; import fs from "fs"; import HttpStatus from "./src/interfaces/http-status"; import HttpError from "./src/interfaces/http-error"; import { PrismaClient } from "@prisma/client"; import { CustomerType, Status } from "@prisma/client"; // enum จาก Prisma const prisma = new PrismaClient({ datasourceUrl: process.env.TEST_DATABASE_URL || process.env.DATABASE_URL, }); type CsvRow = { workerId: string; fullName: string; firstName: string; gender: string; dateOfBirth: string; age: string; ppBirthCountry: string; nationality: string; ppNumber: string; ppIssuePlace: string; ppIssueCountry: string; ppIssueDate: string; ppExpireDate: string; visaNumber: string; visaIssuePlace: string; visaType: string; visaIssueDate: string; visaExpireDate: string; visaArrivalTM: string; visaArrivalAt: string; visaArrivalTMNo: string; visaReportDate: string; identityNo: string; workPermitNo: string; nameListNo: string; nrcNo: string; nameOfId: string; fatherFirstNameEN: string; jobNo: string; companyFullName: string; companyFullNameEN: string; address: string; jobDescription: string; contactName: string; contactTel: string; }; type UniqueCompany = { companyFullName: string; address: string; jobDescription: string; }; async function importEmployeeCsv(filePath: string) { const parser = fs.createReadStream(filePath).pipe(parse({ columns: true, trim: true })); const rows: CsvRow[] = []; for await (const row of parser) { rows.push(row as CsvRow); } const uniqueMap = new Map(); for (const row of rows) { const key = `${row.companyFullName}|${row.address}|${row.jobDescription}`; if (!uniqueMap.has(key)) { uniqueMap.set(key, { companyFullName: row.companyFullName, address: row.address, jobDescription: row.jobDescription, }); } } const uniqueCompanies: UniqueCompany[] = Array.from(uniqueMap.values()); console.log("Unique Companies:", uniqueCompanies); const getCustomerBranches = await prisma.customerBranch.findMany({ where: { OR: uniqueCompanies.map((c) => ({ AND: [{ authorizedName: c.companyFullName }], })), }, }); if (getCustomerBranches.length > 0) { // map สำหรับ lookup customerBranchId const branchMap = new Map(); getCustomerBranches.forEach((branch) => { branchMap.set(branch.authorizedName || "", branch); }); await prisma.$transaction(async (tx) => { for (const row of rows) { const branch = branchMap.get(row.companyFullName); if (!branch) continue; const gender = row.gender?.toLowerCase(); const addressParts = parseThaiAddress(row.address); // โหลดทั้งหมดจาก DB const provinces = await prisma.province.findMany(); const districts = await prisma.district.findMany(); const subDistricts = await prisma.subDistrict.findMany(); // สร้าง map สำหรับ lookup const provinceMap = new Map(); provinces.forEach((p) => provinceMap.set(p.name, p.id)); const districtMap = new Map(); districts.forEach((d) => districtMap.set(d.name, d.id)); const subDistrictMap = new Map(); subDistricts.forEach((s) => subDistrictMap.set(s.name, s.id)); const provinceId = provinceMap.get(addressParts.province ?? "") ?? null; const districtId = districtMap.get(addressParts.district ?? "") ?? null; const subDistrictId = subDistrictMap.get(addressParts.subDistrict ?? "") ?? null; const last = await tx.runningNo.upsert({ where: { key: `EMPLOYEE_${branch.id}-${`${new Date().getFullYear()}`.slice(-2).padStart(2, "0")}`, }, create: { key: `EMPLOYEE_${branch.id}-${`${new Date().getFullYear()}`.slice(-2).padStart(2, "0")}`, value: 1, }, update: { value: { increment: 1 } }, }); // 1. Employee const employee = await tx.employee.create({ data: { code: `${branch.code}-${`${new Date().getFullYear()}`.slice(-2).padStart(2, "0")}${`${last.value}`.padStart(7, "0")}`, customerBranchId: branch.id, firstName: row.firstName, firstNameEN: "", gender: gender, dateOfBirth: row.dateOfBirth ? new Date(row.dateOfBirth) : null, nationality: row.nationality, nrcNo: row.nrcNo, workerStatus: null, status: Status.CREATED, address: addressParts.houseNo, street: addressParts.street, soi: addressParts.soi, moo: addressParts.moo, provinceId: provinceId, districtId: districtId, subDistrictId: subDistrictId, }, }); // 2. EmployeeOtherInfo await tx.employeeOtherInfo.create({ data: { employeeId: employee.id, citizenId: row.identityNo, fatherFirstNameEN: row.fatherFirstNameEN, }, }); // 3. EmployeePassport await tx.employeePassport.create({ data: { employeeId: employee.id, number: row.ppNumber, type: "PASSPORT", issueDate: row.ppIssueDate ? new Date(row.ppIssueDate) : "", expireDate: row.ppExpireDate ? new Date(row.ppExpireDate) : "", issueCountry: row.ppIssueCountry, issuePlace: row.ppIssuePlace, firstName: row.firstName, firstNameEN: "", gender: gender, birthCountry: row.ppBirthCountry, birthDate: row.dateOfBirth, nationality: row.nationality, workerStatus: "normal", }, }); // 4. EmployeeVisa await tx.employeeVisa.create({ data: { employeeId: employee.id, number: row.visaNumber, type: row.visaType, entryCount: 1, issuePlace: row.visaIssuePlace, issueCountry: "", issueDate: row.visaIssueDate ? new Date(row.visaIssueDate) : "", expireDate: row.visaExpireDate ? new Date(row.visaExpireDate) : "", reportDate: row.visaReportDate ? new Date(row.visaReportDate) : undefined, arrivalTM: row.visaArrivalTM, arrivalTMNo: row.visaArrivalTMNo, arrivalAt: row.visaArrivalAt, }, }); } }); console.log("Import completed ✅"); } else { console.warn("No customer branches found for the CSV companies."); } } function parseThaiAddress(address: string) { const regex = /^(?[\d\-\/]+)?\s*(?:หมู่\s*(?\d+))?\s*(?:ซอย\s*(?[^ ]+))?\s*(?.*?)\s*(?:ตำบล|แขวง)?\s*(?[^ ]+)?\s*(?:อำเภอ|เขต)?\s*(?[^ ]+)?\s*จังหวัด\s*(?[^ ]+)?\s*(?\d{5})?$/; const match = address.match(regex); if (!match || !match.groups) return {}; return { houseNo: match.groups.houseNo?.trim(), moo: match.groups.moo?.trim(), soi: match.groups.soi?.trim(), street: match.groups.street?.trim(), subDistrict: match.groups.subDistrict?.trim(), district: match.groups.district?.trim(), province: match.groups.province?.trim(), postalCode: match.groups.postalCode?.trim(), }; } importEmployeeCsv("/home/hamu/Downloads/JWS - import employee template example 3.csv") .then(() => console.log("Import finished ✅")) .catch((err) => { console.error("Import failed:", err); process.exit(1); });