jws-backend/jws-import-employee-data.ts
2026-01-12 14:47:19 +07:00

235 lines
7.7 KiB
TypeScript

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<string, UniqueCompany>();
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<string, (typeof getCustomerBranches)[0]>();
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<string, string>();
provinces.forEach((p) => provinceMap.set(p.name, p.id));
const districtMap = new Map<string, string>();
districts.forEach((d) => districtMap.set(d.name, d.id));
const subDistrictMap = new Map<string, string>();
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 =
/^(?<houseNo>[\d\-\/]+)?\s*(?:หมู่\s*(?<moo>\d+))?\s*(?:ซอย\s*(?<soi>[^ ]+))?\s*(?<street>.*?)\s*(?:ตำบล|แขวง)?\s*(?<subDistrict>[^ ]+)?\s*(?:อำเภอ|เขต)?\s*(?<district>[^ ]+)?\s*จังหวัด\s*(?<province>[^ ]+)?\s*(?<postalCode>\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);
});