forked from Yara724/api
YARA-1244
This commit is contained in:
@@ -2,143 +2,13 @@
|
||||
"clientCode": 8,
|
||||
"branches": [
|
||||
{
|
||||
"code": "100100",
|
||||
"name": "واحدصدورالکترونيکي",
|
||||
"fullName": "واحدصدورالکترونيکي(100100)",
|
||||
"code": "210120",
|
||||
"name": "شعبه والفجر",
|
||||
"fullName": "شعبه والفجر(210120)",
|
||||
"city": "تهران",
|
||||
"state": "تهران",
|
||||
"address": "خيابان وليعصر_بلوار ميرداماد_پلاک 22",
|
||||
"phoneNumber": "8259",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "110011",
|
||||
"name": "ستاد مرکزي",
|
||||
"fullName": "ستاد مرکزي(110011)",
|
||||
"city": "تهران",
|
||||
"state": "تهران",
|
||||
"address": "تهران، خيابان وليعصر، بالاتراز ميرداماد، خيابان قباديان غربي، پلاك22",
|
||||
"phoneNumber": "8259",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "111130",
|
||||
"name": "شعبه ويژه ميرداماد",
|
||||
"fullName": "شعبه ويژه ميرداماد(111130)",
|
||||
"city": "تهران",
|
||||
"state": "تهران",
|
||||
"address": "تهران، خيابان وليعصر، بالاتراز ميرداماد، خيابان قباديان غربي، پلاك22",
|
||||
"phoneNumber": "8259",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "120021",
|
||||
"name": "سرپرستي منطقه يک كشور",
|
||||
"fullName": "سرپرستي منطقه يک كشور(120021)",
|
||||
"city": "تهران",
|
||||
"state": "تهران",
|
||||
"address": "تهران،خيابان وليعصر ،خيابان قباديان غربي ،پلاک 22 ، طبقه همکف",
|
||||
"phoneNumber": "0218259",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "130031",
|
||||
"name": "سرپرستي منطقه مركزي كشور",
|
||||
"fullName": "سرپرستي منطقه مركزي كشور(130031)",
|
||||
"city": "تهران",
|
||||
"state": "تهران",
|
||||
"address": "اصفهان، خيابان امام خميني (ره) - بعد از چهارراه شريف - کوچه شهيد احمدي (85)",
|
||||
"phoneNumber": "03133328257",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "140041",
|
||||
"name": "سرپرستي منطقه شمالغرب کشور",
|
||||
"fullName": "سرپرستي منطقه شمالغرب کشور(140041)",
|
||||
"city": "تهران",
|
||||
"state": "تهران",
|
||||
"address": "تبريز- خيابان ائل گلي - فلکه خيام - نبش فلکه رجائي - بيمه پارسيان",
|
||||
"phoneNumber": "04133832289",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "150051",
|
||||
"name": "سرپرستي منطقه جنوب كشور",
|
||||
"fullName": "سرپرستي منطقه جنوب كشور(150051)",
|
||||
"city": "تهران",
|
||||
"state": "تهران",
|
||||
"address": "شيراز ـ فلکه فرودگاه (ميدان بسيج) ـ ابتداي بلوار سياحتگر",
|
||||
"phoneNumber": "01738315473",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "150053",
|
||||
"name": "سرپرست منطقه جنوب شرقي کشور",
|
||||
"fullName": "سرپرست منطقه جنوب شرقي کشور(150053)",
|
||||
"city": "کرمان",
|
||||
"state": "کرمان",
|
||||
"address": "کرمان، حافظ، بعد از چهارراه جامي، پلاک 153",
|
||||
"phoneNumber": "03432718000",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "160061",
|
||||
"name": "سرپرستي منطقه شرق كشور",
|
||||
"fullName": "سرپرستي منطقه شرق كشور(160061)",
|
||||
"city": "تهران",
|
||||
"state": "تهران",
|
||||
"address": "مشهد ـ خيام شمالي ـ نبش خيام شمالي 36",
|
||||
"phoneNumber": "05137659005",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "170071",
|
||||
"name": "سرپرستي منطقه غرب كشور",
|
||||
"fullName": "سرپرستي منطقه غرب كشور(170071)",
|
||||
"city": "تهران",
|
||||
"state": "تهران",
|
||||
"address": "کرمانشاه . ميدان مرکزي خيابان خرم نبش کوي بسيج ساختمان عرفان",
|
||||
"phoneNumber": "08338431017",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "180081",
|
||||
"name": "سرپرستي منطقه شمال شرق کشور",
|
||||
"fullName": "سرپرستي منطقه شمال شرق کشور(180081)",
|
||||
"city": "ساري",
|
||||
"state": "مازندران",
|
||||
"address": "ساري، شعبه ساري",
|
||||
"phoneNumber": "01133207241",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "180083",
|
||||
"name": "سرپرست منطقه شمال کشوري",
|
||||
"fullName": "سرپرست منطقه شمال کشوري(180083)",
|
||||
"city": "رشت",
|
||||
"state": "گيلان",
|
||||
"address": "رشت، بلوار آيت اله رودباري،کدپستي:4144761893",
|
||||
"phoneNumber": "01333512135",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "190092",
|
||||
"name": "سرپرستي جنوب غربي کشور",
|
||||
"fullName": "سرپرستي جنوب غربي کشور(190092)",
|
||||
"city": "اهواز",
|
||||
"state": "خوزستان",
|
||||
"address": "اهواز،تقاطع بلوار ساحلي گلستان (خيابان فروردين)،نبش خيابان نصرت شمالي ،پلاک 701 کد پستي 6155977139",
|
||||
"phoneNumber": "06133743877",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "210040",
|
||||
"name": "شعبه شرق تهران",
|
||||
"fullName": "شعبه شرق تهران(210040)",
|
||||
"city": "تهران",
|
||||
"state": "تهران",
|
||||
"address": "تهران، خيابان دماوند، بعداز چهارراه تهرانپارس، روبروي تعميرگاه مرکزي شماره يک سايپا، پلاک129",
|
||||
"phoneNumber": "77393783-4",
|
||||
"address": "تهران، اميرآبادشمالي، شهرک والفجر، ضلع جنوب غربي ميدان استادخسرو سينايي",
|
||||
"phoneNumber": "86051332",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
@@ -151,6 +21,26 @@
|
||||
"phoneNumber": "66021968",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "110011",
|
||||
"name": "ستاد مرکزي",
|
||||
"fullName": "ستاد مرکزي(110011)",
|
||||
"city": "تهران",
|
||||
"state": "تهران",
|
||||
"address": "تهران، خيابان وليعصر، بالاتراز ميرداماد، خيابان قباديان غربي، پلاك22",
|
||||
"phoneNumber": "8259",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "210040",
|
||||
"name": "شعبه شرق تهران",
|
||||
"fullName": "شعبه شرق تهران(210040)",
|
||||
"city": "تهران",
|
||||
"state": "تهران",
|
||||
"address": "تهران، خيابان دماوند، بعداز چهارراه تهرانپارس، روبروي تعميرگاه مرکزي شماره يک سايپا، پلاک129",
|
||||
"phoneNumber": "77393783-4",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "210110",
|
||||
"name": "شعبه پونک",
|
||||
@@ -162,24 +52,14 @@
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "210120",
|
||||
"name": "شعبه والفجر",
|
||||
"fullName": "شعبه والفجر(210120)",
|
||||
"code": "111130",
|
||||
"name": "شعبه ويژه ميرداماد",
|
||||
"fullName": "شعبه ويژه ميرداماد(111130)",
|
||||
"city": "تهران",
|
||||
"state": "تهران",
|
||||
"address": "تهران، اميرآبادشمالي، شهرک والفجر، ضلع جنوب غربي ميدان استادخسرو سينايي",
|
||||
"phoneNumber": "86051332",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"code": "210150",
|
||||
"name": "شعبه شمال شرق تهران",
|
||||
"fullName": "شعبه شمال شرق تهران(210150)",
|
||||
"city": "تهران",
|
||||
"state": "تهران",
|
||||
"address": "تهران، ضلع شمال غربي ميدان بني هاشم، نبش خيابان كشوري، پلاك13",
|
||||
"phoneNumber": "26244319",
|
||||
"address": "تهران، خيابان وليعصر، بالاتراز ميرداماد، خيابان قباديان غربي، پلاك22",
|
||||
"phoneNumber": "8259",
|
||||
"isActive": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
/**
|
||||
* One-time seed for Parsian (clientCode=8) Tehran branches + field experts.
|
||||
* One-time seed for Parsian (clientCode=8) Tehran branches + file reviewers + file makers.
|
||||
*
|
||||
* Usage (before starting the app):
|
||||
* npm run seed:parsian-tehran
|
||||
*
|
||||
* Optional env:
|
||||
* SEED_PARSIAN_TEHRAN_DEFAULT_PASSWORD=Parsian@724
|
||||
*
|
||||
* Backward-compatible env alias:
|
||||
* SEED_FIELD_EXPERT_DEFAULT_PASSWORD=Parsian@724
|
||||
*/
|
||||
import { readFileSync, existsSync } from "node:fs";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import * as crypto from "node:crypto";
|
||||
import mongoose, { Schema, Types } from "mongoose";
|
||||
@@ -23,17 +26,29 @@ type BranchSeed = {
|
||||
isActive?: boolean;
|
||||
};
|
||||
|
||||
type FieldExpertSeed = {
|
||||
type ExpertLocationSeed = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
type FileReviewerSeed = {
|
||||
nationalCode: string;
|
||||
mobile?: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
branchCode: string;
|
||||
branchName?: string;
|
||||
city?: string;
|
||||
state?: string;
|
||||
title?: string;
|
||||
expertCode?: string;
|
||||
locations: ExpertLocationSeed[];
|
||||
ThirdPartyExpertiseClaim?: string;
|
||||
CarBodyExpertiseClaim?: string;
|
||||
};
|
||||
|
||||
type FileMakerSeed = {
|
||||
nationalCode: string;
|
||||
mobile?: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
locations: ExpertLocationSeed[];
|
||||
ThirdPartyClaimExpertId?: string;
|
||||
CarBodyClaimExpertId?: string;
|
||||
};
|
||||
|
||||
function stripQuotes(value: string): string {
|
||||
@@ -76,7 +91,6 @@ function loadEnvFile() {
|
||||
process.env[key] = stripQuotes(value);
|
||||
}
|
||||
|
||||
// Expand ${VAR} placeholders (same as Nest ConfigModule expandVariables).
|
||||
for (let pass = 0; pass < 5; pass++) {
|
||||
let changed = false;
|
||||
for (const key of Object.keys(process.env)) {
|
||||
@@ -110,7 +124,7 @@ function resolveMongoUri(): string {
|
||||
return uri;
|
||||
}
|
||||
|
||||
async function ensureFieldExpertIndexes(collection: mongoose.Collection) {
|
||||
async function ensureUserIndexes(collection: mongoose.Collection) {
|
||||
const indexes = await collection.indexes();
|
||||
const emailIndex = indexes.find((idx) => idx.key?.email === 1);
|
||||
if (emailIndex && !emailIndex.sparse) {
|
||||
@@ -134,6 +148,17 @@ function hashPassword(password: string): Promise<string> {
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeLocations(locations: ExpertLocationSeed[]): ExpertLocationSeed[] {
|
||||
const deduped = new Map<string, ExpertLocationSeed>();
|
||||
for (const location of locations ?? []) {
|
||||
const id = String(location?.id ?? "").trim();
|
||||
const name = String(location?.name ?? "").trim();
|
||||
if (!id || !name || deduped.has(id)) continue;
|
||||
deduped.set(id, { id, name });
|
||||
}
|
||||
return [...deduped.values()];
|
||||
}
|
||||
|
||||
const ClientSchema = new Schema(
|
||||
{
|
||||
clientName: { type: Object, required: true },
|
||||
@@ -159,7 +184,15 @@ const BranchSchema = new Schema(
|
||||
|
||||
BranchSchema.index({ clientKey: 1, code: 1 }, { unique: true });
|
||||
|
||||
const FieldExpertSchema = new Schema(
|
||||
const ExpertLocationSchema = new Schema(
|
||||
{
|
||||
id: { type: String, required: true },
|
||||
name: { type: String, required: true },
|
||||
},
|
||||
{ _id: false, id: false, versionKey: false },
|
||||
);
|
||||
|
||||
const FileReviewerSchema = new Schema(
|
||||
{
|
||||
firstName: { type: String, required: true },
|
||||
lastName: { type: String, required: true },
|
||||
@@ -168,21 +201,180 @@ const FieldExpertSchema = new Schema(
|
||||
nationalCode: { type: String, index: true, sparse: true },
|
||||
clientKey: { type: Schema.Types.ObjectId, index: true },
|
||||
branchId: { type: Schema.Types.ObjectId, index: true },
|
||||
locations: { type: [ExpertLocationSchema], default: [] },
|
||||
password: { type: String, required: true },
|
||||
mobile: { type: String },
|
||||
phone: { type: String },
|
||||
role: { type: String, default: "field_expert" },
|
||||
role: { type: String, default: "file_reviewer" },
|
||||
otp: { type: String, default: "" },
|
||||
expertCode: { type: String, required: false },
|
||||
ThirdPartyExpertiseClaim: { type: String, required: false },
|
||||
CarBodyExpertiseClaim: { type: String, required: false },
|
||||
},
|
||||
{ collection: "field-expert", versionKey: false, timestamps: true },
|
||||
{ collection: "file-reviewer", versionKey: false, timestamps: true },
|
||||
);
|
||||
|
||||
FieldExpertSchema.index(
|
||||
FileReviewerSchema.index(
|
||||
{ clientKey: 1, nationalCode: 1 },
|
||||
{ unique: true, sparse: true },
|
||||
);
|
||||
|
||||
const FileMakerSchema = new Schema(
|
||||
{
|
||||
firstName: { type: String, required: true },
|
||||
lastName: { type: String, required: true },
|
||||
email: { type: String, unique: true, sparse: true },
|
||||
username: { type: String },
|
||||
nationalCode: { type: String, index: true, sparse: true },
|
||||
clientKey: { type: Schema.Types.ObjectId, index: true },
|
||||
branchId: { type: Schema.Types.ObjectId, index: true },
|
||||
locations: { type: [ExpertLocationSchema], default: [] },
|
||||
password: { type: String, required: true },
|
||||
mobile: { type: String },
|
||||
phone: { type: String },
|
||||
role: { type: String, default: "file_maker" },
|
||||
otp: { type: String, default: "" },
|
||||
expertCode: { type: String, required: false },
|
||||
ThirdPartyClaimExpertId: { type: String, required: false },
|
||||
CarBodyClaimExpertId: { type: String, required: false },
|
||||
},
|
||||
{ collection: "file-maker", versionKey: false, timestamps: true },
|
||||
);
|
||||
|
||||
FileMakerSchema.index(
|
||||
{ clientKey: 1, nationalCode: 1 },
|
||||
{ unique: true, sparse: true },
|
||||
);
|
||||
|
||||
function resolveSeedLocations(
|
||||
seedLocations: ExpertLocationSeed[],
|
||||
branchMetaByCode: Map<string, { _id: Types.ObjectId; name: string }>,
|
||||
) {
|
||||
const resolved: { id: string; name: string; branchId: Types.ObjectId }[] = [];
|
||||
const skippedCodes: string[] = [];
|
||||
|
||||
for (const location of normalizeLocations(seedLocations)) {
|
||||
const branch = branchMetaByCode.get(location.id);
|
||||
if (!branch) {
|
||||
skippedCodes.push(location.id);
|
||||
continue;
|
||||
}
|
||||
resolved.push({
|
||||
id: location.id,
|
||||
name: branch.name || location.name,
|
||||
branchId: branch._id,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
resolved,
|
||||
skippedCodes,
|
||||
primaryBranchId: resolved[0]?.branchId,
|
||||
};
|
||||
}
|
||||
|
||||
async function upsertRoleUsers({
|
||||
label,
|
||||
seeds,
|
||||
model,
|
||||
clientKey,
|
||||
hashedPassword,
|
||||
branchMetaByCode,
|
||||
role,
|
||||
codeFields,
|
||||
}: {
|
||||
label: string;
|
||||
seeds: Array<Record<string, any>>;
|
||||
model: mongoose.Model<any>;
|
||||
clientKey: Types.ObjectId;
|
||||
hashedPassword: string;
|
||||
branchMetaByCode: Map<string, { _id: Types.ObjectId; name: string }>;
|
||||
role: string;
|
||||
codeFields: string[];
|
||||
}) {
|
||||
let created = 0;
|
||||
let updated = 0;
|
||||
let skipped = 0;
|
||||
|
||||
for (const seed of seeds) {
|
||||
const { resolved, skippedCodes, primaryBranchId } = resolveSeedLocations(
|
||||
seed.locations,
|
||||
branchMetaByCode,
|
||||
);
|
||||
|
||||
if (skippedCodes.length > 0) {
|
||||
console.warn(
|
||||
`Skipping unknown ${label} locations for ${seed.nationalCode}: ${skippedCodes.join(
|
||||
", ",
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!primaryBranchId || resolved.length === 0) {
|
||||
console.warn(
|
||||
`Skipping ${label} ${seed.nationalCode}: no valid branch locations remained`,
|
||||
);
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const setPayload: Record<string, unknown> = {
|
||||
firstName: seed.firstName,
|
||||
lastName: seed.lastName,
|
||||
username: seed.nationalCode,
|
||||
nationalCode: seed.nationalCode,
|
||||
clientKey,
|
||||
branchId: primaryBranchId,
|
||||
locations: resolved.map(({ id, name }) => ({ id, name })),
|
||||
role,
|
||||
otp: "",
|
||||
};
|
||||
|
||||
if (seed.mobile) {
|
||||
setPayload.mobile = seed.mobile;
|
||||
}
|
||||
|
||||
const unsetPayload: Record<string, ""> = {
|
||||
expertCode: "",
|
||||
};
|
||||
|
||||
for (const field of codeFields) {
|
||||
if (seed[field]) {
|
||||
setPayload[field] = seed[field];
|
||||
} else {
|
||||
unsetPayload[field] = "";
|
||||
}
|
||||
}
|
||||
|
||||
const existing = await model.findOne({
|
||||
clientKey,
|
||||
nationalCode: seed.nationalCode,
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
await model.updateOne(
|
||||
{ _id: existing._id },
|
||||
{
|
||||
$set: {
|
||||
...setPayload,
|
||||
password: existing.password,
|
||||
},
|
||||
$unset: unsetPayload,
|
||||
},
|
||||
);
|
||||
updated++;
|
||||
} else {
|
||||
await model.create({
|
||||
...setPayload,
|
||||
password: hashedPassword,
|
||||
});
|
||||
created++;
|
||||
}
|
||||
}
|
||||
|
||||
return { created, updated, skipped };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
loadEnvFile();
|
||||
const mongoUri = resolveMongoUri();
|
||||
@@ -191,12 +383,24 @@ async function main() {
|
||||
const branchesFile = JSON.parse(
|
||||
readFileSync(join(dataDir, "branches.json"), "utf8"),
|
||||
) as { clientCode: number; branches: BranchSeed[] };
|
||||
const expertsFile = JSON.parse(
|
||||
readFileSync(join(dataDir, "field-experts.json"), "utf8"),
|
||||
) as { clientCode: number; fieldExperts: FieldExpertSeed[] };
|
||||
const fileReviewersFile = JSON.parse(
|
||||
readFileSync(join(dataDir, "file-reviewers.json"), "utf8"),
|
||||
) as { clientCode: number; fileReviewers: FileReviewerSeed[] };
|
||||
const fileMakersFile = JSON.parse(
|
||||
readFileSync(join(dataDir, "file-makers.json"), "utf8"),
|
||||
) as { clientCode: number; fileMakers: FileMakerSeed[] };
|
||||
|
||||
if (
|
||||
branchesFile.clientCode !== fileReviewersFile.clientCode ||
|
||||
branchesFile.clientCode !== fileMakersFile.clientCode
|
||||
) {
|
||||
throw new Error("Seed data clientCode mismatch between branch/reviewer/maker files");
|
||||
}
|
||||
|
||||
const defaultPassword =
|
||||
process.env.SEED_FIELD_EXPERT_DEFAULT_PASSWORD ?? "123321";
|
||||
process.env.SEED_PARSIAN_TEHRAN_DEFAULT_PASSWORD ??
|
||||
process.env.SEED_FIELD_EXPERT_DEFAULT_PASSWORD ??
|
||||
"123321";
|
||||
const hashedPassword = await hashPassword(defaultPassword);
|
||||
|
||||
await mongoose.connect(mongoUri, {
|
||||
@@ -204,11 +408,14 @@ async function main() {
|
||||
tlsAllowInvalidCertificates:
|
||||
process.env.MONGO_TLS_ALLOW_INVALID_CERTS === "true",
|
||||
});
|
||||
|
||||
const Client = mongoose.model("ClientSeedClient", ClientSchema);
|
||||
const Branch = mongoose.model("ClientSeedBranch", BranchSchema);
|
||||
const FieldExpert = mongoose.model("ClientSeedFieldExpert", FieldExpertSchema);
|
||||
const FileReviewer = mongoose.model("ClientSeedFileReviewer", FileReviewerSchema);
|
||||
const FileMaker = mongoose.model("ClientSeedFileMaker", FileMakerSchema);
|
||||
|
||||
await ensureFieldExpertIndexes(FieldExpert.collection);
|
||||
await ensureUserIndexes(FileReviewer.collection);
|
||||
await ensureUserIndexes(FileMaker.collection);
|
||||
|
||||
const client = await Client.findOne({
|
||||
clientCode: branchesFile.clientCode,
|
||||
@@ -220,7 +427,7 @@ async function main() {
|
||||
}
|
||||
const clientKey = new Types.ObjectId(String(client._id));
|
||||
|
||||
const branchIdByCode = new Map<string, Types.ObjectId>();
|
||||
const branchMetaByCode = new Map<string, { _id: Types.ObjectId; name: string }>();
|
||||
let branchesCreated = 0;
|
||||
let branchesUpdated = 0;
|
||||
|
||||
@@ -239,67 +446,45 @@ async function main() {
|
||||
phoneNumber: branch.phoneNumber,
|
||||
isActive: branch.isActive ?? true,
|
||||
};
|
||||
|
||||
if (existing) {
|
||||
await Branch.updateOne({ _id: existing._id }, { $set: payload });
|
||||
branchIdByCode.set(branch.code, existing._id as Types.ObjectId);
|
||||
branchMetaByCode.set(branch.code, {
|
||||
_id: existing._id as Types.ObjectId,
|
||||
name: branch.name,
|
||||
});
|
||||
branchesUpdated++;
|
||||
} else {
|
||||
const created = await Branch.create(payload);
|
||||
branchIdByCode.set(branch.code, created._id as Types.ObjectId);
|
||||
branchMetaByCode.set(branch.code, {
|
||||
_id: created._id as Types.ObjectId,
|
||||
name: branch.name,
|
||||
});
|
||||
branchesCreated++;
|
||||
}
|
||||
}
|
||||
|
||||
let expertsCreated = 0;
|
||||
let expertsUpdated = 0;
|
||||
let expertsSkipped = 0;
|
||||
const fileReviewersResult = await upsertRoleUsers({
|
||||
label: "file-reviewer",
|
||||
seeds: fileReviewersFile.fileReviewers,
|
||||
model: FileReviewer,
|
||||
clientKey,
|
||||
hashedPassword,
|
||||
branchMetaByCode,
|
||||
role: "file_reviewer",
|
||||
codeFields: ["ThirdPartyExpertiseClaim", "CarBodyExpertiseClaim"],
|
||||
});
|
||||
|
||||
for (const expert of expertsFile.fieldExperts) {
|
||||
const branchId = branchIdByCode.get(expert.branchCode);
|
||||
if (!branchId) {
|
||||
console.warn(
|
||||
`Skipping ${expert.nationalCode}: unknown branch ${expert.branchCode}`,
|
||||
);
|
||||
expertsSkipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
firstName: expert.firstName,
|
||||
lastName: expert.lastName,
|
||||
username: expert.nationalCode,
|
||||
nationalCode: expert.nationalCode,
|
||||
clientKey,
|
||||
branchId,
|
||||
password: hashedPassword,
|
||||
mobile: expert.mobile,
|
||||
role: "field_expert",
|
||||
otp: "",
|
||||
expertCode: expert.expertCode,
|
||||
};
|
||||
|
||||
const existing = await FieldExpert.findOne({
|
||||
clientKey,
|
||||
nationalCode: expert.nationalCode,
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
await FieldExpert.updateOne(
|
||||
{ _id: existing._id },
|
||||
{
|
||||
$set: {
|
||||
...payload,
|
||||
// Do not rotate password on re-seed unless explicitly desired.
|
||||
password: existing.password,
|
||||
},
|
||||
},
|
||||
);
|
||||
expertsUpdated++;
|
||||
} else {
|
||||
await FieldExpert.create(payload);
|
||||
expertsCreated++;
|
||||
}
|
||||
}
|
||||
const fileMakersResult = await upsertRoleUsers({
|
||||
label: "file-maker",
|
||||
seeds: fileMakersFile.fileMakers,
|
||||
model: FileMaker,
|
||||
clientKey,
|
||||
hashedPassword,
|
||||
branchMetaByCode,
|
||||
role: "file_maker",
|
||||
codeFields: ["ThirdPartyClaimExpertId", "CarBodyClaimExpertId"],
|
||||
});
|
||||
|
||||
console.log("Parsian Tehran seed completed.");
|
||||
console.log({
|
||||
@@ -307,11 +492,15 @@ async function main() {
|
||||
clientKey: String(clientKey),
|
||||
branchesCreated,
|
||||
branchesUpdated,
|
||||
expertsCreated,
|
||||
expertsUpdated,
|
||||
expertsSkipped,
|
||||
fileReviewersCreated: fileReviewersResult.created,
|
||||
fileReviewersUpdated: fileReviewersResult.updated,
|
||||
fileReviewersSkipped: fileReviewersResult.skipped,
|
||||
fileMakersCreated: fileMakersResult.created,
|
||||
fileMakersUpdated: fileMakersResult.updated,
|
||||
fileMakersSkipped: fileMakersResult.skipped,
|
||||
defaultPassword,
|
||||
loginHint: "Use nationalCode + password on POST /actor/login with role field_expert",
|
||||
loginHint:
|
||||
"Use nationalCode + password on POST /actor/login with role file_reviewer or file_maker",
|
||||
});
|
||||
|
||||
await mongoose.disconnect();
|
||||
|
||||
@@ -2,6 +2,19 @@ import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||
import { Types } from "mongoose";
|
||||
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||
|
||||
@Schema({ _id: false, versionKey: false })
|
||||
export class FileMakerLocationModel {
|
||||
@Prop({ type: String, required: true })
|
||||
id: string;
|
||||
|
||||
@Prop({ type: String, required: true })
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const FileMakerLocationDbSchema = SchemaFactory.createForClass(
|
||||
FileMakerLocationModel,
|
||||
);
|
||||
|
||||
@Schema({
|
||||
collection: "file-maker",
|
||||
versionKey: false,
|
||||
@@ -31,6 +44,9 @@ export class FileMakerModel {
|
||||
@Prop({ type: Types.ObjectId, index: true })
|
||||
branchId?: Types.ObjectId;
|
||||
|
||||
@Prop({ type: [FileMakerLocationDbSchema], default: [] })
|
||||
locations?: FileMakerLocationModel[];
|
||||
|
||||
@Prop({ required: true })
|
||||
password: string;
|
||||
|
||||
@@ -49,6 +65,12 @@ export class FileMakerModel {
|
||||
@Prop({ type: "string", required: false })
|
||||
expertCode?: string;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
ThirdPartyClaimExpertId?: string;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
CarBodyClaimExpertId?: string;
|
||||
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,19 @@ import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||
import { Types } from "mongoose";
|
||||
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||
|
||||
@Schema({ _id: false, versionKey: false })
|
||||
export class FileReviewerLocationModel {
|
||||
@Prop({ type: String, required: true })
|
||||
id: string;
|
||||
|
||||
@Prop({ type: String, required: true })
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const FileReviewerLocationDbSchema = SchemaFactory.createForClass(
|
||||
FileReviewerLocationModel,
|
||||
);
|
||||
|
||||
@Schema({
|
||||
collection: "file-reviewer",
|
||||
versionKey: false,
|
||||
@@ -31,6 +44,9 @@ export class FileReviewerModel {
|
||||
@Prop({ type: Types.ObjectId, index: true })
|
||||
branchId?: Types.ObjectId;
|
||||
|
||||
@Prop({ type: [FileReviewerLocationDbSchema], default: [] })
|
||||
locations?: FileReviewerLocationModel[];
|
||||
|
||||
@Prop({ required: true })
|
||||
password: string;
|
||||
|
||||
@@ -49,6 +65,12 @@ export class FileReviewerModel {
|
||||
@Prop({ type: "string", required: false })
|
||||
expertCode?: string;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
ThirdPartyExpertiseClaim?: string;
|
||||
|
||||
@Prop({ type: String, required: false })
|
||||
CarBodyExpertiseClaim?: string;
|
||||
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user