forked from Yara724/api
Merge pull request 'script updated , inquiry field added to blame case and claim case' (#106) from s.hajizadeh/yara724api:main into main
Reviewed-on: Yara724/api#106
This commit is contained in:
@@ -3,7 +3,8 @@
|
|||||||
/*
|
/*
|
||||||
* One-time script to replace mocked blameCases party vehicle inquiry data.
|
* One-time script to replace mocked blameCases party vehicle inquiry data.
|
||||||
* THIRD_PARTY cases use Tejarat block inquiry; CAR_BODY cases use both
|
* THIRD_PARTY cases use Tejarat block inquiry; CAR_BODY cases use both
|
||||||
* Tejarat third-party block inquiry and car-body inquiry.
|
* Tejarat third-party block inquiry and car-body inquiry. All cases also
|
||||||
|
* refresh available party personal inquiries into inquiries.person.
|
||||||
*
|
*
|
||||||
* Defaults to DRY_RUN=true. Set DRY_RUN=false to write changes.
|
* Defaults to DRY_RUN=true. Set DRY_RUN=false to write changes.
|
||||||
*/
|
*/
|
||||||
@@ -61,10 +62,15 @@ const config = {
|
|||||||
carBodyUrl:
|
carBodyUrl:
|
||||||
process.env.TEJARAT_CAR_BODY_URL ||
|
process.env.TEJARAT_CAR_BODY_URL ||
|
||||||
"http://82.99.202.245:3027/block-inquiry-tejarat/badane",
|
"http://82.99.202.245:3027/block-inquiry-tejarat/badane",
|
||||||
|
personUrl:
|
||||||
|
process.env.TEJARAT_PERSON_URL ||
|
||||||
|
"http://82.99.202.245:3027/personal-inquiry/tejarat-no",
|
||||||
thirdPartyToken:
|
thirdPartyToken:
|
||||||
process.env.TEJARAT_THIRD_PARTY_TOKEN || process.env.TEJARAT_TOKEN || "",
|
process.env.TEJARAT_THIRD_PARTY_TOKEN || process.env.TEJARAT_TOKEN || "",
|
||||||
carBodyToken:
|
carBodyToken:
|
||||||
process.env.TEJARAT_CAR_BODY_TOKEN || process.env.TEJARAT_TOKEN || "",
|
process.env.TEJARAT_CAR_BODY_TOKEN || process.env.TEJARAT_TOKEN || "",
|
||||||
|
personToken:
|
||||||
|
process.env.TEJARAT_PERSON_TOKEN || process.env.TEJARAT_TOKEN || "",
|
||||||
rateLimitPerMinute: Number(process.env.RATE_LIMIT_PER_MINUTE || 5),
|
rateLimitPerMinute: Number(process.env.RATE_LIMIT_PER_MINUTE || 5),
|
||||||
retryEnabled: String(process.env.RETRY_ENABLED ?? "true").toLowerCase() !== "false",
|
retryEnabled: String(process.env.RETRY_ENABLED ?? "true").toLowerCase() !== "false",
|
||||||
retryCount: Number(process.env.RETRY_COUNT || 3),
|
retryCount: Number(process.env.RETRY_COUNT || 3),
|
||||||
@@ -124,6 +130,7 @@ async function main() {
|
|||||||
requestNo: 1,
|
requestNo: 1,
|
||||||
type: 1,
|
type: 1,
|
||||||
parties: 1,
|
parties: 1,
|
||||||
|
inquiries: 1,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.sort({ createdAt: 1, _id: 1 });
|
.sort({ createdAt: 1, _id: 1 });
|
||||||
@@ -136,6 +143,9 @@ async function main() {
|
|||||||
partiesInquired: 0,
|
partiesInquired: 0,
|
||||||
partiesSkipped: 0,
|
partiesSkipped: 0,
|
||||||
partiesFailed: 0,
|
partiesFailed: 0,
|
||||||
|
personInquired: 0,
|
||||||
|
personSkipped: 0,
|
||||||
|
personFailed: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
for await (const doc of cursor) {
|
for await (const doc of cursor) {
|
||||||
@@ -145,7 +155,9 @@ async function main() {
|
|||||||
console.log(`currently inquiry for doc with ${label} and requestId ${requestId}`);
|
console.log(`currently inquiry for doc with ${label} and requestId ${requestId}`);
|
||||||
|
|
||||||
const parties = Array.isArray(doc.parties) ? clone(doc.parties) : [];
|
const parties = Array.isArray(doc.parties) ? clone(doc.parties) : [];
|
||||||
|
const inquiries = doc.inquiries && typeof doc.inquiries === "object" ? clone(doc.inquiries) : {};
|
||||||
let docChanged = false;
|
let docChanged = false;
|
||||||
|
let inquiriesChanged = false;
|
||||||
|
|
||||||
for (let index = 0; index < parties.length; index += 1) {
|
for (let index = 0; index < parties.length; index += 1) {
|
||||||
const party = parties[index];
|
const party = parties[index];
|
||||||
@@ -155,61 +167,101 @@ async function main() {
|
|||||||
if (!input.ok) {
|
if (!input.ok) {
|
||||||
summary.partiesSkipped += 1;
|
summary.partiesSkipped += 1;
|
||||||
console.warn(`[skip] ${partyLabel}: ${input.reason}`);
|
console.warn(`[skip] ${partyLabel}: ${input.reason}`);
|
||||||
continue;
|
} else {
|
||||||
}
|
let nextParty = party;
|
||||||
|
let partyChanged = false;
|
||||||
|
|
||||||
let nextParty = party;
|
for (const request of input.requests) {
|
||||||
let partyChanged = false;
|
|
||||||
|
|
||||||
for (const request of input.requests) {
|
|
||||||
console.log(
|
|
||||||
`[request] ${partyLabel} type=${request.type} body=${JSON.stringify(request.body)}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const inquiryResponse = await inquiryWithRetry(request.type, request.body);
|
|
||||||
const successful = isInquirySuccessful(request.type, inquiryResponse);
|
|
||||||
console.log(
|
console.log(
|
||||||
`[response] ${partyLabel} type=${request.type} successful=${successful} body=${JSON.stringify(inquiryResponse)}`,
|
`[request] ${partyLabel} type=${request.type} body=${JSON.stringify(request.body)}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!successful) {
|
try {
|
||||||
summary.partiesFailed += 1;
|
const inquiryResponse = await inquiryWithRetry(request.type, request.body);
|
||||||
continue;
|
const successful = isInquirySuccessful(request.type, inquiryResponse);
|
||||||
}
|
console.log(
|
||||||
|
`[response] ${partyLabel} type=${request.type} successful=${successful} body=${JSON.stringify(inquiryResponse)}`,
|
||||||
|
);
|
||||||
|
|
||||||
nextParty = applyInquiryToParty(request.type, nextParty, inquiryResponse, input.plate);
|
if (!successful) {
|
||||||
summary.partiesInquired += 1;
|
summary.partiesFailed += 1;
|
||||||
partyChanged = true;
|
continue;
|
||||||
} catch (error) {
|
}
|
||||||
summary.partiesFailed += 1;
|
|
||||||
console.error(`[error] ${partyLabel} type=${request.type}: ${error.message}`);
|
nextParty = applyInquiryToParty(request.type, nextParty, inquiryResponse, input.plate);
|
||||||
|
summary.partiesInquired += 1;
|
||||||
|
partyChanged = true;
|
||||||
|
} catch (error) {
|
||||||
|
summary.partiesFailed += 1;
|
||||||
|
console.error(`[error] ${partyLabel} type=${request.type}: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (partyChanged) {
|
||||||
|
parties[index] = nextParty;
|
||||||
|
docChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (partyChanged) {
|
const personInput = buildPersonInquiryInput(party);
|
||||||
parties[index] = nextParty;
|
if (!personInput.ok) {
|
||||||
docChanged = true;
|
summary.personSkipped += 1;
|
||||||
|
console.warn(`[skip] ${partyLabel} person: ${personInput.reason}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[request] ${partyLabel} type=PERSON body=${JSON.stringify(personInput.body)}`);
|
||||||
|
try {
|
||||||
|
const personResponse = await inquiryWithRetry("PERSON", personInput.body);
|
||||||
|
const successful = isInquirySuccessful("PERSON", personResponse);
|
||||||
|
console.log(
|
||||||
|
`[response] ${partyLabel} type=PERSON successful=${successful} body=${JSON.stringify(personResponse)}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!successful) {
|
||||||
|
summary.personFailed += 1;
|
||||||
|
applyPersonInquiryToCaseInquiries(inquiries, party, index, false, {}, personResponse);
|
||||||
|
inquiriesChanged = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyPersonInquiryToCaseInquiries(
|
||||||
|
inquiries,
|
||||||
|
party,
|
||||||
|
index,
|
||||||
|
true,
|
||||||
|
normalizePersonResponse(personResponse),
|
||||||
|
);
|
||||||
|
summary.personInquired += 1;
|
||||||
|
inquiriesChanged = true;
|
||||||
|
} catch (error) {
|
||||||
|
summary.personFailed += 1;
|
||||||
|
applyPersonInquiryToCaseInquiries(inquiries, party, index, false, {}, error);
|
||||||
|
inquiriesChanged = true;
|
||||||
|
console.error(`[error] ${partyLabel} type=PERSON: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!docChanged) {
|
if (!docChanged && !inquiriesChanged) {
|
||||||
console.log(`[doc] ${label} no changes`);
|
console.log(`[doc] ${label} no changes`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.dryRun) {
|
if (config.dryRun) {
|
||||||
console.log(`[dry-run] ${label} would update parties array`);
|
console.log(`[dry-run] ${label} would update parties/inquiries`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateSet = {
|
||||||
|
updatedAt: new Date(),
|
||||||
|
};
|
||||||
|
if (docChanged) updateSet.parties = parties;
|
||||||
|
if (inquiriesChanged) updateSet.inquiries = inquiries;
|
||||||
|
|
||||||
const result = await collection.updateOne(
|
const result = await collection.updateOne(
|
||||||
{ _id: doc._id },
|
{ _id: doc._id },
|
||||||
{
|
{
|
||||||
$set: {
|
$set: updateSet,
|
||||||
parties,
|
|
||||||
updatedAt: new Date(),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
summary.docsChanged += result.modifiedCount;
|
summary.docsChanged += result.modifiedCount;
|
||||||
@@ -285,6 +337,40 @@ function buildCarBodyRequest(plate, serialLetter, nationalCode) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildPersonInquiryInput(party) {
|
||||||
|
if (!party || typeof party !== "object") return { ok: false, reason: "party is empty" };
|
||||||
|
const person = party.person && typeof party.person === "object" ? party.person : null;
|
||||||
|
if (!person) return { ok: false, reason: "party.person is missing" };
|
||||||
|
|
||||||
|
const nationalCode =
|
||||||
|
cleanString(person.nationalCodeOfInsurer) ||
|
||||||
|
cleanString(person.nationalCodeOfDriver);
|
||||||
|
const birthDate = firstPresent(
|
||||||
|
person.insurerBirthday,
|
||||||
|
person.driverBirthday,
|
||||||
|
person.birthday,
|
||||||
|
);
|
||||||
|
const gregorianBirthdate = jalaliToGregorianDate(birthDate);
|
||||||
|
|
||||||
|
if (!nationalCode) {
|
||||||
|
return { ok: false, reason: "nationalCodeOfInsurer/nationalCodeOfDriver missing" };
|
||||||
|
}
|
||||||
|
if (!gregorianBirthdate) {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
reason: `invalid insurerBirthday/driverBirthday=${cleanString(birthDate)}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
body: {
|
||||||
|
nationalCode,
|
||||||
|
birthdate: gregorianBirthdate,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function parsePlateId(plateId) {
|
function parsePlateId(plateId) {
|
||||||
const value = cleanString(plateId);
|
const value = cleanString(plateId);
|
||||||
if (!value) return null;
|
if (!value) return null;
|
||||||
@@ -352,8 +438,10 @@ function extractPlate(party) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function inquiryWithRetry(type, body) {
|
async function inquiryWithRetry(type, body) {
|
||||||
const url = type === "CAR_BODY" ? config.carBodyUrl : config.thirdPartyUrl;
|
const endpoint = getInquiryEndpoint(type);
|
||||||
const token = type === "CAR_BODY" ? config.carBodyToken : config.thirdPartyToken;
|
const url = endpoint.url;
|
||||||
|
const token = endpoint.token;
|
||||||
|
const accept = endpoint.accept;
|
||||||
let lastError;
|
let lastError;
|
||||||
|
|
||||||
const maxAttempts = config.retryEnabled ? config.retryCount : 1;
|
const maxAttempts = config.retryEnabled ? config.retryCount : 1;
|
||||||
@@ -362,7 +450,7 @@ async function inquiryWithRetry(type, body) {
|
|||||||
await waitForRateLimit();
|
await waitForRateLimit();
|
||||||
try {
|
try {
|
||||||
console.log("[http] " + type + " attempt=" + attempt + "/" + maxAttempts);
|
console.log("[http] " + type + " attempt=" + attempt + "/" + maxAttempts);
|
||||||
return await postJson(url, token, body);
|
return await postJson(url, token, body, accept);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
lastError = error;
|
lastError = error;
|
||||||
console.error(`[retry] ${type} attempt=${attempt} failed: ${error.message}`);
|
console.error(`[retry] ${type} attempt=${attempt} failed: ${error.message}`);
|
||||||
@@ -375,11 +463,11 @@ async function inquiryWithRetry(type, body) {
|
|||||||
throw lastError;
|
throw lastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function postJson(url, token, body) {
|
async function postJson(url, token, body, accept = "application/json") {
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
accept: "application/json",
|
accept,
|
||||||
authorization: `Bearer ${token}`,
|
authorization: `Bearer ${token}`,
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
},
|
},
|
||||||
@@ -395,7 +483,10 @@ async function postJson(url, token, body) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP ${response.status}: ${JSON.stringify(data)}`);
|
const error = new Error(`HTTP ${response.status}: ${JSON.stringify(data)}`);
|
||||||
|
error.status = response.status;
|
||||||
|
error.data = data;
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
@@ -403,9 +494,20 @@ async function postJson(url, token, body) {
|
|||||||
|
|
||||||
function isInquirySuccessful(type, response) {
|
function isInquirySuccessful(type, response) {
|
||||||
if (type === "CAR_BODY") return response && response.isSuccess === true && response.data;
|
if (type === "CAR_BODY") return response && response.isSuccess === true && response.data;
|
||||||
|
if (type === "PERSON") return response && response.status === 200 && response.data;
|
||||||
return response && response.resultStatus === true;
|
return response && response.resultStatus === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getInquiryEndpoint(type) {
|
||||||
|
if (type === "CAR_BODY") {
|
||||||
|
return { url: config.carBodyUrl, token: config.carBodyToken, accept: "application/json" };
|
||||||
|
}
|
||||||
|
if (type === "PERSON") {
|
||||||
|
return { url: config.personUrl, token: config.personToken, accept: "*/*" };
|
||||||
|
}
|
||||||
|
return { url: config.thirdPartyUrl, token: config.thirdPartyToken, accept: "application/json" };
|
||||||
|
}
|
||||||
|
|
||||||
function applyInquiryToParty(type, party, response, originalPlate) {
|
function applyInquiryToParty(type, party, response, originalPlate) {
|
||||||
if (type === "CAR_BODY") return applyCarBodyInquiryToParty(party, response, originalPlate);
|
if (type === "CAR_BODY") return applyCarBodyInquiryToParty(party, response, originalPlate);
|
||||||
return applyThirdPartyInquiryToParty(party, response, originalPlate);
|
return applyThirdPartyInquiryToParty(party, response, originalPlate);
|
||||||
@@ -486,6 +588,51 @@ function applyCarBodyInquiryToParty(party, response, originalPlate) {
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyPersonInquiryToCaseInquiries(inquiries, party, index, has, data, error) {
|
||||||
|
if (!inquiries.person || typeof inquiries.person !== "object") {
|
||||||
|
inquiries.person = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingData =
|
||||||
|
inquiries.person.data && typeof inquiries.person.data === "object" && !Array.isArray(inquiries.person.data)
|
||||||
|
? inquiries.person.data
|
||||||
|
: {};
|
||||||
|
const existingError =
|
||||||
|
inquiries.person.error && typeof inquiries.person.error === "object" && !Array.isArray(inquiries.person.error)
|
||||||
|
? inquiries.person.error
|
||||||
|
: {};
|
||||||
|
const roleKey = party && party.role ? party.role : `party_${index}`;
|
||||||
|
const nextData = { ...existingData };
|
||||||
|
const nextError = { ...existingError };
|
||||||
|
|
||||||
|
if (has) {
|
||||||
|
nextData[roleKey] = data || {};
|
||||||
|
delete nextError[roleKey];
|
||||||
|
} else {
|
||||||
|
nextError[roleKey] = normalizeInquiryError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
inquiries.person = {
|
||||||
|
has: Object.keys(nextData).length > 0,
|
||||||
|
data: nextData,
|
||||||
|
...(Object.keys(nextError).length > 0 ? { error: nextError } : {}),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizePersonResponse(response) {
|
||||||
|
return response && response.data ? response.data : response;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeInquiryError(error) {
|
||||||
|
if (!error) return undefined;
|
||||||
|
return {
|
||||||
|
message: error.message || String(error),
|
||||||
|
status: error.status,
|
||||||
|
data: error.data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function buildPlateId(plate) {
|
function buildPlateId(plate) {
|
||||||
if (!plate) return "";
|
if (!plate) return "";
|
||||||
const serialLetter = NUMBER_TO_LETTER[String(plate.Plk2)] || cleanString(plate.Plk2);
|
const serialLetter = NUMBER_TO_LETTER[String(plate.Plk2)] || cleanString(plate.Plk2);
|
||||||
@@ -555,6 +702,111 @@ function normalizeCarBodyResponse(response, originalPlate) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function jalaliToGregorianDate(input) {
|
||||||
|
if (input === null || input === undefined) return null;
|
||||||
|
|
||||||
|
const raw = normalizeDigits(typeof input === "number" ? String(input) : String(input).trim());
|
||||||
|
if (!raw) return null;
|
||||||
|
|
||||||
|
let year = 0;
|
||||||
|
let month = 0;
|
||||||
|
let day = 0;
|
||||||
|
|
||||||
|
const separated = raw.match(/^(\d{4})[\-/](\d{1,2})[\-/](\d{1,2})$/);
|
||||||
|
if (separated) {
|
||||||
|
year = parseInt(separated[1], 10);
|
||||||
|
month = parseInt(separated[2], 10);
|
||||||
|
day = parseInt(separated[3], 10);
|
||||||
|
} else {
|
||||||
|
const digits = raw.replace(/\D/g, "");
|
||||||
|
if (digits.length !== 8) return null;
|
||||||
|
year = parseInt(digits.slice(0, 4), 10);
|
||||||
|
month = parseInt(digits.slice(4, 6), 10);
|
||||||
|
day = parseInt(digits.slice(6, 8), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!year || !month || !day) return null;
|
||||||
|
|
||||||
|
if (year >= 1900) {
|
||||||
|
const mm = String(month).padStart(2, "0");
|
||||||
|
const dd = String(day).padStart(2, "0");
|
||||||
|
const result = `${year}-${mm}-${dd}`;
|
||||||
|
return isNaN(new Date(result).getTime()) ? null : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return jalaliPartsToGregorian(year, month, day);
|
||||||
|
}
|
||||||
|
|
||||||
|
function jalaliPartsToGregorian(jYear, jMonth, jDay) {
|
||||||
|
const jy = jYear - 979;
|
||||||
|
const jm = jMonth - 1;
|
||||||
|
const jd = jDay - 1;
|
||||||
|
|
||||||
|
let jDayNo =
|
||||||
|
365 * jy + Math.floor(jy / 33) * 8 + Math.floor(((jy % 33) + 3) / 4);
|
||||||
|
|
||||||
|
const jalaliMonthDays = [31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29];
|
||||||
|
for (let i = 0; i < jm; i += 1) {
|
||||||
|
jDayNo += jalaliMonthDays[i];
|
||||||
|
}
|
||||||
|
jDayNo += jd;
|
||||||
|
|
||||||
|
let gDayNo = jDayNo + 79;
|
||||||
|
|
||||||
|
let gy = 1600 + 400 * Math.floor(gDayNo / 146097);
|
||||||
|
gDayNo %= 146097;
|
||||||
|
|
||||||
|
let leap = true;
|
||||||
|
if (gDayNo >= 36525) {
|
||||||
|
gDayNo -= 1;
|
||||||
|
gy += 100 * Math.floor(gDayNo / 36524);
|
||||||
|
gDayNo %= 36524;
|
||||||
|
if (gDayNo >= 365) gDayNo += 1;
|
||||||
|
else leap = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
gy += 4 * Math.floor(gDayNo / 1461);
|
||||||
|
gDayNo %= 1461;
|
||||||
|
|
||||||
|
if (gDayNo >= 366) {
|
||||||
|
leap = false;
|
||||||
|
gDayNo -= 1;
|
||||||
|
gy += Math.floor(gDayNo / 365);
|
||||||
|
gDayNo %= 365;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gregorianMonthDays = [
|
||||||
|
31,
|
||||||
|
leap ? 29 : 28,
|
||||||
|
31,
|
||||||
|
30,
|
||||||
|
31,
|
||||||
|
30,
|
||||||
|
31,
|
||||||
|
31,
|
||||||
|
30,
|
||||||
|
31,
|
||||||
|
30,
|
||||||
|
31,
|
||||||
|
];
|
||||||
|
|
||||||
|
let gm = 0;
|
||||||
|
for (let i = 0; i < 12; i += 1) {
|
||||||
|
if (gDayNo < gregorianMonthDays[i]) {
|
||||||
|
gm = i + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
gDayNo -= gregorianMonthDays[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
const gd = gDayNo + 1;
|
||||||
|
const mm = String(gm).padStart(2, "0");
|
||||||
|
const dd = String(gd).padStart(2, "0");
|
||||||
|
const result = `${gy}-${mm}-${dd}`;
|
||||||
|
|
||||||
|
return isNaN(new Date(result).getTime()) ? null : result;
|
||||||
|
}
|
||||||
|
|
||||||
async function waitForRateLimit() {
|
async function waitForRateLimit() {
|
||||||
const minDelayMs = Math.ceil(60000 / config.rateLimitPerMinute);
|
const minDelayMs = Math.ceil(60000 / config.rateLimitPerMinute);
|
||||||
const elapsed = Date.now() - lastRequestAt;
|
const elapsed = Date.now() - lastRequestAt;
|
||||||
@@ -580,6 +832,9 @@ function validateConfig() {
|
|||||||
if (!config.carBodyToken) {
|
if (!config.carBodyToken) {
|
||||||
throw new Error("TEJARAT_CAR_BODY_TOKEN or TEJARAT_TOKEN is required");
|
throw new Error("TEJARAT_CAR_BODY_TOKEN or TEJARAT_TOKEN is required");
|
||||||
}
|
}
|
||||||
|
if (!config.personToken) {
|
||||||
|
throw new Error("TEJARAT_PERSON_TOKEN or TEJARAT_TOKEN is required");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadEnvFiles(filePath) {
|
function loadEnvFiles(filePath) {
|
||||||
|
|||||||
@@ -3973,6 +3973,7 @@ export class ClaimRequestManagementService {
|
|||||||
blameRequestNo: blameRequest.requestNo,
|
blameRequestNo: blameRequest.requestNo,
|
||||||
status: ClaimCaseStatus.SELECTING_OUTER_PARTS,
|
status: ClaimCaseStatus.SELECTING_OUTER_PARTS,
|
||||||
claimStatus: ClaimStatus.PENDING,
|
claimStatus: ClaimStatus.PENDING,
|
||||||
|
inquiries: (blameRequest as any).inquiries ?? {},
|
||||||
workflow: {
|
workflow: {
|
||||||
currentStep: ClaimWorkflowStep.SELECT_OUTER_PARTS,
|
currentStep: ClaimWorkflowStep.SELECT_OUTER_PARTS,
|
||||||
nextStep: ClaimWorkflowStep.SELECT_OTHER_PARTS,
|
nextStep: ClaimWorkflowStep.SELECT_OTHER_PARTS,
|
||||||
@@ -4108,6 +4109,7 @@ export class ClaimRequestManagementService {
|
|||||||
blameRequestNo: blameRequest.requestNo,
|
blameRequestNo: blameRequest.requestNo,
|
||||||
status: ClaimCaseStatus.SELECTING_OUTER_PARTS,
|
status: ClaimCaseStatus.SELECTING_OUTER_PARTS,
|
||||||
claimStatus: ClaimStatus.PENDING,
|
claimStatus: ClaimStatus.PENDING,
|
||||||
|
inquiries: (blameRequest as any).inquiries ?? {},
|
||||||
workflow: {
|
workflow: {
|
||||||
currentStep: ClaimWorkflowStep.SELECT_OUTER_PARTS,
|
currentStep: ClaimWorkflowStep.SELECT_OUTER_PARTS,
|
||||||
nextStep: ClaimWorkflowStep.SELECT_OTHER_PARTS,
|
nextStep: ClaimWorkflowStep.SELECT_OTHER_PARTS,
|
||||||
@@ -4258,6 +4260,7 @@ export class ClaimRequestManagementService {
|
|||||||
blameRequestNo: blameRequest.requestNo,
|
blameRequestNo: blameRequest.requestNo,
|
||||||
status: ClaimCaseStatus.SELECTING_OUTER_PARTS,
|
status: ClaimCaseStatus.SELECTING_OUTER_PARTS,
|
||||||
claimStatus: ClaimStatus.PENDING,
|
claimStatus: ClaimStatus.PENDING,
|
||||||
|
inquiries: (blameRequest as any).inquiries ?? {},
|
||||||
workflow: {
|
workflow: {
|
||||||
currentStep: ClaimWorkflowStep.SELECT_OUTER_PARTS,
|
currentStep: ClaimWorkflowStep.SELECT_OUTER_PARTS,
|
||||||
nextStep: ClaimWorkflowStep.SELECT_OTHER_PARTS,
|
nextStep: ClaimWorkflowStep.SELECT_OTHER_PARTS,
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ import { HydratedDocument, Schema as MongooseSchema, Types } from "mongoose";
|
|||||||
import { ClaimRequiredDocumentType } from "src/Types&Enums/claim-request-management/required-document-type.enum";
|
import { ClaimRequiredDocumentType } from "src/Types&Enums/claim-request-management/required-document-type.enum";
|
||||||
import { ClaimStatus } from "src/Types&Enums/claim-request-management/claimStatus.enum";
|
import { ClaimStatus } from "src/Types&Enums/claim-request-management/claimStatus.enum";
|
||||||
import { ClaimCaseStatus } from "src/Types&Enums/claim-request-management/claim-case-status.enum";
|
import { ClaimCaseStatus } from "src/Types&Enums/claim-request-management/claim-case-status.enum";
|
||||||
import { HistoryEvent, HistoryEventSchema } from "src/request-management/entities/schema/historyEvent.type";
|
import {
|
||||||
|
HistoryEvent,
|
||||||
|
HistoryEventSchema,
|
||||||
|
} from "src/request-management/entities/schema/historyEvent.type";
|
||||||
import {
|
import {
|
||||||
ClaimDamageSelection,
|
ClaimDamageSelection,
|
||||||
ClaimDamageSelectionSchema,
|
ClaimDamageSelectionSchema,
|
||||||
@@ -25,8 +28,15 @@ import {
|
|||||||
ClaimCaseSnapshot,
|
ClaimCaseSnapshot,
|
||||||
ClaimCaseSnapshotSchema,
|
ClaimCaseSnapshotSchema,
|
||||||
} from "./claim-case.snapshot.schema";
|
} from "./claim-case.snapshot.schema";
|
||||||
import { ClaimWorkflow, ClaimWorkflowSchema } from "./claim-case.workflow.schema";
|
import {
|
||||||
|
ClaimWorkflow,
|
||||||
|
ClaimWorkflowSchema,
|
||||||
|
} from "./claim-case.workflow.schema";
|
||||||
import { UserClaimRating } from "./claim-request-management.schema";
|
import { UserClaimRating } from "./claim-request-management.schema";
|
||||||
|
import {
|
||||||
|
CaseInquiries,
|
||||||
|
CaseInquiriesSchema,
|
||||||
|
} from "src/common/schema/case-inquiries.schema";
|
||||||
|
|
||||||
@Schema({ _id: false })
|
@Schema({ _id: false })
|
||||||
export class RequiredDocumentRef {
|
export class RequiredDocumentRef {
|
||||||
@@ -45,7 +55,8 @@ export class RequiredDocumentRef {
|
|||||||
@Prop({ type: Date })
|
@Prop({ type: Date })
|
||||||
uploadedAt?: Date;
|
uploadedAt?: Date;
|
||||||
}
|
}
|
||||||
export const RequiredDocumentRefSchema = SchemaFactory.createForClass(RequiredDocumentRef);
|
export const RequiredDocumentRefSchema =
|
||||||
|
SchemaFactory.createForClass(RequiredDocumentRef);
|
||||||
|
|
||||||
@Schema({
|
@Schema({
|
||||||
collection: "claimCases",
|
collection: "claimCases",
|
||||||
@@ -67,7 +78,12 @@ export class ClaimCase {
|
|||||||
/**
|
/**
|
||||||
* Overall case status (user flow + expert flow progression)
|
* Overall case status (user flow + expert flow progression)
|
||||||
*/
|
*/
|
||||||
@Prop({ required: true, type: String, enum: ClaimCaseStatus, default: ClaimCaseStatus.CREATED })
|
@Prop({
|
||||||
|
required: true,
|
||||||
|
type: String,
|
||||||
|
enum: ClaimCaseStatus,
|
||||||
|
default: ClaimCaseStatus.CREATED,
|
||||||
|
})
|
||||||
status: ClaimCaseStatus;
|
status: ClaimCaseStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -119,6 +135,9 @@ export class ClaimCase {
|
|||||||
@Prop({ type: ClaimEvaluationSchema, default: () => ({}) })
|
@Prop({ type: ClaimEvaluationSchema, default: () => ({}) })
|
||||||
evaluation?: ClaimEvaluation;
|
evaluation?: ClaimEvaluation;
|
||||||
|
|
||||||
|
@Prop({ type: CaseInquiriesSchema, default: () => ({}) })
|
||||||
|
inquiries?: CaseInquiries;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional “read-optimized” copy of fields from blame/request side.
|
* Optional “read-optimized” copy of fields from blame/request side.
|
||||||
* Source of truth remains `blameRequestId`.
|
* Source of truth remains `blameRequestId`.
|
||||||
@@ -160,4 +179,3 @@ export class ClaimCase {
|
|||||||
export type ClaimCaseDocument = HydratedDocument<ClaimCase>;
|
export type ClaimCaseDocument = HydratedDocument<ClaimCase>;
|
||||||
export const ClaimCaseSchema = SchemaFactory.createForClass(ClaimCase);
|
export const ClaimCaseSchema = SchemaFactory.createForClass(ClaimCase);
|
||||||
ClaimCaseSchema.index({ status: 1, "workflow.locked": 1 });
|
ClaimCaseSchema.index({ status: 1, "workflow.locked": 1 });
|
||||||
|
|
||||||
|
|||||||
34
src/common/schema/case-inquiries.schema.ts
Normal file
34
src/common/schema/case-inquiries.schema.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||||
|
import { Schema as MongooseSchema } from "mongoose";
|
||||||
|
|
||||||
|
@Schema({ _id: false })
|
||||||
|
export class InquiryStatus {
|
||||||
|
@Prop({ type: Boolean, default: false })
|
||||||
|
has?: boolean;
|
||||||
|
|
||||||
|
@Prop({ type: MongooseSchema.Types.Mixed, default: () => ({}) })
|
||||||
|
data?: any;
|
||||||
|
|
||||||
|
@Prop({ type: MongooseSchema.Types.Mixed })
|
||||||
|
error?: any;
|
||||||
|
|
||||||
|
@Prop({ type: Date })
|
||||||
|
updatedAt?: Date;
|
||||||
|
}
|
||||||
|
export const InquiryStatusSchema = SchemaFactory.createForClass(InquiryStatus);
|
||||||
|
|
||||||
|
@Schema({ _id: false, strict: false })
|
||||||
|
export class CaseInquiries {
|
||||||
|
@Prop({ type: InquiryStatusSchema })
|
||||||
|
thirdParty?: InquiryStatus;
|
||||||
|
|
||||||
|
@Prop({ type: InquiryStatusSchema })
|
||||||
|
carBody?: InquiryStatus;
|
||||||
|
|
||||||
|
@Prop({ type: InquiryStatusSchema })
|
||||||
|
person?: InquiryStatus;
|
||||||
|
|
||||||
|
@Prop({ type: InquiryStatusSchema })
|
||||||
|
drivingLicence?: InquiryStatus;
|
||||||
|
}
|
||||||
|
export const CaseInquiriesSchema = SchemaFactory.createForClass(CaseInquiries);
|
||||||
@@ -8,6 +8,10 @@ import { HistoryEvent, HistoryEventSchema } from "./historyEvent.type";
|
|||||||
import { CaseStatus } from "src/Types&Enums/blame-request-management/caseStatus.enum";
|
import { CaseStatus } from "src/Types&Enums/blame-request-management/caseStatus.enum";
|
||||||
import { BlameStatus } from "src/Types&Enums/blame-request-management/blameStatus.enum";
|
import { BlameStatus } from "src/Types&Enums/blame-request-management/blameStatus.enum";
|
||||||
import { CreationMethod, FilledBy } from "./request-management.schema";
|
import { CreationMethod, FilledBy } from "./request-management.schema";
|
||||||
|
import {
|
||||||
|
CaseInquiries,
|
||||||
|
CaseInquiriesSchema,
|
||||||
|
} from "src/common/schema/case-inquiries.schema";
|
||||||
|
|
||||||
/** CAR_BODY only: mocked insurance inquiry result (legacy top-level; prefer parties[].insurance.carBodyInsurance) */
|
/** CAR_BODY only: mocked insurance inquiry result (legacy top-level; prefer parties[].insurance.carBodyInsurance) */
|
||||||
@Schema({ _id: false })
|
@Schema({ _id: false })
|
||||||
@@ -18,84 +22,93 @@ export class CarBodyInsuranceDetail {
|
|||||||
@Prop() insurerCompany?: string;
|
@Prop() insurerCompany?: string;
|
||||||
@Prop({ type: [String] }) coverages?: string[];
|
@Prop({ type: [String] }) coverages?: string[];
|
||||||
}
|
}
|
||||||
export const CarBodyInsuranceDetailSchema = SchemaFactory.createForClass(CarBodyInsuranceDetail);
|
export const CarBodyInsuranceDetailSchema = SchemaFactory.createForClass(
|
||||||
|
CarBodyInsuranceDetail,
|
||||||
|
);
|
||||||
|
|
||||||
@Schema({ _id: false })
|
@Schema({ _id: false })
|
||||||
export class BlameRequestSnapshot {
|
export class BlameRequestSnapshot {
|
||||||
@Prop({ type: [PartySchema], default: [] })
|
@Prop({ type: [PartySchema], default: [] })
|
||||||
parties?: Party[];
|
parties?: Party[];
|
||||||
}
|
}
|
||||||
export const BlameRequestSnapshotSchema =
|
export const BlameRequestSnapshotSchema =
|
||||||
SchemaFactory.createForClass(BlameRequestSnapshot);
|
SchemaFactory.createForClass(BlameRequestSnapshot);
|
||||||
|
|
||||||
|
@Schema({ collection: "blameCases", timestamps: true, id: true })
|
||||||
@Schema({ collection: "blameCases", timestamps: true , id:true })
|
|
||||||
export class BlameRequest {
|
export class BlameRequest {
|
||||||
@Prop({ required: true, unique: true, index: true, trim: true })
|
@Prop({ required: true, unique: true, index: true, trim: true })
|
||||||
requestNo: string;
|
requestNo: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Human-friendly shared id across blame+claim (e.g. A14235).
|
* Human-friendly shared id across blame+claim (e.g. A14235).
|
||||||
* Generated once at creation time and reused by claim.
|
* Generated once at creation time and reused by claim.
|
||||||
*/
|
*/
|
||||||
@Prop({ required: true, unique: true, index: true, trim: true })
|
@Prop({ required: true, unique: true, index: true, trim: true })
|
||||||
publicId: string;
|
publicId: string;
|
||||||
|
|
||||||
@Prop({ required: true, type: String, enum: BlameRequestType })
|
@Prop({ required: true, type: String, enum: BlameRequestType })
|
||||||
type: BlameRequestType;
|
type: BlameRequestType;
|
||||||
|
|
||||||
@Prop({ required: true, type: String, enum: CaseStatus, default: CaseStatus.OPEN })
|
@Prop({
|
||||||
status: CaseStatus;
|
required: true,
|
||||||
|
type: String,
|
||||||
|
enum: CaseStatus,
|
||||||
|
default: CaseStatus.OPEN,
|
||||||
|
})
|
||||||
|
status: CaseStatus;
|
||||||
|
|
||||||
@Prop({ type: String, enum: BlameStatus, default: BlameStatus.UNKNOWN })
|
@Prop({ type: String, enum: BlameStatus, default: BlameStatus.UNKNOWN })
|
||||||
blameStatus: BlameStatus;
|
blameStatus: BlameStatus;
|
||||||
|
|
||||||
@Prop({ type: WorkflowSchema })
|
@Prop({ type: WorkflowSchema })
|
||||||
workflow?: Workflow;
|
workflow?: Workflow;
|
||||||
|
|
||||||
@Prop({ type: [PartySchema], default: [] })
|
@Prop({ type: [PartySchema], default: [] })
|
||||||
parties: Party[];
|
parties: Party[];
|
||||||
|
|
||||||
@Prop({ type: ExpertSectionSchema })
|
@Prop({ type: CaseInquiriesSchema, default: () => ({}) })
|
||||||
expert?: ExpertSection;
|
inquiries?: CaseInquiries;
|
||||||
|
|
||||||
@Prop({ type: [HistoryEventSchema], default: [] })
|
@Prop({ type: ExpertSectionSchema })
|
||||||
history: HistoryEvent[];
|
expert?: ExpertSection;
|
||||||
|
|
||||||
/** CAR_BODY only: first party car-body insurance (mocked inquiry) – legacy; prefer parties[0].insurance.carBodyInsurance */
|
@Prop({ type: [HistoryEventSchema], default: [] })
|
||||||
@Prop({ type: CarBodyInsuranceDetailSchema })
|
history: HistoryEvent[];
|
||||||
carBodyInsuranceDetail?: CarBodyInsuranceDetail;
|
|
||||||
|
|
||||||
/**
|
/** CAR_BODY only: first party car-body insurance (mocked inquiry) – legacy; prefer parties[0].insurance.carBodyInsurance */
|
||||||
* Optional snapshot structure (kept for future use / read-optimized copies).
|
@Prop({ type: CarBodyInsuranceDetailSchema })
|
||||||
* Source of truth remains the top-level fields of this document.
|
carBodyInsuranceDetail?: CarBodyInsuranceDetail;
|
||||||
*/
|
|
||||||
@Prop({ type: BlameRequestSnapshotSchema, required: false })
|
|
||||||
snapshot?: BlameRequestSnapshot;
|
|
||||||
|
|
||||||
/** True when this blame was created by a field expert (independent expert); only that expert can see/review it. */
|
/**
|
||||||
@Prop({ default: false })
|
* Optional snapshot structure (kept for future use / read-optimized copies).
|
||||||
expertInitiated?: boolean;
|
* Source of truth remains the top-level fields of this document.
|
||||||
|
*/
|
||||||
|
@Prop({ type: BlameRequestSnapshotSchema, required: false })
|
||||||
|
snapshot?: BlameRequestSnapshot;
|
||||||
|
|
||||||
/** Field expert who created this file (for expert-initiated flows). */
|
/** True when this blame was created by a field expert (independent expert); only that expert can see/review it. */
|
||||||
@Prop({ type: Types.ObjectId })
|
@Prop({ default: false })
|
||||||
initiatedByFieldExpertId?: Types.ObjectId;
|
expertInitiated?: boolean;
|
||||||
|
|
||||||
/** True when this blame was created by a registrar. */
|
/** Field expert who created this file (for expert-initiated flows). */
|
||||||
@Prop({ default: false })
|
@Prop({ type: Types.ObjectId })
|
||||||
registrarInitiated?: boolean;
|
initiatedByFieldExpertId?: Types.ObjectId;
|
||||||
|
|
||||||
/** Registrar who created this file (for registrar-initiated flows). */
|
/** True when this blame was created by a registrar. */
|
||||||
@Prop({ type: Types.ObjectId })
|
@Prop({ default: false })
|
||||||
initiatedByRegistrarId?: Types.ObjectId;
|
registrarInitiated?: boolean;
|
||||||
|
|
||||||
/** LINK = expert sends link to user(s); IN_PERSON = expert fills form on-site. */
|
/** Registrar who created this file (for registrar-initiated flows). */
|
||||||
@Prop({ type: String, enum: CreationMethod })
|
@Prop({ type: Types.ObjectId })
|
||||||
creationMethod?: CreationMethod;
|
initiatedByRegistrarId?: Types.ObjectId;
|
||||||
|
|
||||||
/** Who fills the blame data: CUSTOMER (LINK) or EXPERT (IN_PERSON). */
|
/** LINK = expert sends link to user(s); IN_PERSON = expert fills form on-site. */
|
||||||
@Prop({ type: String, enum: FilledBy })
|
@Prop({ type: String, enum: CreationMethod })
|
||||||
filledBy?: FilledBy;
|
creationMethod?: CreationMethod;
|
||||||
|
|
||||||
|
/** Who fills the blame data: CUSTOMER (LINK) or EXPERT (IN_PERSON). */
|
||||||
|
@Prop({ type: String, enum: FilledBy })
|
||||||
|
filledBy?: FilledBy;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BlameRequestDocument = HydratedDocument<BlameRequest>;
|
export type BlameRequestDocument = HydratedDocument<BlameRequest>;
|
||||||
|
|||||||
@@ -123,6 +123,64 @@ export class RequestManagementService {
|
|||||||
: -1;
|
: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private normalizeInquiryError(err: any): any {
|
||||||
|
if (!err) return undefined;
|
||||||
|
return {
|
||||||
|
message: err?.message || String(err),
|
||||||
|
status: err?.response?.status,
|
||||||
|
data: err?.response?.data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private recordCaseInquiryStatus(
|
||||||
|
req: any,
|
||||||
|
key: "thirdParty" | "carBody" | "person" | "drivingLicence" | string,
|
||||||
|
has: boolean,
|
||||||
|
data: any = {},
|
||||||
|
error?: any,
|
||||||
|
): void {
|
||||||
|
const existingInquiries =
|
||||||
|
req.inquiries?.toObject?.() ??
|
||||||
|
(req.inquiries && typeof req.inquiries === "object" ? req.inquiries : {});
|
||||||
|
req.inquiries = {
|
||||||
|
...existingInquiries,
|
||||||
|
[key]: {
|
||||||
|
has,
|
||||||
|
data: data ?? {},
|
||||||
|
...(error ? { error: this.normalizeInquiryError(error) } : {}),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private recordPartyCaseInquiryStatus(
|
||||||
|
req: any,
|
||||||
|
key: "thirdParty" | "carBody" | "person" | "drivingLicence" | string,
|
||||||
|
role: PartyRole,
|
||||||
|
has: boolean,
|
||||||
|
data: any = {},
|
||||||
|
error?: any,
|
||||||
|
): void {
|
||||||
|
const existingData =
|
||||||
|
req?.inquiries?.[key]?.data?.toObject?.() ?? req?.inquiries?.[key]?.data;
|
||||||
|
const dataByRole =
|
||||||
|
existingData &&
|
||||||
|
typeof existingData === "object" &&
|
||||||
|
!Array.isArray(existingData)
|
||||||
|
? existingData
|
||||||
|
: {};
|
||||||
|
this.recordCaseInquiryStatus(
|
||||||
|
req,
|
||||||
|
key,
|
||||||
|
has,
|
||||||
|
{
|
||||||
|
...dataByRole,
|
||||||
|
[role]: data ?? {},
|
||||||
|
},
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private assertPartyOwner(party: any, user: any, errMsg: string) {
|
private assertPartyOwner(party: any, user: any, errMsg: string) {
|
||||||
const partyUserId = party?.person?.userId
|
const partyUserId = party?.person?.userId
|
||||||
? String(party.person.userId)
|
? String(party.person.userId)
|
||||||
@@ -967,6 +1025,11 @@ export class RequestManagementService {
|
|||||||
this.logger.log(
|
this.logger.log(
|
||||||
`[TEJARAT] block inquiry mapped for request=${req._id}: ${JSON.stringify(inquiryMapped)}`,
|
`[TEJARAT] block inquiry mapped for request=${req._id}: ${JSON.stringify(inquiryMapped)}`,
|
||||||
);
|
);
|
||||||
|
this.recordPartyCaseInquiryStatus(req, "thirdParty", role, true, {
|
||||||
|
source: "TEJARAT_BLOCK_INQUIRY",
|
||||||
|
raw: inquiryRaw,
|
||||||
|
mapped: inquiryMapped,
|
||||||
|
});
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
`[TEJARAT] block inquiry failed for request=${req._id}: ${err?.message || err}`,
|
`[TEJARAT] block inquiry failed for request=${req._id}: ${err?.message || err}`,
|
||||||
@@ -978,6 +1041,15 @@ export class RequestManagementService {
|
|||||||
}, data=${JSON.stringify(err.response.data)}`,
|
}, data=${JSON.stringify(err.response.data)}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
this.recordPartyCaseInquiryStatus(
|
||||||
|
req,
|
||||||
|
"thirdParty",
|
||||||
|
role,
|
||||||
|
false,
|
||||||
|
{},
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
await (req as any).save();
|
||||||
throw new HttpException("Inquiry failed", HttpStatus.BAD_REQUEST);
|
throw new HttpException("Inquiry failed", HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -987,6 +1059,12 @@ export class RequestManagementService {
|
|||||||
inquiryMapped.Error,
|
inquiryMapped.Error,
|
||||||
)}`,
|
)}`,
|
||||||
);
|
);
|
||||||
|
this.recordPartyCaseInquiryStatus(req, "thirdParty", role, false, {
|
||||||
|
source: "TEJARAT_BLOCK_INQUIRY",
|
||||||
|
raw: inquiryRaw,
|
||||||
|
mapped: inquiryMapped,
|
||||||
|
});
|
||||||
|
await (req as any).save();
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
inquiryMapped.Error.Message || "Inquiry returned error",
|
inquiryMapped.Error.Message || "Inquiry returned error",
|
||||||
HttpStatus.BAD_REQUEST,
|
HttpStatus.BAD_REQUEST,
|
||||||
@@ -1020,6 +1098,13 @@ export class RequestManagementService {
|
|||||||
personalNationalCode,
|
personalNationalCode,
|
||||||
personalBirthDate,
|
personalBirthDate,
|
||||||
);
|
);
|
||||||
|
this.recordPartyCaseInquiryStatus(
|
||||||
|
req,
|
||||||
|
"person",
|
||||||
|
role,
|
||||||
|
true,
|
||||||
|
personalInquiry,
|
||||||
|
);
|
||||||
this.logger.log(
|
this.logger.log(
|
||||||
`[SANDHUB] personal inquiry success request=${req._id} nationalCode=${personalNationalCode}: ${JSON.stringify(
|
`[SANDHUB] personal inquiry success request=${req._id} nationalCode=${personalNationalCode}: ${JSON.stringify(
|
||||||
personalInquiry,
|
personalInquiry,
|
||||||
@@ -1029,6 +1114,8 @@ export class RequestManagementService {
|
|||||||
this.logger.error(
|
this.logger.error(
|
||||||
`[SANDHUB] personal inquiry failed request=${req._id} nationalCode=${personalNationalCode}: ${err?.message || err}`,
|
`[SANDHUB] personal inquiry failed request=${req._id} nationalCode=${personalNationalCode}: ${err?.message || err}`,
|
||||||
);
|
);
|
||||||
|
this.recordPartyCaseInquiryStatus(req, "person", role, false, {}, err);
|
||||||
|
await (req as any).save();
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
"Personal identity inquiry failed",
|
"Personal identity inquiry failed",
|
||||||
HttpStatus.BAD_REQUEST,
|
HttpStatus.BAD_REQUEST,
|
||||||
@@ -1120,10 +1207,28 @@ export class RequestManagementService {
|
|||||||
|
|
||||||
// CAR BODY EXTERNAL API INQUIRY
|
// CAR BODY EXTERNAL API INQUIRY
|
||||||
if (req.type === BlameRequestType.CAR_BODY && role === PartyRole.FIRST) {
|
if (req.type === BlameRequestType.CAR_BODY && role === PartyRole.FIRST) {
|
||||||
const carBodyInfo = await this.sandHubService.getTejaratCarBodyInquiry({
|
let carBodyInfo: any;
|
||||||
nationalCodeOfInsurer: body.nationalCodeOfInsurer,
|
try {
|
||||||
plate: body.plate,
|
carBodyInfo = await this.sandHubService.getTejaratCarBodyInquiry({
|
||||||
});
|
nationalCodeOfInsurer: body.nationalCodeOfInsurer,
|
||||||
|
plate: body.plate,
|
||||||
|
});
|
||||||
|
this.recordPartyCaseInquiryStatus(req, "carBody", role, true, {
|
||||||
|
source: "TEJARAT_CAR_BODY_INQUIRY",
|
||||||
|
raw: carBodyInfo.raw,
|
||||||
|
mapped: carBodyInfo.mapped,
|
||||||
|
});
|
||||||
|
} catch (err: any) {
|
||||||
|
this.logger.error(
|
||||||
|
`[TEJARAT] car body inquiry failed for request=${req._id}: ${err?.message || err}`,
|
||||||
|
);
|
||||||
|
this.recordPartyCaseInquiryStatus(req, "carBody", role, false, {}, err);
|
||||||
|
await (req as any).save();
|
||||||
|
throw new HttpException(
|
||||||
|
"Car body inquiry failed",
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Raw + mapped stored under vehicle (same pattern as third-party inquiry)
|
// Raw + mapped stored under vehicle (same pattern as third-party inquiry)
|
||||||
party.vehicle.inquiry = {
|
party.vehicle.inquiry = {
|
||||||
@@ -4829,8 +4934,24 @@ export class RequestManagementService {
|
|||||||
nationalCodeOfInsurer: firstPartyPlate.nationalCodeOfInsurer,
|
nationalCodeOfInsurer: firstPartyPlate.nationalCodeOfInsurer,
|
||||||
});
|
});
|
||||||
sandHubReport = sandHubResponse["_doc"] || sandHubResponse;
|
sandHubReport = sandHubResponse["_doc"] || sandHubResponse;
|
||||||
|
this.recordPartyCaseInquiryStatus(
|
||||||
|
req,
|
||||||
|
"thirdParty",
|
||||||
|
PartyRole.FIRST,
|
||||||
|
true,
|
||||||
|
sandHubReport,
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error("SandHub error in expertCompleteCarBodyFormV2:", e);
|
this.logger.error("SandHub error in expertCompleteCarBodyFormV2:", e);
|
||||||
|
this.recordPartyCaseInquiryStatus(
|
||||||
|
req,
|
||||||
|
"thirdParty",
|
||||||
|
PartyRole.FIRST,
|
||||||
|
false,
|
||||||
|
{},
|
||||||
|
e,
|
||||||
|
);
|
||||||
|
await (req as any).save();
|
||||||
throw new InternalServerErrorException(
|
throw new InternalServerErrorException(
|
||||||
"Failed to process plate information.",
|
"Failed to process plate information.",
|
||||||
);
|
);
|
||||||
@@ -4864,6 +4985,13 @@ export class RequestManagementService {
|
|||||||
firstPartyPlate.plate,
|
firstPartyPlate.plate,
|
||||||
firstPartyPlate.nationalCodeOfInsurer,
|
firstPartyPlate.nationalCodeOfInsurer,
|
||||||
);
|
);
|
||||||
|
this.recordPartyCaseInquiryStatus(
|
||||||
|
req,
|
||||||
|
"carBody",
|
||||||
|
PartyRole.FIRST,
|
||||||
|
true,
|
||||||
|
carBodyInfo,
|
||||||
|
);
|
||||||
|
|
||||||
const firstParty: any = {
|
const firstParty: any = {
|
||||||
role: PartyRole.FIRST,
|
role: PartyRole.FIRST,
|
||||||
@@ -5010,11 +5138,32 @@ export class RequestManagementService {
|
|||||||
desc: string,
|
desc: string,
|
||||||
role: PartyRole,
|
role: PartyRole,
|
||||||
) => {
|
) => {
|
||||||
const sandHubResponse = await this.sandHubService.getSandHubResponse({
|
let sandHubResponse: any;
|
||||||
plate: plateDto.plate,
|
try {
|
||||||
nationalCodeOfInsurer: plateDto.nationalCodeOfInsurer,
|
sandHubResponse = await this.sandHubService.getSandHubResponse({
|
||||||
});
|
plate: plateDto.plate,
|
||||||
|
nationalCodeOfInsurer: plateDto.nationalCodeOfInsurer,
|
||||||
|
});
|
||||||
|
} catch (err: any) {
|
||||||
|
this.recordPartyCaseInquiryStatus(
|
||||||
|
req,
|
||||||
|
"thirdParty",
|
||||||
|
role,
|
||||||
|
false,
|
||||||
|
{},
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
await (req as any).save();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
const sandHubReport = (sandHubResponse["_doc"] || sandHubResponse) as any;
|
const sandHubReport = (sandHubResponse["_doc"] || sandHubResponse) as any;
|
||||||
|
this.recordPartyCaseInquiryStatus(
|
||||||
|
req,
|
||||||
|
"thirdParty",
|
||||||
|
role,
|
||||||
|
true,
|
||||||
|
sandHubReport,
|
||||||
|
);
|
||||||
const clientName =
|
const clientName =
|
||||||
sandHubReport?.CompanyName || sandHubReport?.LastCompanyName;
|
sandHubReport?.CompanyName || sandHubReport?.LastCompanyName;
|
||||||
const companyCode = sandHubReport?.CompanyCode;
|
const companyCode = sandHubReport?.CompanyCode;
|
||||||
|
|||||||
Reference in New Issue
Block a user