sms template corrected and now fires at the end of the fanavaran stage (Expertise). Docs also added.

This commit is contained in:
2026-08-18 11:18:33 +03:30
parent cb69b496b5
commit 693d602e49
11 changed files with 68 additions and 44 deletions

View File

@@ -127,8 +127,7 @@ Full shape: see skill reference sample and `buildFanavaranSubmitPayload` / `appl
| `Id` | `claimCases.claimId` | | `Id` | `claimCases.claimId` |
| `ClaimNo` | `claimCases.claimNo` | | `ClaimNo` | `claimCases.claimNo` |
History: `FANAVARAN_EARLY_AUTO_SUBMIT_SUCCEEDED`. History: `FANAVARAN_EARLY_AUTO_SUBMIT_SUCCEEDED`.
SMS owner via `SmsOrchestrationService` (deduped `fanavaranSync.baseClaim.smsNotifiedAt`).
### Error handling ### Error handling
@@ -309,6 +308,7 @@ Requires `claimId` + `dmgCaseId` (soft-ensures earlier stages).
| `Id` | `claimCases.expertiseId` | | `Id` | `claimCases.expertiseId` |
History: `FANAVARAN_EXPERTISE_AUTO_SUBMIT_SUCCEEDED`. History: `FANAVARAN_EXPERTISE_AUTO_SUBMIT_SUCCEEDED`.
SMS owner via `SmsOrchestrationService` (deduped `fanavaranSync.expertise.smsNotifiedAt`).
Proven Parsian: `expertiseId=403144`, `ClaimExpertId=29`. Proven Parsian: `expertiseId=403144`, `ClaimExpertId=29`.
Manual: `POST /v2/fanavaran/{client}/claim-cases/{id}/expertise/submit`. Manual: `POST /v2/fanavaran/{client}/claim-cases/{id}/expertise/submit`.

View File

@@ -31,7 +31,7 @@ source: fanavaran-module-docs
| Validation: FileTypeId ∈ tenant lookup | logic ✅ | allowed ids ✅ | | Validation: FileTypeId ∈ tenant lookup | logic ✅ | allowed ids ✅ |
| Business: THIRD_PARTY only | ✅ | ❌ | | Business: THIRD_PARTY only | ✅ | ❌ |
| Provisional address / dummy licence | ✅ today | may customize later | | Provisional address / dummy licence | ✅ today | may customize later |
| SMS template after base claim | shared orchestration | provider/templates may ✅ | | SMS template after expertise | shared orchestration | provider/templates may ✅ |
| Mongo collections / audit schema | ✅ | ❌ | | Mongo collections / audit schema | ✅ | ❌ |
| Offline inquiry seeds | may exist per client | ✅ | | Offline inquiry seeds | may exist per client | ✅ |

View File

@@ -15,13 +15,13 @@ Start local claim
→ Validate THIRD_PARTY + data readiness → Validate THIRD_PARTY + data readiness
→ Auth (cached token) → Auth (cached token)
→ Policy inquiry (resolve-once PolicyId) → Policy inquiry (resolve-once PolicyId)
→ GEN.03 base claim → store claimId / claimNo → SMS owner → GEN.03 base claim → store claimId / claimNo
→ SELECT_OUTER_PARTS → SELECT_OUTER_PARTS
→ GEN.12 damage case → store dmgCaseId → GEN.12 damage case → store dmgCaseId
→ Upload docs/images locally → Upload docs/images locally
→ GEN.07 attachments (per file, best-effort) → GEN.07 attachments (per file, best-effort)
→ Expert pricing ready → Expert pricing ready
→ GEN.08 expertise → store expertiseId → GEN.08 expertise → store expertiseId → SMS owner
→ End (local completion independent of Fanavaran success) → End (local completion independent of Fanavaran success)
``` ```
@@ -56,7 +56,6 @@ sequenceDiagram
YARA->>FV: POST third-party-car-financial-claims (GEN.03) YARA->>FV: POST third-party-car-financial-claims (GEN.03)
FV-->>YARA: Id, ClaimNo FV-->>YARA: Id, ClaimNo
YARA->>DB: claimId, claimNo, history SUCCESS YARA->>DB: claimId, claimNo, history SUCCESS
YARA->>YARA: SMS owner (deduped)
User->>YARA: SELECT_OUTER_PARTS User->>YARA: SELECT_OUTER_PARTS
YARA->>YARA: autoSubmitFanavaranDamageCase... YARA->>YARA: autoSubmitFanavaranDamageCase...
@@ -77,6 +76,7 @@ sequenceDiagram
YARA->>FV: POST .../expertise (GEN.08) YARA->>FV: POST .../expertise (GEN.08)
FV-->>YARA: ExpertiseId FV-->>YARA: ExpertiseId
YARA->>DB: expertiseId, history SUCCESS YARA->>DB: expertiseId, history SUCCESS
YARA->>YARA: SMS owner (deduped)
``` ```
## Flow diagram (stages + soft-ensure) ## Flow diagram (stages + soft-ensure)

View File

@@ -17,7 +17,7 @@ source: fanavaran-module-docs
| Tenant config | boot + System Settings | `fanavaran-client-config.service.ts`, `system-settings.*` | | Tenant config | boot + System Settings | `fanavaran-client-config.service.ts`, `system-settings.*` |
| Orchestration | `ClaimRequestManagementModule` | `claim-request-management.service.ts` | | Orchestration | `ClaimRequestManagementModule` | `claim-request-management.service.ts` |
| Policy select | same | `fanavaran-policy-selection.ts` | | Policy select | same | `fanavaran-policy-selection.ts` |
| SMS after base | `SmsOrchestrationModule` | `sms-orchestration.service.ts` | | SMS after expertise | `SmsOrchestrationModule` | `sms-orchestration.service.ts` |
--- ---

View File

@@ -21,7 +21,7 @@ source: fanavaran-module-docs
| Dependency | Role | | Dependency | Role |
|------------|------| |------------|------|
| **SMS provider** | Notify owner after successful GEN.03 (`SmsOrchestrationService`; same as login SMS stack) | | **SMS provider** | Notify owner after successful GEN.08 expertise (`SmsOrchestrationService`; same as login SMS stack) |
| **Offline inquiry seeds** | Can supply driver/policy test data without live inquiry | | **Offline inquiry seeds** | Can supply driver/policy test data without live inquiry |
| **Redis / Queue** | **Not** used for Fanavaran stage orchestration today — retries are in-process `setTimeout` + Mongo `nextRetryAt` | | **Redis / Queue** | **Not** used for Fanavaran stage orchestration today — retries are in-process `setTimeout` + Mongo `nextRetryAt` |

View File

@@ -69,4 +69,4 @@ Per HTTP step:
## SMS ## SMS
After successful base claim, owner SMS may be sent once (`smsNotifiedAt`). Failure to SMS should not roll back Fanavaran claim create; treat as separate notification concern. After successful expertise (last Fanavaran stage), owner SMS may be sent once (`fanavaranSync.expertise.smsNotifiedAt`). Failure to SMS should not roll back Fanavaran expertise submit; treat as separate notification concern. Claims already notified at base claim (`fanavaranSync.baseClaim.smsNotifiedAt`) are not re-notified.

View File

@@ -5918,6 +5918,13 @@ export class ClaimRequestManagementService {
this.logger.log( this.logger.log(
`[executeFanavaranExpertiseSubmit] Already have expertiseId=${claimCase.expertiseId}; skipping Fanavaran POST`, `[executeFanavaranExpertiseSubmit] Already have expertiseId=${claimCase.expertiseId}; skipping Fanavaran POST`,
); );
// Catch-up / deduped: SMS may still be pending if expertise predated this notify.
await this.notifyClaimOwnerFanavaranClaimRegistered({
claimCaseId,
claimId: claimCase.claimId,
claimNo: claimCase.claimNo,
logPrefix: `[Fanavaran ${clientKey} V2 Expertise]`,
});
return ( return (
(claimCase as any)?.fanavaranSync?.expertise?.response ?? { (claimCase as any)?.fanavaranSync?.expertise?.response ?? {
Id: claimCase.expertiseId, Id: claimCase.expertiseId,
@@ -5982,6 +5989,14 @@ export class ClaimRequestManagementService {
}, },
}); });
// Best-effort: SMS after last Fanavaran stage (expertise) succeeds
await this.notifyClaimOwnerFanavaranClaimRegistered({
claimCaseId,
claimId: claimCase.claimId,
claimNo: claimCase.claimNo,
logPrefix: `[Fanavaran ${clientKey} V2 Expertise]`,
});
return response.data; return response.data;
} catch (error) { } catch (error) {
const exchange = this.fanavaranAuditService.captureAxiosExchange(error); const exchange = this.fanavaranAuditService.captureAxiosExchange(error);
@@ -6018,6 +6033,12 @@ export class ClaimRequestManagementService {
}; };
} }
if (claimCase.expertiseId != null) { if (claimCase.expertiseId != null) {
await this.notifyClaimOwnerFanavaranClaimRegistered({
claimCaseId,
claimId: claimCase.claimId,
claimNo: claimCase.claimNo,
logPrefix,
});
return { return {
attempted: false, attempted: false,
submitted: false, submitted: false,
@@ -7083,11 +7104,11 @@ export class ClaimRequestManagementService {
} }
/** /**
* After Fanavaran base claim create succeeds, SMS the claim owner with * After Fanavaran expertise (last stage) succeeds, SMS the claim owner with
* ClaimNo + claimId. Uses SmsOrchestrationService → same provider as login * publicId + claimId + ClaimNo. Uses SmsOrchestrationService → same provider
* for this deployment (`SMS` / `SMS_PROVIDER`). Never throws. * as login for this deployment (`SMS` / `SMS_PROVIDER`). Never throws.
*/ */
private async notifyClaimOwnerFanavaranBaseClaimRegistered(input: { private async notifyClaimOwnerFanavaranClaimRegistered(input: {
claimCaseId: string; claimCaseId: string;
claimId?: number | string | null; claimId?: number | string | null;
claimNo?: number | string | null; claimNo?: number | string | null;
@@ -7106,7 +7127,9 @@ export class ClaimRequestManagementService {
); );
if (!claimCase) return; if (!claimCase) return;
if ((claimCase as any)?.fanavaranSync?.baseClaim?.smsNotifiedAt) { const sync = (claimCase as any)?.fanavaranSync;
// Dedup: expertise stage, or older base-claim notify from before the move.
if (sync?.expertise?.smsNotifiedAt || sync?.baseClaim?.smsNotifiedAt) {
this.logger.log( this.logger.log(
`${input.logPrefix} Fanavaran SMS already sent; skipping duplicate`, `${input.logPrefix} Fanavaran SMS already sent; skipping duplicate`,
); );
@@ -7127,23 +7150,21 @@ export class ClaimRequestManagementService {
: String(input.claimCaseId); : String(input.claimCaseId);
const sent = const sent =
await this.smsOrchestrationService.sendFanavaranBaseClaimRegisteredNotice( await this.smsOrchestrationService.sendFanavaranClaimRegisteredNotice({
{ receptor: phone,
receptor: phone, publicId,
publicId, claimNo: input.claimNo ?? claimCase.claimNo,
claimNo: input.claimNo, claimId: input.claimId ?? claimCase.claimId,
claimId: input.claimId, });
},
);
if (sent) { if (sent) {
await this.claimCaseDbService.findByIdAndUpdate(input.claimCaseId, { await this.claimCaseDbService.findByIdAndUpdate(input.claimCaseId, {
$set: { $set: {
"fanavaranSync.baseClaim.smsNotifiedAt": new Date(), "fanavaranSync.expertise.smsNotifiedAt": new Date(),
}, },
}); });
this.logger.log( this.logger.log(
`${input.logPrefix} Fanavaran claim SMS sent to claim owner phone=${phone} claimNo=${input.claimNo ?? "-"} claimId=${input.claimId ?? "-"}`, `${input.logPrefix} Fanavaran claim SMS sent to claim owner phone=${phone} publicId=${publicId} claimNo=${input.claimNo ?? claimCase.claimNo ?? "-"} claimId=${input.claimId ?? claimCase.claimId ?? "-"}`,
); );
} }
} catch (error) { } catch (error) {
@@ -7301,14 +7322,6 @@ export class ClaimRequestManagementService {
claimCaseId, claimCaseId,
updateData, updateData,
); );
// Best-effort: same SMS provider as login for this deployment
await this.notifyClaimOwnerFanavaranBaseClaimRegistered({
claimCaseId,
claimId,
claimNo,
logPrefix,
});
} }
return response.data; return response.data;

View File

@@ -107,7 +107,7 @@ export class FanavaranSyncStage {
@Prop({ type: Date }) @Prop({ type: Date })
lastPayloadBuiltAt?: Date; lastPayloadBuiltAt?: Date;
/** When we SMS'd the claim owner about Fanavaran claimId/ClaimNo (base claim). */ /** When we SMS'd the claim owner about Fanavaran claimId/ClaimNo (after expertise). */
@Prop({ type: Date }) @Prop({ type: Date })
smsNotifiedAt?: Date; smsNotifiedAt?: Date;

View File

@@ -115,7 +115,7 @@ export const SEED_FANAVARAN_CLIENT_PROFILES: Record<
defaults: { defaults: {
...SHARED_FANAVARAN_DEFAULTS, ...SHARED_FANAVARAN_DEFAULTS,
// GEN.03 — کارشناس مسئول پرونده مالی // GEN.03 — کارشناس مسئول پرونده مالی
ClaimExpertId: 4543092, ClaimExpertId: 2721,
// GEN.08 — ارزیاب (proven successful submit 2026-07-18 ClaimExpertId: 2709) // GEN.08 — ارزیاب (proven successful submit 2026-07-18 ClaimExpertId: 2709)
ExpertiseClaimExpertId: 2709, ExpertiseClaimExpertId: 2709,
ClaimFileTypeId: 23, ClaimFileTypeId: 23,
@@ -152,7 +152,7 @@ export const SEED_FANAVARAN_CLIENT_PROFILES: Record<
password: "itT@l!3@user", password: "itT@l!3@user",
corpId: "5650", corpId: "5650",
contractId: "304", contractId: "304",
location: "30900", location: "1",
}, },
// Expert role ids not confirmed for Moallem yet — start from shared shape. // Expert role ids not confirmed for Moallem yet — start from shared shape.
defaults: { defaults: {

View File

@@ -19,7 +19,7 @@ const PARSIAN_TEMPLATE_BODIES: Record<string, string> = {
"yara-signature": "yara-signature":
"امضای پرونده {token} ({token2}) توسط کارشناس {token3} بررسی شد.\nلینک: {token10}", "امضای پرونده {token} ({token2}) توسط کارشناس {token3} بررسی شد.\nلینک: {token10}",
"yara-fanavaran-claim": "yara-fanavaran-claim":
"کاربر گرامی پرونده شما به شماره {token3} در فناوران با شناسه {token2} و شماره {token} ثبت شده است. جهت پیگیری های آتی پرونده خود باید از این اطلاعات استفاده کنید.", "کاربرگرامی پرونده شما به شماره {token} در سامانه فناوران با شناسه {token2} و شماره پرونده {token3} ثبت شده است. جهت پیگیری های آتی از این اطلاعات استفاده کنید.",
}; };
function applyTokens( function applyTokens(

View File

@@ -194,17 +194,18 @@ export class SmsOrchestrationService implements OnModuleInit {
} }
/** /**
* Notify the claim owner (damaged party) that the Fanavaran base claim was * Notify the claim owner (damaged party) after Fanavaran expertise (last
* created. Uses the same SMS gateway/provider as login for this deployment. * stage) succeeds. Uses the same SMS gateway/provider as login.
* *
* token = ClaimNo, token2 = claimId (Fanavaran Id), token3 = Yara publicId * token = Yara publicId (claim case number), token2 = Fanavaran claimId,
* token3 = Fanavaran claimNo
* *
* Parsian body: * Parsian body:
* کاربر گرامی پرونده شما به شماره {publicId} در فناوران با شناسه {claimId} * کاربرگرامی پرونده شما به شماره {publicId} در سامانه فناوران با شناسه
* و شماره {claimNo} ثبت شده است. جهت پیگیری های آتی پرونده خود باید از این * {claimId} و شماره پرونده {claimNo} ثبت شده است. جهت پیگیری های آتی از
* اطلاعات استفاده کنید. * این اطلاعات استفاده کنید.
*/ */
async sendFanavaranBaseClaimRegisteredNotice(params: { async sendFanavaranClaimRegisteredNotice(params: {
receptor: string; receptor: string;
publicId: string; publicId: string;
claimNo?: string | number | null; claimNo?: string | number | null;
@@ -221,12 +222,22 @@ export class SmsOrchestrationService implements OnModuleInit {
return this.sendTemplate({ return this.sendTemplate({
template: "yara-fanavaran-claim", template: "yara-fanavaran-claim",
receptor: params.receptor, receptor: params.receptor,
token: params.publicId || "-", token: params.publicId || "-",
token2: claimId, token2: claimId,
token3: claimNo, token3: claimNo,
}); });
} }
/** @deprecated Use sendFanavaranClaimRegisteredNotice */
async sendFanavaranBaseClaimRegisteredNotice(params: {
receptor: string;
publicId: string;
claimNo?: string | number | null;
claimId?: string | number | null;
}): Promise<boolean> {
return this.sendFanavaranClaimRegisteredNotice(params);
}
private async sendTemplate(args: TemplateArgs): Promise<boolean> { private async sendTemplate(args: TemplateArgs): Promise<boolean> {
try { try {
await this.smsGatewayService.verifyLookUp(args); await this.smsGatewayService.verifyLookUp(args);