chore: update Dockerfile
This commit is contained in:
parent
c11fed9832
commit
d2c47ac15f
4 changed files with 779 additions and 6 deletions
319
jws-import-data.ts
Normal file
319
jws-import-data.ts
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
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 = {
|
||||
jobNo: string;
|
||||
class: string;
|
||||
authorizedName: string;
|
||||
customerNameEN: string;
|
||||
customerName: string;
|
||||
fullAddress: string;
|
||||
fullAddressEN: string;
|
||||
jobDescription: string;
|
||||
businessTypeTH: string;
|
||||
businessType: string;
|
||||
citizenId: string;
|
||||
registerDate: string;
|
||||
legalPersonNo: string;
|
||||
authorizedCapital: string;
|
||||
jobPositionTH: string;
|
||||
jobPosition: string;
|
||||
homeCode: string;
|
||||
address: string;
|
||||
addressEN: string;
|
||||
moo: string;
|
||||
mooEN: string;
|
||||
soi: string;
|
||||
soiEN: string;
|
||||
street: string;
|
||||
streetEN: string;
|
||||
subDistrict: string;
|
||||
subDistrictEN: string;
|
||||
district: string;
|
||||
districtEN: string;
|
||||
province: string;
|
||||
provinceEN: string;
|
||||
zipCode: string;
|
||||
telephoneNo: string;
|
||||
employmentOffice: string;
|
||||
employmentOfficeEN: string;
|
||||
payDate: string;
|
||||
payDateEN: string;
|
||||
wageRate: string;
|
||||
contactName: string;
|
||||
contactTel: string;
|
||||
};
|
||||
|
||||
async function importCsv(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);
|
||||
}
|
||||
|
||||
// 1) Group by (authorizedName + address) => key = authorizedName|subDistrict|district|province
|
||||
const comboMap = new Map<string, CsvRow>();
|
||||
for (const row of rows) {
|
||||
const sub = (row.subDistrict || "").trim();
|
||||
const dist = (row.district || "").trim();
|
||||
const prov = (row.province || "").trim();
|
||||
const auth = (row.authorizedName || "").trim();
|
||||
|
||||
if (!auth || !sub || !dist || !prov) continue; // ข้ามบรรทัดถ้าขาดข้อมูลสำคัญ
|
||||
|
||||
const comboKey = `${auth}|${sub}|${dist}|${prov}`;
|
||||
if (!comboMap.has(comboKey)) {
|
||||
comboMap.set(comboKey, row); // เก็บแค่รายการแรกของ combo นี้
|
||||
}
|
||||
}
|
||||
|
||||
const groupedRows = Array.from(comboMap.values()); // รายการ unique (authorizedName+address)
|
||||
|
||||
// 2) สร้าง set ของ unique address keys (subDistrict|district|province) จาก groupedRows
|
||||
const uniqueAddrKeys = [
|
||||
...new Set(
|
||||
groupedRows.map((r) => `${r.subDistrict.trim()}|${r.district.trim()}|${r.province.trim()}`),
|
||||
),
|
||||
];
|
||||
|
||||
// 3) ดึงชื่อ subDistrict เพื่อ query DB (query ทีเดียว)
|
||||
const subDistrictNames = [...new Set(groupedRows.map((r) => r.subDistrict.trim()))];
|
||||
|
||||
// 4) Query DB หา subDistrict (รวม district + province) แล้ว filter ให้ตรงทั้ง 3 ชั้น
|
||||
const searchAddr = await prisma.subDistrict.findMany({
|
||||
where: { name: { in: subDistrictNames } },
|
||||
include: {
|
||||
district: {
|
||||
include: { province: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 5) สร้าง map: key = "subDistrict|district|province" -> ids
|
||||
const allAddrList = new Map<
|
||||
string,
|
||||
{ subDistrictId: string; districtId: string; provinceId: string; zipCode?: string }
|
||||
>();
|
||||
|
||||
for (const s of searchAddr) {
|
||||
const key = `${s.name.trim()}|${s.district.name.trim()}|${s.district.province.name.trim()}`;
|
||||
if (uniqueAddrKeys.includes(key)) {
|
||||
allAddrList.set(key, {
|
||||
subDistrictId: s.id,
|
||||
districtId: s.districtId,
|
||||
provinceId: s.district.provinceId,
|
||||
zipCode: s.zipCode || undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 6) ตอนนี้ groupedRows คือ list ของ authorizedName+address ที่ไม่ซ้ำกัน (ตาม combo)
|
||||
// และ allAddrList ให้ mapping จากชื่อ -> ids
|
||||
// ตัวอย่าง: สรุปผล (หรือเอาไป process ต่อ เช่น สร้าง Customer ฯล.)
|
||||
const resultList = groupedRows.map((r) => {
|
||||
const addrKey = `${r.subDistrict.trim()}|${r.district.trim()}|${r.province.trim()}`;
|
||||
const ids = allAddrList.get(addrKey) || null;
|
||||
return {
|
||||
authorizedName: r.authorizedName,
|
||||
addressKey: addrKey,
|
||||
addrIds: ids,
|
||||
raw: r,
|
||||
};
|
||||
});
|
||||
|
||||
console.log(`Grouped combos: ${resultList.length}`);
|
||||
|
||||
return saveToDb(resultList, allAddrList);
|
||||
}
|
||||
|
||||
async function saveToDb(resultList: any[], allAddrList: Map<string, any>) {
|
||||
for (const item of resultList) {
|
||||
const row = item.raw;
|
||||
|
||||
// แปลง class -> customerType
|
||||
let customerType: CustomerType;
|
||||
if (row.class === "นายจ้าง (นิติบุคคล)") customerType = CustomerType.CORP;
|
||||
else if (row.class === "นายจ้าง (บุคคลธรรมดา)") customerType = CustomerType.PERS;
|
||||
else customerType = CustomerType.PERS; // default ถ้าไม่แมทช์
|
||||
|
||||
const registerBranch = await prisma.branch.findFirst({
|
||||
where: { id: "cmfywjxk0003hqm1xybqwe48t" },
|
||||
});
|
||||
|
||||
if (registerBranch) {
|
||||
await prisma.branch.updateMany({
|
||||
where: {
|
||||
id: registerBranch.id,
|
||||
status: "CREATED",
|
||||
},
|
||||
data: {
|
||||
status: "INACTIVE",
|
||||
statusOrder: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const branch = [
|
||||
{
|
||||
legalPersonNo: "1234567890123",
|
||||
namePrefix: "",
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
firstNameEN: "",
|
||||
lastNameEN: "",
|
||||
telephoneNo: "",
|
||||
gender: "",
|
||||
businessTypeId: "cmfp1dvrt0009ph1x6qk21l1s",
|
||||
jobPosition: "domesticHelper",
|
||||
jobDescription: "",
|
||||
payDate: "",
|
||||
payDateEN: "",
|
||||
wageRate: 0,
|
||||
wageRateText: "",
|
||||
homeCode: "",
|
||||
employmentOffice: "",
|
||||
employmentOfficeEN: "",
|
||||
address: "123",
|
||||
addressEN: "123",
|
||||
street: "",
|
||||
streetEN: "",
|
||||
moo: "",
|
||||
mooEN: "",
|
||||
soi: "",
|
||||
soiEN: "",
|
||||
provinceId: "40",
|
||||
districtId: "4010",
|
||||
subDistrictId: "401005",
|
||||
contactName: "",
|
||||
email: "",
|
||||
contactTel: "",
|
||||
officeTel: "",
|
||||
status: "CREATED",
|
||||
registerName: "ทดสอบ",
|
||||
registerNameEN: "Opaspong",
|
||||
authorizedCapital: "",
|
||||
authorizedName: "",
|
||||
authorizedNameEN: "",
|
||||
},
|
||||
];
|
||||
const company = registerBranch.code;
|
||||
const headoffice = branch[0];
|
||||
|
||||
if (!headoffice) {
|
||||
throw new HttpError(
|
||||
HttpStatus.BAD_REQUEST,
|
||||
"Require at least one branch as headoffice",
|
||||
"requireOneMinBranch",
|
||||
);
|
||||
}
|
||||
|
||||
const runningKey = `CUSTOMER_BRANCH_${company}_${"citizenId" in headoffice ? headoffice.citizenId : headoffice.legalPersonNo}`;
|
||||
|
||||
const last = await prisma.runningNo.upsert({
|
||||
where: { key: runningKey },
|
||||
create: {
|
||||
key: runningKey,
|
||||
value: branch.length,
|
||||
},
|
||||
update: { value: { increment: branch.length } },
|
||||
});
|
||||
|
||||
// ✅ Customer
|
||||
const customer = await prisma.customer.create({
|
||||
data: {
|
||||
customerType,
|
||||
status: Status.CREATED,
|
||||
statusOrder: 0,
|
||||
registeredBranch: { connect: { id: registerBranch.id } },
|
||||
},
|
||||
});
|
||||
|
||||
// ✅ Address mapping
|
||||
const addrKey = `${row.subDistrict.trim()}|${row.district.trim()}|${row.province.trim()}`;
|
||||
const addr = allAddrList.get(addrKey);
|
||||
|
||||
// ✅ CustomerBranch
|
||||
await prisma.customerBranch.create({
|
||||
data: {
|
||||
customerId: customer.id,
|
||||
code: `${runningKey.replace(`CUSTOMER_BRANCH_${company}_`, "")}-${`${last.value - branch.length + 1}`.padStart(2, "0")}`,
|
||||
codeCustomer: runningKey.replace(`CUSTOMER_BRANCH_${company}_`, ""),
|
||||
telephoneNo: row.telephoneNo || "-",
|
||||
|
||||
// บุคคลธรรมดา
|
||||
citizenId: row.citizenId || null,
|
||||
namePrefix: row.namePrefix || null,
|
||||
firstName: row.firstname || null,
|
||||
firstNameEN: row.firstnameEN || null,
|
||||
lastName: row.firstname || null,
|
||||
lastNameEN: row.firstnameEN || null,
|
||||
authorizedName: row.authorizedName || null,
|
||||
authorizedNameEN: row.authorizedNameEN || null,
|
||||
|
||||
// นิติบุคคล
|
||||
legalPersonNo: row.legalPersonNo || null,
|
||||
registerName: row.registerName || null,
|
||||
registerNameEN: row.registerNameEN || null,
|
||||
registerDate: parseBuddhistDate(row.registerDate),
|
||||
authorizedCapital: row.authorizedCapital || null,
|
||||
|
||||
// ที่อยู่
|
||||
homeCode: row.homeCode || "-",
|
||||
employmentOffice: row.employmentOffice || "-",
|
||||
employmentOfficeEN: row.employmentOfficeEN || "-",
|
||||
address: row.address || "-",
|
||||
addressEN: row.addressEN || "-",
|
||||
soi: row.soi || null,
|
||||
soiEN: row.soiEN || null,
|
||||
moo: row.moo || null,
|
||||
mooEN: row.mooEN || null,
|
||||
street: row.street || null,
|
||||
streetEN: row.streetEN || null,
|
||||
provinceId: addr?.provinceId,
|
||||
districtId: addr?.districtId,
|
||||
subDistrictId: addr?.subDistrictId,
|
||||
|
||||
// Contact
|
||||
email: row.email || "-",
|
||||
contactTel: row.contactTel || "-",
|
||||
officeTel: row.officeTel || "-",
|
||||
contactName: row.contactName || "-",
|
||||
|
||||
// Job
|
||||
jobPosition: row.jobPosition || "-",
|
||||
jobDescription: row.jobDescription || "-",
|
||||
payDate: row.payDate || "-",
|
||||
payDateEN: row.payDateEN || "-",
|
||||
wageRate: parseInt(row.wageRate || "0", 10),
|
||||
wageRateText: "-",
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`✅ Saved customer ${customer.id}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseBuddhistDate(csvDate: string): Date {
|
||||
const [day, monthStr, yearStr] = csvDate.split("-");
|
||||
const dayNum = parseInt(day, 10);
|
||||
const monthNum = new Date(`${monthStr} 1, 2000`).getMonth();
|
||||
const yearBE = parseInt(yearStr, 10);
|
||||
const yearCE = yearBE + 2500 - 543;
|
||||
return new Date(yearCE, monthNum, dayNum);
|
||||
}
|
||||
|
||||
importCsv("/home/hamu/Downloads/JWS - import customer template.csv")
|
||||
.then(() => console.log("Import finished ✅"))
|
||||
.catch((err) => {
|
||||
console.error("Import failed:", err);
|
||||
process.exit(1);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue