forked from Yara724/api
Merge pull request 'main' (#162) from s.hajizadeh/yara724api:main into main
Reviewed-on: Yara724/api#162
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
# External API Curl Guide
|
||||
|
||||
This file documents outbound HTTP calls made by the app or maintenance scripts.
|
||||
Use placeholder values for secrets, tokens, IDs, plate values, and payloads before running any curl.
|
||||
Client credentials that are hardcoded in the codebase are filled in below. Keep
|
||||
placeholders only for runtime data such as national codes, plate values, tokens
|
||||
returned by login calls, and payload files.
|
||||
|
||||
## Common Notes
|
||||
|
||||
@@ -32,29 +34,73 @@ Used by:
|
||||
2. Login with `appToken` to get `authenticationToken`.
|
||||
3. Call lookup, policy inquiry, or submit endpoint with `authenticationToken`, `CorpId`, `ContractId`, and `Location`.
|
||||
|
||||
### Auth Script
|
||||
|
||||
Use this when you only need fresh Fanavaran tokens and do not want to copy the
|
||||
curl commands manually:
|
||||
|
||||
```sh
|
||||
scripts/fanavaran-auth.sh tejaratno
|
||||
scripts/fanavaran-auth.sh parsian
|
||||
```
|
||||
|
||||
The script stores raw responses and reusable variables under
|
||||
`files/fanavaran-auth/<client>/`. For example:
|
||||
|
||||
```sh
|
||||
source files/fanavaran-auth/tejaratno/tokens.env
|
||||
curl -X GET "$FANAVARAN_BIME_URL/car/base-info/accident-causes" \
|
||||
-H "authenticationToken: $AUTHENTICATION_TOKEN" \
|
||||
-H "CorpId: $CORP_ID" \
|
||||
-H "ContractId: $CONTRACT_ID" \
|
||||
-H "Location: $LOCATION" \
|
||||
-H "Content-Type: application/json"
|
||||
```
|
||||
|
||||
### Tenant Credentials
|
||||
|
||||
The app supports these Fanavaran client profiles:
|
||||
|
||||
| Client | appname | secret | userName | password | CorpId | ContractId | Location |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| tejaratno | from `src/core/config/fanavaran-client.config.ts` | hardcoded in source | hardcoded in source | hardcoded in source | `3539` | `263` | `100` |
|
||||
| parsian | from `src/core/config/fanavaran-client.config.ts` | hardcoded in source | hardcoded in source | hardcoded in source | `543` | `28` | `210050` |
|
||||
| tejaratno | `fanhab` | `5Fa@N#A2B` | `fanhabUser` | `Fan#@2U$3er` | `3539` | `263` | `100` |
|
||||
| parsian | `ParsianService` | `P@r30@n$erv!ce` | `ParsianServiceUser` | `P@r30@n123` | `543` | `28` | `210050` |
|
||||
|
||||
Set them as shell variables before running:
|
||||
Set the client variables before running. These values come from
|
||||
`src/core/config/fanavaran-client.config.ts` and match
|
||||
`src/claim-request-management/claim-request-management.service.ts`.
|
||||
|
||||
Tejaratno:
|
||||
|
||||
```sh
|
||||
APP_NAME="<appname>"
|
||||
APP_SECRET="<secret>"
|
||||
FANAVARAN_USERNAME="<userName>"
|
||||
FANAVARAN_PASSWORD="<password>"
|
||||
CORP_ID="<CorpId>"
|
||||
CONTRACT_ID="<ContractId>"
|
||||
LOCATION="<Location>"
|
||||
FANAVARAN_CLIENT="tejaratno"
|
||||
APP_NAME='fanhab'
|
||||
APP_SECRET='5Fa@N#A2B'
|
||||
FANAVARAN_USERNAME='fanhabUser'
|
||||
FANAVARAN_PASSWORD='Fan#@2U$3er'
|
||||
CORP_ID='3539'
|
||||
CONTRACT_ID='263'
|
||||
LOCATION='100'
|
||||
```
|
||||
|
||||
Parsian:
|
||||
|
||||
```sh
|
||||
FANAVARAN_CLIENT="parsian"
|
||||
APP_NAME='ParsianService'
|
||||
APP_SECRET='P@r30@n$erv!ce'
|
||||
FANAVARAN_USERNAME='ParsianServiceUser'
|
||||
FANAVARAN_PASSWORD='P@r30@n123'
|
||||
CORP_ID='543'
|
||||
CONTRACT_ID='28'
|
||||
LOCATION='210050'
|
||||
```
|
||||
|
||||
### 1. Get App Token
|
||||
|
||||
The app sends an empty string body, deletes `Content-Type`, and keeps
|
||||
`Content-Length: 0`.
|
||||
|
||||
```sh
|
||||
curl -i -X POST "$FANAVARAN_BASE_URL/EITAuthentication/GetAppToken" \
|
||||
-H "appname: $APP_NAME" \
|
||||
@@ -65,11 +111,21 @@ curl -i -X POST "$FANAVARAN_BASE_URL/EITAuthentication/GetAppToken" \
|
||||
The token is returned in a response header named `appToken` or `apptoken`.
|
||||
|
||||
```sh
|
||||
APP_TOKEN="<response appToken header>"
|
||||
curl -sS -D /tmp/fanavaran-app-token.headers -o /tmp/fanavaran-app-token.body \
|
||||
-X POST "$FANAVARAN_BASE_URL/EITAuthentication/GetAppToken" \
|
||||
-H "appname: $APP_NAME" \
|
||||
-H "secret: $APP_SECRET" \
|
||||
-H "Content-Length: 0"
|
||||
APP_TOKEN="$(awk -F': ' 'tolower($1)=="apptoken" {gsub(/\r/,"",$2); print $2}' /tmp/fanavaran-app-token.headers)"
|
||||
printf 'APP_TOKEN=%s\n' "$APP_TOKEN"
|
||||
```
|
||||
|
||||
### 2. Login
|
||||
|
||||
Use the `APP_TOKEN` returned by the immediately previous GetAppToken request.
|
||||
Do not reuse an old app token pasted from logs or another machine; the app does
|
||||
not do that.
|
||||
|
||||
```sh
|
||||
curl -i -X POST "$FANAVARAN_BASE_URL/EITAuthentication/Login" \
|
||||
-H "appToken: $APP_TOKEN" \
|
||||
@@ -81,7 +137,95 @@ curl -i -X POST "$FANAVARAN_BASE_URL/EITAuthentication/Login" \
|
||||
The token is returned in a response header or body field named `authenticationToken`, `authenticationtoken`, or `authentication_token`.
|
||||
|
||||
```sh
|
||||
AUTHENTICATION_TOKEN="<response authenticationToken>"
|
||||
curl -sS -D /tmp/fanavaran-login.headers -o /tmp/fanavaran-login.body \
|
||||
-X POST "$FANAVARAN_BASE_URL/EITAuthentication/Login" \
|
||||
-H "appToken: $APP_TOKEN" \
|
||||
-H "userName: $FANAVARAN_USERNAME" \
|
||||
-H "password: $FANAVARAN_PASSWORD" \
|
||||
-H "Content-Length: 0"
|
||||
AUTHENTICATION_TOKEN="$(awk -F': ' 'tolower($1)=="authenticationtoken" {gsub(/\r/,"",$2); print $2}' /tmp/fanavaran-login.headers)"
|
||||
if [ -z "$AUTHENTICATION_TOKEN" ]; then
|
||||
AUTHENTICATION_TOKEN="$(node -e 'const fs=require("fs"); const body=fs.readFileSync("/tmp/fanavaran-login.body","utf8"); try { const json=JSON.parse(body); console.log(json.authenticationToken || json.authenticationtoken || json.authentication_token || ""); } catch { console.log(""); }')"
|
||||
fi
|
||||
printf 'AUTHENTICATION_TOKEN=%s\n' "$AUTHENTICATION_TOKEN"
|
||||
```
|
||||
|
||||
If login returns `نام کاربر یا رمز عبور صحیح نیست` for `tejaratno`, first check
|
||||
that `APP_TOKEN` was generated with `appname: fanhab` and `secret: 5Fa@N#A2B` in
|
||||
the same sequence. The runtime code calls `GetAppToken` first, then passes that
|
||||
fresh header value to `Login`; it does not use a static value such as
|
||||
`182197f6-7b41-47b4-9b18-5bfcbc027f2e`.
|
||||
|
||||
### Ready-To-Run Auth Sequences
|
||||
|
||||
Tejaratno:
|
||||
|
||||
```sh
|
||||
FANAVARAN_BASE_URL="https://apimanager.iraneit.com/BimeApiManager/api"
|
||||
FANAVARAN_BIME_URL="$FANAVARAN_BASE_URL/BimeApi/v2.0"
|
||||
FANAVARAN_CLIENT="tejaratno"
|
||||
APP_NAME='fanhab'
|
||||
APP_SECRET='5Fa@N#A2B'
|
||||
FANAVARAN_USERNAME='fanhabUser'
|
||||
FANAVARAN_PASSWORD='Fan#@2U$3er'
|
||||
CORP_ID='3539'
|
||||
CONTRACT_ID='263'
|
||||
LOCATION='100'
|
||||
|
||||
curl -sS -D /tmp/fanavaran-app-token.headers -o /tmp/fanavaran-app-token.body \
|
||||
-X POST "$FANAVARAN_BASE_URL/EITAuthentication/GetAppToken" \
|
||||
-H "appname: $APP_NAME" \
|
||||
-H "secret: $APP_SECRET" \
|
||||
-H "Content-Length: 0"
|
||||
APP_TOKEN="$(awk -F': ' 'tolower($1)=="apptoken" {gsub(/\r/,"",$2); print $2}' /tmp/fanavaran-app-token.headers)"
|
||||
|
||||
curl -sS -D /tmp/fanavaran-login.headers -o /tmp/fanavaran-login.body \
|
||||
-X POST "$FANAVARAN_BASE_URL/EITAuthentication/Login" \
|
||||
-H "appToken: $APP_TOKEN" \
|
||||
-H "userName: $FANAVARAN_USERNAME" \
|
||||
-H "password: $FANAVARAN_PASSWORD" \
|
||||
-H "Content-Length: 0"
|
||||
AUTHENTICATION_TOKEN="$(awk -F': ' 'tolower($1)=="authenticationtoken" {gsub(/\r/,"",$2); print $2}' /tmp/fanavaran-login.headers)"
|
||||
if [ -z "$AUTHENTICATION_TOKEN" ]; then
|
||||
AUTHENTICATION_TOKEN="$(node -e 'const fs=require("fs"); const body=fs.readFileSync("/tmp/fanavaran-login.body","utf8"); try { const json=JSON.parse(body); console.log(json.authenticationToken || json.authenticationtoken || json.authentication_token || ""); } catch { console.log(""); }')"
|
||||
fi
|
||||
|
||||
printf 'APP_TOKEN=%s\nAUTHENTICATION_TOKEN=%s\n' "$APP_TOKEN" "$AUTHENTICATION_TOKEN"
|
||||
```
|
||||
|
||||
Parsian:
|
||||
|
||||
```sh
|
||||
FANAVARAN_BASE_URL="https://apimanager.iraneit.com/BimeApiManager/api"
|
||||
FANAVARAN_BIME_URL="$FANAVARAN_BASE_URL/BimeApi/v2.0"
|
||||
FANAVARAN_CLIENT="parsian"
|
||||
APP_NAME='ParsianService'
|
||||
APP_SECRET='P@r30@n$erv!ce'
|
||||
FANAVARAN_USERNAME='ParsianServiceUser'
|
||||
FANAVARAN_PASSWORD='P@r30@n123'
|
||||
CORP_ID='543'
|
||||
CONTRACT_ID='28'
|
||||
LOCATION='210050'
|
||||
|
||||
curl -sS -D /tmp/fanavaran-app-token.headers -o /tmp/fanavaran-app-token.body \
|
||||
-X POST "$FANAVARAN_BASE_URL/EITAuthentication/GetAppToken" \
|
||||
-H "appname: $APP_NAME" \
|
||||
-H "secret: $APP_SECRET" \
|
||||
-H "Content-Length: 0"
|
||||
APP_TOKEN="$(awk -F': ' 'tolower($1)=="apptoken" {gsub(/\r/,"",$2); print $2}' /tmp/fanavaran-app-token.headers)"
|
||||
|
||||
curl -sS -D /tmp/fanavaran-login.headers -o /tmp/fanavaran-login.body \
|
||||
-X POST "$FANAVARAN_BASE_URL/EITAuthentication/Login" \
|
||||
-H "appToken: $APP_TOKEN" \
|
||||
-H "userName: $FANAVARAN_USERNAME" \
|
||||
-H "password: $FANAVARAN_PASSWORD" \
|
||||
-H "Content-Length: 0"
|
||||
AUTHENTICATION_TOKEN="$(awk -F': ' 'tolower($1)=="authenticationtoken" {gsub(/\r/,"",$2); print $2}' /tmp/fanavaran-login.headers)"
|
||||
if [ -z "$AUTHENTICATION_TOKEN" ]; then
|
||||
AUTHENTICATION_TOKEN="$(node -e 'const fs=require("fs"); const body=fs.readFileSync("/tmp/fanavaran-login.body","utf8"); try { const json=JSON.parse(body); console.log(json.authenticationToken || json.authenticationtoken || json.authentication_token || ""); } catch { console.log(""); }')"
|
||||
fi
|
||||
|
||||
printf 'APP_TOKEN=%s\nAUTHENTICATION_TOKEN=%s\n' "$APP_TOKEN" "$AUTHENTICATION_TOKEN"
|
||||
```
|
||||
|
||||
### 3A. Lookup Endpoints
|
||||
@@ -167,6 +311,8 @@ Default base URL:
|
||||
|
||||
```sh
|
||||
TEJARAT_INQUIRY_BASE_URL="${TEJARAT_INQUIRY_BASE_URL:-http://82.99.202.245:3027}"
|
||||
TEJARAT_INQUIRY_EMAIL='xxx@example.com'
|
||||
TEJARAT_INQUIRY_PASSWORD='123321'
|
||||
```
|
||||
|
||||
Used by `src/sand-hub/sand-hub.service.ts` for third-party plate and car-body plate inquiries when ESG is not selected.
|
||||
@@ -311,8 +457,10 @@ Used by `src/sand-hub/sand-hub.service.ts` for legacy inquiry flows.
|
||||
Environment:
|
||||
|
||||
```sh
|
||||
SANHUB_URL_LOGIN="<login URL>"
|
||||
SANHUB_BASE_URL="<base URL>"
|
||||
SANHUB_BASE_URL='http://82.99.202.245:3027'
|
||||
SANHUB_URL_LOGIN='http://82.99.202.245:3027/user/login'
|
||||
SANHUB_USERNAME='default@admin.com'
|
||||
SANHUB_PASSWORD='123321'
|
||||
```
|
||||
|
||||
### Sequence
|
||||
@@ -415,7 +563,7 @@ curl -X POST "$SANHUB_BASE_URL/sheba/sheba-tejaratno" \
|
||||
Used by `src/claim-request-management/claim-request-management.service.ts`.
|
||||
|
||||
```sh
|
||||
MAP_IR_API_KEY="<x-api-key>"
|
||||
MAP_IR_API_KEY='eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImI5ZDZjMThkNDRjZjc2OWI2Yjk1ODcyMGFjYmEzMmRiN2NhZjg0Zjk4OTRlMjZiZDg0Yzg3YjVlMzhlMTAyZDlkMWYxOGM5NjNmOTk4YjY2In0.eyJhdWQiOiIyMTcxOCIsImp0aSI6ImI5ZDZjMThkNDRjZjc2OWI2Yjk1ODcyMGFjYmEzMmRiN2NhZjg0Zjk4OTRlMjZiZDg0Yzg3YjVlMzhlMTAyZDlkMWYxOGM5NjNmOTk4YjY2IiwiaWF0IjoxNjgwNjA4NTkxLCJuYmYiOjE2ODA2MDg1OTEsImV4cCI6MTY4MzIwMDU5MSwic3ViIjoiIiwic2NvcGVzIjpbImJhc2ljIl19.rTviLd8b5yTHUDa3ODZyva593eMnL0d3XPg3sKkZxMOf_jNIH6lFQyIfbId-wsd1EAdsOdsL3CME_Y8t332PWJbxMNgnEq4Rf2IkClkvkSx6Sb5_4bmlhBM75zw2SmccvgbFUn4xkTOw0FT4vABC2Y3-MKctjMpmO8QOrVULSKt4psrmQhr7hBu7YRDnAAEc6muZ1VpRvdB1kqNKddoSIrfDaq6aDRJ-BNbGRAaFFvP_kH4cgSCKV4dU0TknL3mRKUiVy6_TDkjtzAN8fE2wsdvNo2pGTJPzKFsR2ipgGNTvB__g3bOnVpKsgFXPBH0e_Qa7ff1tZ3VGWy3jRNh9Lg'
|
||||
LAT="35.6892"
|
||||
LON="51.3890"
|
||||
|
||||
@@ -431,7 +579,7 @@ curl -X GET "https://map.ir/fast-reverse?lat=$LAT&lon=$LON" \
|
||||
Used by `src/sms-orchestration/provider/kavenegar.service.ts`.
|
||||
|
||||
```sh
|
||||
SMS_API_KEY="<kavenegar API key>"
|
||||
SMS_API_KEY='75776C717969412B4B52306A5956462F4A714E6F6C65544D6A2B654B7566786E'
|
||||
KAVENEGAR_BASE_URL="https://api.kavenegar.com/v1/$SMS_API_KEY"
|
||||
```
|
||||
|
||||
@@ -462,9 +610,9 @@ Used by `src/sms-orchestration/provider/parsian-sms.gateway.ts`.
|
||||
`PARSIAN_SMS_URL` is expected to already include the provider URL prefix and query key before receptor. The app appends `=<receptor>&Message=<encoded message>`.
|
||||
|
||||
```sh
|
||||
PARSIAN_SMS_URL="<provider URL prefix>"
|
||||
PARSIAN_API_KEY="<package API key>"
|
||||
PARSIAN_BASIC_TOKEN="<basic token>"
|
||||
PARSIAN_SMS_URL='https://apigateway.parsianinsurance.com/api/SendSMS?ReceiverNumbers'
|
||||
PARSIAN_API_KEY='be988c9c-dbd6-494e-9c04-944ecc6426bf'
|
||||
PARSIAN_BASIC_TOKEN='UGFyc2lhbkFQSTpQYXJzaWFuQHBpMjI='
|
||||
RECEPTOR="09120000000"
|
||||
MESSAGE="Hello"
|
||||
|
||||
@@ -494,7 +642,9 @@ curl -X GET "${CW_URL}price?hamrah"
|
||||
3. `POST $AI_URL_V2/services/car-damage/detector?version=ai-v7`
|
||||
|
||||
```sh
|
||||
AI_URL_V2="<AI service base URL>"
|
||||
AI_URL_V2='https://ai-gw.ittalie.ir'
|
||||
AI_USERNAME='yara@gmail.io'
|
||||
AI_PASSWORD='123321'
|
||||
|
||||
curl -X POST "$AI_URL_V2/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
|
||||
143
scripts/fanavaran-auth.sh
Executable file
143
scripts/fanavaran-auth.sh
Executable file
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage:
|
||||
scripts/fanavaran-auth.sh <tejaratno|parsian>
|
||||
|
||||
Calls Fanavaran GetAppToken, then Login with the returned appToken.
|
||||
Outputs the appToken and authenticationToken, and writes raw responses to:
|
||||
files/fanavaran-auth/<client>/
|
||||
|
||||
Optional:
|
||||
FANAVARAN_BASE_URL can override the default API Manager base URL.
|
||||
USAGE
|
||||
}
|
||||
|
||||
client="${1:-}"
|
||||
if [[ -z "$client" || "$client" == "-h" || "$client" == "--help" ]]; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case "$client" in
|
||||
tejaratno)
|
||||
app_name='fanhab'
|
||||
app_secret='5Fa@N#A2B'
|
||||
fanavaran_username='fanhabUser'
|
||||
fanavaran_password='Fan#@2U$3er'
|
||||
corp_id='3539'
|
||||
contract_id='263'
|
||||
location='100'
|
||||
;;
|
||||
parsian)
|
||||
app_name='ParsianService'
|
||||
app_secret='P@r30@n$erv!ce'
|
||||
fanavaran_username='ParsianServiceUser'
|
||||
fanavaran_password='P@r30@n123'
|
||||
corp_id='543'
|
||||
contract_id='28'
|
||||
location='210050'
|
||||
;;
|
||||
*)
|
||||
printf 'Unknown Fanavaran client: %s\n\n' "$client" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
base_url="${FANAVARAN_BASE_URL:-https://apimanager.iraneit.com/BimeApiManager/api}"
|
||||
auth_dir="files/fanavaran-auth/$client"
|
||||
mkdir -p "$auth_dir"
|
||||
|
||||
app_token_headers="$auth_dir/get-app-token.headers"
|
||||
app_token_body="$auth_dir/get-app-token.body.json"
|
||||
login_headers="$auth_dir/login.headers"
|
||||
login_body="$auth_dir/login.body.json"
|
||||
tokens_file="$auth_dir/tokens.env"
|
||||
|
||||
extract_header() {
|
||||
local header_name="$1"
|
||||
local header_file="$2"
|
||||
awk -F': ' -v wanted="$header_name" '
|
||||
tolower($1) == tolower(wanted) {
|
||||
gsub(/\r/, "", $2)
|
||||
print $2
|
||||
exit
|
||||
}
|
||||
' "$header_file"
|
||||
}
|
||||
|
||||
extract_authentication_token_from_body() {
|
||||
local body_file="$1"
|
||||
node -e '
|
||||
const fs = require("fs");
|
||||
const path = process.argv[1];
|
||||
const body = fs.existsSync(path) ? fs.readFileSync(path, "utf8") : "";
|
||||
try {
|
||||
const json = JSON.parse(body || "{}");
|
||||
console.log(json.authenticationToken || json.authenticationtoken || json.authentication_token || "");
|
||||
} catch {
|
||||
console.log("");
|
||||
}
|
||||
' "$body_file"
|
||||
}
|
||||
|
||||
printf 'Fanavaran client: %s\n' "$client"
|
||||
printf 'Base URL: %s\n\n' "$base_url"
|
||||
|
||||
printf '1. Calling GetAppToken...\n'
|
||||
curl -sS -D "$app_token_headers" -o "$app_token_body" \
|
||||
-X POST "$base_url/EITAuthentication/GetAppToken" \
|
||||
-H "appname: $app_name" \
|
||||
-H "secret: $app_secret" \
|
||||
-H "Content-Length: 0"
|
||||
|
||||
app_token="$(extract_header "apptoken" "$app_token_headers")"
|
||||
if [[ -z "$app_token" ]]; then
|
||||
printf 'Failed to extract appToken from %s\n' "$app_token_headers" >&2
|
||||
printf 'Response body is saved at %s\n' "$app_token_body" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '2. Calling Login...\n'
|
||||
curl -sS -D "$login_headers" -o "$login_body" \
|
||||
-X POST "$base_url/EITAuthentication/Login" \
|
||||
-H "appToken: $app_token" \
|
||||
-H "userName: $fanavaran_username" \
|
||||
-H "password: $fanavaran_password" \
|
||||
-H "Content-Length: 0"
|
||||
|
||||
authentication_token="$(extract_header "authenticationtoken" "$login_headers")"
|
||||
if [[ -z "$authentication_token" ]]; then
|
||||
authentication_token="$(extract_authentication_token_from_body "$login_body")"
|
||||
fi
|
||||
|
||||
if [[ -z "$authentication_token" ]]; then
|
||||
printf 'Failed to extract authenticationToken from login response.\n' >&2
|
||||
printf 'Headers: %s\n' "$login_headers" >&2
|
||||
printf 'Body: %s\n' "$login_body" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat > "$tokens_file" <<TOKENS
|
||||
FANAVARAN_CLIENT='$client'
|
||||
FANAVARAN_BASE_URL='$base_url'
|
||||
FANAVARAN_BIME_URL='$base_url/BimeApi/v2.0'
|
||||
APP_TOKEN='$app_token'
|
||||
AUTHENTICATION_TOKEN='$authentication_token'
|
||||
CORP_ID='$corp_id'
|
||||
CONTRACT_ID='$contract_id'
|
||||
LOCATION='$location'
|
||||
TOKENS
|
||||
|
||||
printf '\nDone.\n'
|
||||
printf 'APP_TOKEN=%s\n' "$app_token"
|
||||
printf 'AUTHENTICATION_TOKEN=%s\n' "$authentication_token"
|
||||
printf 'CORP_ID=%s\n' "$corp_id"
|
||||
printf 'CONTRACT_ID=%s\n' "$contract_id"
|
||||
printf 'LOCATION=%s\n' "$location"
|
||||
printf '\nSaved token variables: %s\n' "$tokens_file"
|
||||
printf 'Saved raw GetAppToken response: %s, %s\n' "$app_token_headers" "$app_token_body"
|
||||
printf 'Saved raw Login response: %s, %s\n' "$login_headers" "$login_body"
|
||||
@@ -176,6 +176,7 @@ import {
|
||||
FanavaranAuditStatus,
|
||||
FanavaranAuditStep,
|
||||
} from "src/fanavaran/schema/fanavaran-audit-log.schema";
|
||||
import { selectLatestActiveFanavaranPolicy } from "./fanavaran-policy-selection";
|
||||
|
||||
export interface FanavaranAutoSubmitResult {
|
||||
attempted: boolean;
|
||||
@@ -3438,17 +3439,13 @@ export class ClaimRequestManagementService {
|
||||
|
||||
this.applyFanavaranDefaultFields(result);
|
||||
|
||||
try {
|
||||
const policyId = await input.resolvePolicyId();
|
||||
if (policyId !== undefined && policyId !== null) {
|
||||
result.PolicyId = policyId;
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`${input.logPrefix} Failed to get PolicyId from external API:`,
|
||||
error,
|
||||
if (policyId === undefined || policyId === null) {
|
||||
throw new BadRequestException(
|
||||
`${input.logPrefix} PolicyId is required for Fanavaran submit. Policy inquiry returned no valid policy; contact the administrator.`,
|
||||
);
|
||||
}
|
||||
result.PolicyId = policyId;
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -3567,15 +3564,18 @@ export class ClaimRequestManagementService {
|
||||
blameRequest,
|
||||
claimRequestId,
|
||||
);
|
||||
if (policyId) {
|
||||
result.PolicyId = policyId;
|
||||
if (!policyId) {
|
||||
throw new BadRequestException(
|
||||
`[Fanavaran Submit] PolicyId is required for Fanavaran submit. Policy inquiry returned no valid policy; contact the administrator.`,
|
||||
);
|
||||
}
|
||||
result.PolicyId = policyId;
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`[Fanavaran Submit] Failed to get PolicyId from external API:`,
|
||||
error,
|
||||
);
|
||||
// Continue without PolicyId if API call fails
|
||||
throw error;
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -3866,11 +3866,7 @@ export class ClaimRequestManagementService {
|
||||
}),
|
||||
);
|
||||
|
||||
const policyCount = Array.isArray(response.data)
|
||||
? response.data.length
|
||||
: 0;
|
||||
const lastPolicy =
|
||||
policyCount > 0 ? response.data[policyCount - 1] : null;
|
||||
const policyCount = Array.isArray(response.data) ? response.data.length : 0;
|
||||
|
||||
this.logger.log(
|
||||
`${logPrefix} Policy inquiry response status=${response.status} count=${policyCount}`,
|
||||
@@ -3882,20 +3878,11 @@ export class ClaimRequestManagementService {
|
||||
2,
|
||||
)}`,
|
||||
);
|
||||
if (lastPolicy && typeof lastPolicy === "object") {
|
||||
const selectedPolicy = selectLatestActiveFanavaranPolicy(response.data);
|
||||
this.logger.log(
|
||||
`${logPrefix} Policy inquiry last policy keys: ${Object.keys(
|
||||
lastPolicy,
|
||||
).join(", ")}`,
|
||||
`${logPrefix} Selected latest active policy PolicyId=${selectedPolicy.policyId} EndDate=${selectedPolicy.endDate}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (Array.isArray(response.data) && response.data.length > 0) {
|
||||
const policyId = lastPolicy?.PolicyId;
|
||||
if (policyId !== undefined && policyId !== null) {
|
||||
this.logger.log(
|
||||
`${logPrefix} Successfully retrieved PolicyId from last policy entry: ${policyId}`,
|
||||
);
|
||||
if (auditSession) {
|
||||
await this.fanavaranAuditService.recordStep({
|
||||
session: auditSession,
|
||||
@@ -3904,34 +3891,15 @@ export class ClaimRequestManagementService {
|
||||
requestUrl: policyInquiryUrl,
|
||||
httpStatus: response.status,
|
||||
responseMeta: {
|
||||
policyId,
|
||||
policyCount: response.data.length,
|
||||
policyId: selectedPolicy.policyId,
|
||||
policyEndDate: selectedPolicy.endDate,
|
||||
policyEndDateGregorian: selectedPolicy.endDateGregorian,
|
||||
policyCount,
|
||||
},
|
||||
durationMs: Date.now() - startedAt,
|
||||
});
|
||||
}
|
||||
return policyId;
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.warn(`${logPrefix} No PolicyId found in API response`);
|
||||
if (auditSession) {
|
||||
await this.fanavaranAuditService.recordStep({
|
||||
session: auditSession,
|
||||
step: FanavaranAuditStep.POLICY_INQUIRY,
|
||||
status: FanavaranAuditStatus.SUCCESS,
|
||||
requestUrl: policyInquiryUrl,
|
||||
httpStatus: response.status,
|
||||
responseMeta: {
|
||||
policyId: null,
|
||||
policyCount: Array.isArray(response.data)
|
||||
? response.data.length
|
||||
: 0,
|
||||
},
|
||||
durationMs: Date.now() - startedAt,
|
||||
});
|
||||
}
|
||||
return null;
|
||||
return selectedPolicy.policyId;
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : "unknown policy inquiry error";
|
||||
@@ -3948,8 +3916,11 @@ export class ClaimRequestManagementService {
|
||||
});
|
||||
}
|
||||
this.logger.warn(
|
||||
`${logPrefix} Policy inquiry failed; continuing with empty PolicyId: ${errorMessage}`,
|
||||
`${logPrefix} Policy inquiry failed: ${errorMessage}`,
|
||||
);
|
||||
if (error instanceof BadRequestException) {
|
||||
throw error;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -4011,6 +3982,9 @@ export class ClaimRequestManagementService {
|
||||
`[Fanavaran Submit] Error getting PolicyId from external API:`,
|
||||
error,
|
||||
);
|
||||
if (error instanceof BadRequestException) {
|
||||
throw error;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { BadRequestException } from "@nestjs/common";
|
||||
import { selectLatestActiveFanavaranPolicy } from "./fanavaran-policy-selection";
|
||||
|
||||
describe("selectLatestActiveFanavaranPolicy", () => {
|
||||
const today = "2026-06-30";
|
||||
|
||||
it("selects the policy with the latest EndDate when newest is first", () => {
|
||||
const selected = selectLatestActiveFanavaranPolicy(
|
||||
[
|
||||
{ PolicyId: 4826286, EndDate: "1405/09/23" },
|
||||
{ PolicyId: 3719458, EndDate: "1404/09/06" },
|
||||
{ PolicyId: 2800731, EndDate: "1403/09/05" },
|
||||
],
|
||||
today,
|
||||
);
|
||||
|
||||
expect(selected.policyId).toBe(4826286);
|
||||
expect(selected.endDate).toBe("1405/09/23");
|
||||
});
|
||||
|
||||
it("selects the policy with the latest EndDate when newest is last", () => {
|
||||
const selected = selectLatestActiveFanavaranPolicy(
|
||||
[
|
||||
{ PolicyId: 2800731, EndDate: "1403/09/05" },
|
||||
{ PolicyId: 3719458, EndDate: "1404/09/06" },
|
||||
{ PolicyId: 4826286, EndDate: "1405/09/23" },
|
||||
],
|
||||
today,
|
||||
);
|
||||
|
||||
expect(selected.policyId).toBe(4826286);
|
||||
});
|
||||
|
||||
it("rejects when no policies are returned", () => {
|
||||
expect(() => selectLatestActiveFanavaranPolicy([], today)).toThrow(
|
||||
BadRequestException,
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects when the latest policy is expired", () => {
|
||||
expect(() =>
|
||||
selectLatestActiveFanavaranPolicy(
|
||||
[
|
||||
{ PolicyId: 3719458, EndDate: "1404/09/06" },
|
||||
{ PolicyId: 2800731, EndDate: "1403/09/05" },
|
||||
],
|
||||
today,
|
||||
),
|
||||
).toThrow(BadRequestException);
|
||||
});
|
||||
|
||||
it("rejects when the latest policy has no valid PolicyId", () => {
|
||||
expect(() =>
|
||||
selectLatestActiveFanavaranPolicy(
|
||||
[
|
||||
{ PolicyId: 3719458, EndDate: "1404/09/06" },
|
||||
{ PolicyId: null, EndDate: "1405/09/23" },
|
||||
],
|
||||
today,
|
||||
),
|
||||
).toThrow(BadRequestException);
|
||||
});
|
||||
});
|
||||
95
src/claim-request-management/fanavaran-policy-selection.ts
Normal file
95
src/claim-request-management/fanavaran-policy-selection.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { BadRequestException } from "@nestjs/common";
|
||||
import { jalaliToGregorianDate } from "src/helpers/date-jalali";
|
||||
import { gregorianDateInIran } from "src/helpers/iran-datetime";
|
||||
|
||||
export type FanavaranPolicyInquiryRow = {
|
||||
PolicyId?: unknown;
|
||||
EndDate?: unknown;
|
||||
};
|
||||
|
||||
export type SelectedFanavaranPolicy = {
|
||||
policy: FanavaranPolicyInquiryRow;
|
||||
policyId: number;
|
||||
endDate: string;
|
||||
endDateGregorian: string;
|
||||
};
|
||||
|
||||
type DatedFanavaranPolicy = {
|
||||
policy: FanavaranPolicyInquiryRow;
|
||||
endDate: string;
|
||||
endDateGregorian: string;
|
||||
};
|
||||
|
||||
const NO_POLICY_MESSAGE =
|
||||
"No Fanavaran policies were found for the insurer national code. PolicyId is required; contact the administrator.";
|
||||
|
||||
const EXPIRED_POLICY_MESSAGE =
|
||||
"The latest insurance policy is expired and cannot be sent to Fanavaran. Contact the administrator.";
|
||||
|
||||
const INVALID_POLICY_MESSAGE =
|
||||
"Fanavaran policy inquiry returned policies without a valid PolicyId or EndDate. PolicyId is required; contact the administrator.";
|
||||
|
||||
function parsePolicyId(value: unknown): number | null {
|
||||
if (value === null || value === undefined) return null;
|
||||
if (typeof value === "string" && value.trim() === "") return null;
|
||||
const id = Number(value);
|
||||
return Number.isFinite(id) && id > 0 ? id : null;
|
||||
}
|
||||
|
||||
function normalizeEndDate(value: unknown): {
|
||||
endDate: string;
|
||||
endDateGregorian: string;
|
||||
} | null {
|
||||
if (value === null || value === undefined) return null;
|
||||
const endDate = String(value).trim();
|
||||
if (!endDate) return null;
|
||||
|
||||
const endDateGregorian = jalaliToGregorianDate(endDate);
|
||||
if (!endDateGregorian) return null;
|
||||
|
||||
return { endDate, endDateGregorian };
|
||||
}
|
||||
|
||||
export function selectLatestActiveFanavaranPolicy(
|
||||
policies: unknown,
|
||||
todayGregorian: string = gregorianDateInIran(new Date()),
|
||||
): SelectedFanavaranPolicy {
|
||||
if (!Array.isArray(policies) || policies.length === 0) {
|
||||
throw new BadRequestException(NO_POLICY_MESSAGE);
|
||||
}
|
||||
|
||||
const candidates = policies
|
||||
.map((policy) => {
|
||||
if (!policy || typeof policy !== "object") return null;
|
||||
const row = policy as FanavaranPolicyInquiryRow;
|
||||
const endDate = normalizeEndDate(row.EndDate);
|
||||
if (!endDate) return null;
|
||||
return {
|
||||
policy: row,
|
||||
endDate: endDate.endDate,
|
||||
endDateGregorian: endDate.endDateGregorian,
|
||||
};
|
||||
})
|
||||
.filter((policy): policy is DatedFanavaranPolicy => policy !== null);
|
||||
|
||||
if (candidates.length === 0) {
|
||||
throw new BadRequestException(INVALID_POLICY_MESSAGE);
|
||||
}
|
||||
|
||||
const latest = candidates.reduce((currentLatest, candidate) =>
|
||||
candidate.endDateGregorian > currentLatest.endDateGregorian
|
||||
? candidate
|
||||
: currentLatest,
|
||||
);
|
||||
|
||||
if (latest.endDateGregorian < todayGregorian) {
|
||||
throw new BadRequestException(EXPIRED_POLICY_MESSAGE);
|
||||
}
|
||||
|
||||
const policyId = parsePolicyId(latest.policy.PolicyId);
|
||||
if (policyId === null) {
|
||||
throw new BadRequestException(INVALID_POLICY_MESSAGE);
|
||||
}
|
||||
|
||||
return { ...latest, policyId };
|
||||
}
|
||||
Reference in New Issue
Block a user