YARA-1244

This commit is contained in:
SepehrYahyaee
2026-08-24 11:11:31 +03:30
parent 93edba412f
commit 2df7a889d3
4 changed files with 341 additions and 228 deletions

View File

@@ -2,143 +2,13 @@
"clientCode": 8, "clientCode": 8,
"branches": [ "branches": [
{ {
"code": "100100", "code": "210120",
"name": "واحدصدورالکترونيکي", "name": "شعبه والفجر",
"fullName": "واحدصدورالکترونيکي(100100)", "fullName": "شعبه والفجر(210120)",
"city": "تهران", "city": "تهران",
"state": "تهران", "state": "تهران",
"address": "خيابان وليعصر_بلوار ميرداماد_پلاک 22", "address": "تهران، اميرآبادشمالي، شهرک والفجر، ضلع جنوب غربي ميدان استادخسرو سينايي",
"phoneNumber": "8259", "phoneNumber": "86051332",
"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",
"isActive": true "isActive": true
}, },
{ {
@@ -151,6 +21,26 @@
"phoneNumber": "66021968", "phoneNumber": "66021968",
"isActive": true "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", "code": "210110",
"name": "شعبه پونک", "name": "شعبه پونک",
@@ -162,23 +52,13 @@
"isActive": true "isActive": true
}, },
{ {
"code": "210120", "code": "111130",
"name": "شعبه والفجر", "name": "شعبه ويژه ميرداماد",
"fullName": "شعبه والفجر(210120)", "fullName": "شعبه ويژه ميرداماد(111130)",
"city": "تهران", "city": "تهران",
"state": "تهران", "state": "تهران",
"address": "تهران، اميرآبادشمالي، شهرک والفجر، ضلع جنوب غربي ميدان استادخسرو سينايي", "address": "تهران، خيابان وليعصر، بالاتراز ميرداماد، خيابان قباديان غربي، پلاك22",
"phoneNumber": "86051332", "phoneNumber": "8259",
"isActive": true
},
{
"code": "210150",
"name": "شعبه شمال شرق تهران",
"fullName": "شعبه شمال شرق تهران(210150)",
"city": "تهران",
"state": "تهران",
"address": "تهران، ضلع شمال غربي ميدان بني هاشم، نبش خيابان كشوري، پلاك13",
"phoneNumber": "26244319",
"isActive": true "isActive": true
} }
] ]

View File

@@ -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): * Usage (before starting the app):
* npm run seed:parsian-tehran * npm run seed:parsian-tehran
* *
* Optional env: * Optional env:
* SEED_PARSIAN_TEHRAN_DEFAULT_PASSWORD=Parsian@724
*
* Backward-compatible env alias:
* SEED_FIELD_EXPERT_DEFAULT_PASSWORD=Parsian@724 * 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 { join } from "node:path";
import * as crypto from "node:crypto"; import * as crypto from "node:crypto";
import mongoose, { Schema, Types } from "mongoose"; import mongoose, { Schema, Types } from "mongoose";
@@ -23,17 +26,29 @@ type BranchSeed = {
isActive?: boolean; isActive?: boolean;
}; };
type FieldExpertSeed = { type ExpertLocationSeed = {
id: string;
name: string;
};
type FileReviewerSeed = {
nationalCode: string; nationalCode: string;
mobile?: string; mobile?: string;
firstName: string; firstName: string;
lastName: string; lastName: string;
branchCode: string; locations: ExpertLocationSeed[];
branchName?: string; ThirdPartyExpertiseClaim?: string;
city?: string; CarBodyExpertiseClaim?: string;
state?: string; };
title?: string;
expertCode?: string; type FileMakerSeed = {
nationalCode: string;
mobile?: string;
firstName: string;
lastName: string;
locations: ExpertLocationSeed[];
ThirdPartyClaimExpertId?: string;
CarBodyClaimExpertId?: string;
}; };
function stripQuotes(value: string): string { function stripQuotes(value: string): string {
@@ -76,7 +91,6 @@ function loadEnvFile() {
process.env[key] = stripQuotes(value); process.env[key] = stripQuotes(value);
} }
// Expand ${VAR} placeholders (same as Nest ConfigModule expandVariables).
for (let pass = 0; pass < 5; pass++) { for (let pass = 0; pass < 5; pass++) {
let changed = false; let changed = false;
for (const key of Object.keys(process.env)) { for (const key of Object.keys(process.env)) {
@@ -110,7 +124,7 @@ function resolveMongoUri(): string {
return uri; return uri;
} }
async function ensureFieldExpertIndexes(collection: mongoose.Collection) { async function ensureUserIndexes(collection: mongoose.Collection) {
const indexes = await collection.indexes(); const indexes = await collection.indexes();
const emailIndex = indexes.find((idx) => idx.key?.email === 1); const emailIndex = indexes.find((idx) => idx.key?.email === 1);
if (emailIndex && !emailIndex.sparse) { 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( const ClientSchema = new Schema(
{ {
clientName: { type: Object, required: true }, clientName: { type: Object, required: true },
@@ -159,7 +184,15 @@ const BranchSchema = new Schema(
BranchSchema.index({ clientKey: 1, code: 1 }, { unique: true }); 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 }, firstName: { type: String, required: true },
lastName: { type: String, required: true }, lastName: { type: String, required: true },
@@ -168,21 +201,180 @@ const FieldExpertSchema = new Schema(
nationalCode: { type: String, index: true, sparse: true }, nationalCode: { type: String, index: true, sparse: true },
clientKey: { type: Schema.Types.ObjectId, index: true }, clientKey: { type: Schema.Types.ObjectId, index: true },
branchId: { type: Schema.Types.ObjectId, index: true }, branchId: { type: Schema.Types.ObjectId, index: true },
locations: { type: [ExpertLocationSchema], default: [] },
password: { type: String, required: true }, password: { type: String, required: true },
mobile: { type: String }, mobile: { type: String },
phone: { type: String }, phone: { type: String },
role: { type: String, default: "field_expert" }, role: { type: String, default: "file_reviewer" },
otp: { type: String, default: "" }, otp: { type: String, default: "" },
expertCode: { type: String, required: false }, 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 }, { clientKey: 1, nationalCode: 1 },
{ unique: true, sparse: true }, { 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() { async function main() {
loadEnvFile(); loadEnvFile();
const mongoUri = resolveMongoUri(); const mongoUri = resolveMongoUri();
@@ -191,12 +383,24 @@ async function main() {
const branchesFile = JSON.parse( const branchesFile = JSON.parse(
readFileSync(join(dataDir, "branches.json"), "utf8"), readFileSync(join(dataDir, "branches.json"), "utf8"),
) as { clientCode: number; branches: BranchSeed[] }; ) as { clientCode: number; branches: BranchSeed[] };
const expertsFile = JSON.parse( const fileReviewersFile = JSON.parse(
readFileSync(join(dataDir, "field-experts.json"), "utf8"), readFileSync(join(dataDir, "file-reviewers.json"), "utf8"),
) as { clientCode: number; fieldExperts: FieldExpertSeed[] }; ) 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 = 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); const hashedPassword = await hashPassword(defaultPassword);
await mongoose.connect(mongoUri, { await mongoose.connect(mongoUri, {
@@ -204,11 +408,14 @@ async function main() {
tlsAllowInvalidCertificates: tlsAllowInvalidCertificates:
process.env.MONGO_TLS_ALLOW_INVALID_CERTS === "true", process.env.MONGO_TLS_ALLOW_INVALID_CERTS === "true",
}); });
const Client = mongoose.model("ClientSeedClient", ClientSchema); const Client = mongoose.model("ClientSeedClient", ClientSchema);
const Branch = mongoose.model("ClientSeedBranch", BranchSchema); 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({ const client = await Client.findOne({
clientCode: branchesFile.clientCode, clientCode: branchesFile.clientCode,
@@ -220,7 +427,7 @@ async function main() {
} }
const clientKey = new Types.ObjectId(String(client._id)); 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 branchesCreated = 0;
let branchesUpdated = 0; let branchesUpdated = 0;
@@ -239,67 +446,45 @@ async function main() {
phoneNumber: branch.phoneNumber, phoneNumber: branch.phoneNumber,
isActive: branch.isActive ?? true, isActive: branch.isActive ?? true,
}; };
if (existing) { if (existing) {
await Branch.updateOne({ _id: existing._id }, { $set: payload }); 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++; branchesUpdated++;
} else { } else {
const created = await Branch.create(payload); 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++; branchesCreated++;
} }
} }
let expertsCreated = 0; const fileReviewersResult = await upsertRoleUsers({
let expertsUpdated = 0; label: "file-reviewer",
let expertsSkipped = 0; seeds: fileReviewersFile.fileReviewers,
model: FileReviewer,
clientKey,
hashedPassword,
branchMetaByCode,
role: "file_reviewer",
codeFields: ["ThirdPartyExpertiseClaim", "CarBodyExpertiseClaim"],
});
for (const expert of expertsFile.fieldExperts) { const fileMakersResult = await upsertRoleUsers({
const branchId = branchIdByCode.get(expert.branchCode); label: "file-maker",
if (!branchId) { seeds: fileMakersFile.fileMakers,
console.warn( model: FileMaker,
`Skipping ${expert.nationalCode}: unknown branch ${expert.branchCode}`, clientKey,
); hashedPassword,
expertsSkipped++; branchMetaByCode,
continue; role: "file_maker",
} codeFields: ["ThirdPartyClaimExpertId", "CarBodyClaimExpertId"],
});
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++;
}
}
console.log("Parsian Tehran seed completed."); console.log("Parsian Tehran seed completed.");
console.log({ console.log({
@@ -307,11 +492,15 @@ async function main() {
clientKey: String(clientKey), clientKey: String(clientKey),
branchesCreated, branchesCreated,
branchesUpdated, branchesUpdated,
expertsCreated, fileReviewersCreated: fileReviewersResult.created,
expertsUpdated, fileReviewersUpdated: fileReviewersResult.updated,
expertsSkipped, fileReviewersSkipped: fileReviewersResult.skipped,
fileMakersCreated: fileMakersResult.created,
fileMakersUpdated: fileMakersResult.updated,
fileMakersSkipped: fileMakersResult.skipped,
defaultPassword, 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(); await mongoose.disconnect();

View File

@@ -2,6 +2,19 @@ import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { Types } from "mongoose"; import { Types } from "mongoose";
import { RoleEnum } from "src/Types&Enums/role.enum"; 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({ @Schema({
collection: "file-maker", collection: "file-maker",
versionKey: false, versionKey: false,
@@ -31,6 +44,9 @@ export class FileMakerModel {
@Prop({ type: Types.ObjectId, index: true }) @Prop({ type: Types.ObjectId, index: true })
branchId?: Types.ObjectId; branchId?: Types.ObjectId;
@Prop({ type: [FileMakerLocationDbSchema], default: [] })
locations?: FileMakerLocationModel[];
@Prop({ required: true }) @Prop({ required: true })
password: string; password: string;
@@ -49,6 +65,12 @@ export class FileMakerModel {
@Prop({ type: "string", required: false }) @Prop({ type: "string", required: false })
expertCode?: string; expertCode?: string;
@Prop({ type: String, required: false })
ThirdPartyClaimExpertId?: string;
@Prop({ type: String, required: false })
CarBodyClaimExpertId?: string;
createdAt: Date; createdAt: Date;
} }

View File

@@ -2,6 +2,19 @@ import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { Types } from "mongoose"; import { Types } from "mongoose";
import { RoleEnum } from "src/Types&Enums/role.enum"; 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({ @Schema({
collection: "file-reviewer", collection: "file-reviewer",
versionKey: false, versionKey: false,
@@ -31,6 +44,9 @@ export class FileReviewerModel {
@Prop({ type: Types.ObjectId, index: true }) @Prop({ type: Types.ObjectId, index: true })
branchId?: Types.ObjectId; branchId?: Types.ObjectId;
@Prop({ type: [FileReviewerLocationDbSchema], default: [] })
locations?: FileReviewerLocationModel[];
@Prop({ required: true }) @Prop({ required: true })
password: string; password: string;
@@ -49,6 +65,12 @@ export class FileReviewerModel {
@Prop({ type: "string", required: false }) @Prop({ type: "string", required: false })
expertCode?: string; expertCode?: string;
@Prop({ type: String, required: false })
ThirdPartyExpertiseClaim?: string;
@Prop({ type: String, required: false })
CarBodyExpertiseClaim?: string;
createdAt: Date; createdAt: Date;
} }