feat: add bulk import endpoint

This commit is contained in:
2025-11-21 16:55:01 +05:30
parent bb51d62bed
commit f1b6f48a51
6 changed files with 148 additions and 0 deletions

View File

@@ -1,6 +1,7 @@
import { FastifyReply, FastifyRequest } from "fastify";
import { CreatePermitInput, UpdatePermitInput } from "./permit.schema";
import {
bulkImport,
createPermit,
deletePermit,
getPermit,
@@ -10,6 +11,7 @@ import {
updatePermit,
} from "./permit.service";
import { PageQueryParams } from "../pagination";
import { csv2json } from "json-2-csv";
export async function createPermitHandler(
req: FastifyRequest,
@@ -117,3 +119,20 @@ export async function searchPermitByAddressHandler(
return err;
}
}
export async function bulkImportHandler(
req: FastifyRequest,
res: FastifyReply
) {
try {
const data = await req.file();
const csvString = (await data.toBuffer()).toString("utf-8");
const parsedCSV = csv2json(csvString, { delimiter: { eol: "\r\n" } });
const result = await bulkImport(parsedCSV, req.user);
return res.code(200).send(result);
} catch (err) {
return err;
}
}

View File

@@ -1,5 +1,6 @@
import { FastifyInstance } from "fastify";
import {
bulkImportHandler,
createPermitHandler,
deletePermitHandler,
getPermitHandler,
@@ -138,6 +139,15 @@ export async function permitRoutes(fastify: FastifyInstance) {
}
);
fastify.post(
"/bulkImport",
{
config: { requiredClaims: ["permit:write"] },
preHandler: [fastify.authorize],
},
bulkImportHandler
);
await noteRoutes(fastify);
fastify.addHook("onSend", hideFields("permits"));

View File

@@ -110,6 +110,7 @@ const permitSchema = new mongoose.Schema({
relationship: String,
type_text: String,
},
importFlag: Boolean,
});
permitSchema.index({ tenantId: 1, permitNumber: 1 }, { unique: true });

View File

@@ -518,3 +518,96 @@ export async function searchPermitByAddress(address: string) {
.sort({ score: { $meta: "textScore" } })
.limit(1);
}
export async function bulkImport(csvData: Object[], user: AuthenticatedUser) {
const allowedFields = [
"Permit Number",
"County",
"Client",
"Address",
"Open Date",
"County Status",
"Record Type",
"Lot",
"Block",
"Job Number",
"Community Name",
];
const failed = [];
const created = [];
for (const [index, record] of csvData.entries()) {
try {
if (!record["Permit Number"]) {
failed.push(index);
continue;
}
const permitInDb = await permitModel.findOne({
permitNumber: record["Permit Number"],
});
if (!permitInDb) {
let clientData = null;
let countyData = null;
if (record["Client"]) {
const clientInDb = await orgModel.findOne({ name: record["Client"] });
if (clientInDb) {
clientData = {
id: clientInDb._id,
pid: clientInDb.pid,
licenseNumber: clientInDb.licenseNumber,
name: clientInDb.name,
avatar: clientInDb.avatar,
};
}
}
if (record["County"]) {
const countyInDb = await orgModel.findOne({ name: record["County"] });
if (countyInDb) {
countyData = {
id: countyInDb._id,
pid: countyInDb.pid,
name: countyInDb.name,
avatar: countyInDb.avatar,
};
}
}
const newPermit = await permitModel.create({
tenantId: user.tenantId,
pid: generateId(),
permitNumber: record["Permit Number"],
county: countyData,
client: clientData?.id,
clientData: clientData,
cleanStatus: record["County Status"],
address: record["Address"],
recordType: record["Record Type"],
lot: record["Lot"],
block: record["Block"],
jobNumber: record["Job Number"],
communityName: record["Community Name"],
createdAt: new Date(),
createdBy: user.userId,
importFlag: true,
});
const populatedPermit = await newPermit.populate({
path: "assignedTo createdBy",
select: "pid name avatar",
});
created.push(populatedPermit);
}
} catch (err) {
console.log(err);
failed.push(index + 2);
}
}
return { created, failed, allowedFields };
}