// Aria Lead-to-Policy — sandbox app 385, org 53. // Workflow `e29c3c33` (v1). All ids verified against postgres-sandbox. export const ORG_ID = '53'; export const APP_ID = '385'; export const WORKFLOW_UUID = 'e29c3c33-e294-4051-8325-a20de9ed99fc'; // Record views (POST /app/385/view/recordview, body.rv_template_uid). export const RECORD_VIEW_IDS = { ALL_LEADS: '49a5d651-8055-451a-8348-b1ea65cc6357', // comprehensive — drives the pipeline } as const; // Detail views (GET /app/385/view/detailview/{uid}). export const DETAIL_VIEW_IDS = { LEAD: 'aa96f572-b480-4623-ae9c-9ce0c44b8626', } as const; // Onboard activity uid. The trigger writes welcome_email_status + policy_doc // onto the lead record (read from the recordview). onboarding_notes stays a // `local` field — it persists only in the activity submission, surfaced via the // audit feed, where Customer 360 reads it. (welcome_pack_sent is dead.) export const ONBOARD_ACTIVITY_UID = 'ae415f4e-a33e-432e-882f-733238de8bcc'; // Activity uids + their field ids (data{} keys). From introspect. export const ACTIVITIES = { RECOMMEND_PRODUCT: { uid: 'c0e2004e-2d70-45f2-9cd5-734331010906', fields: { product: 'recommended_product_input', // lookup → tbl_products (see PRODUCT_LOOKUP) sumAssured: 'sum_assured_input', // number premiumAmount: 'premium_amount_input', // number premiumFrequency: 'premium_frequency_input', // select riders: 'riders_input', // multiselect notes: 'recommendation_notes', // longtext saValid: 'sa_valid_input', // boolean — computed (see lib/recommend.ts); gates Product Recommended }, }, COLLECT_DOCUMENTS: { uid: 'a8eb0faa-17f9-4491-a161-bd6b533adfd1', fields: { panOcr: 'pan_ocr_2', // ocr aadhaarOcr: 'aadhar_ocr_2', // ocr salarySlip: 'salary_slip_doc_input', // file aadhaarNumber: 'aadhaar_number_input', // text panNumber: 'pan_number_input', // text verifiedIncome: 'verified_income_input', // number docStatus: 'doc_status_input', // select (req) }, }, SUBMIT_UNDERWRITING: { uid: 'ed8ec0dc-4c2c-4d9e-b798-757f6a9d4730', fields: { status: 'underwriting_status_input', // select (req) — the verdict ref: 'underwriting_ref_input', // id_gen (backend-generated UW-YYYY-####; do NOT send) }, }, ISSUE_POLICY: { uid: '7b8fb734-a780-4c6a-837b-c5e7ca28e489', fields: { issueDate: 'policy_issue_date_input', // date (req) term: 'policy_term_input', // number (years) maturity: 'maturity_date_input', // date (computed = issue + term) premium: 'premium_amount_confirm', // number number: 'policy_number_input', // id_gen (backend-generated POL-YYYY-####; do NOT send) }, }, } as const; // Select options + lookup templates are no longer hardcoded here — screens // read them live from the form schema (api/schema.ts → fieldFromSchema). // Canonical ordered lifecycle — matches live `current_state_name` 1:1 so a // state name maps straight to a stepper/kanban index. Terminal "Disqualified" // is handled out-of-band (STAGE_OF_STATE returns -1). export const STAGES = [ 'New Lead', 'Qualified', 'Assigned', 'Contacted', 'Meeting Scheduled', 'Product Recommended', 'Documents Collected', 'Eligibility Assessed', 'Underwriting', 'Policy Issued', 'Customer 360', ] as const; export function stageOfState(stateName?: string): number { if (!stateName) return 0; const i = STAGES.indexOf(stateName as (typeof STAGES)[number]); return i; // -1 for Disqualified / unknown } // UID -> human name maps, for rendering the audit trail (which stores raw // activity + state UIDs). export const STATE_NAME_BY_UID: Record = { 'e469b82f-924e-47d9-9a9f-65b23d270047': 'New Lead', '41c7a19f-f2fa-4b54-9dd5-310ae982d6c6': 'Qualified', '8f038dc4-81b7-48c6-ac5b-b28c42b83214': 'Assigned', 'f2aa6dbd-2149-455a-9466-58bd5beb6997': 'Contacted', 'd5a2e33a-d2fe-4bfb-816f-61e064b77e97': 'Meeting Scheduled', '2f33444a-1af0-4fa0-9461-eb8fc79ec175': 'Product Recommended', 'a0d60916-c332-4d0a-8d02-501e22616b8f': 'Documents Collected', '0a2a0a66-129a-4b2a-8e4e-1defc5ef85f6': 'Eligibility Assessed', 'f8158695-5ac7-4e3a-9eda-bbab3fd8bbb3': 'Underwriting', 'c964fe5e-c2a8-4d30-825c-fefa97a74368': 'Policy Issued', '3d783ea2-b3d0-4eca-9424-9f8451c5d986': 'Customer 360', '0ce7794e-30ba-4f09-b81e-26d5c9fec5b7': 'Disqualified', }; export const ACTIVITY_NAME_BY_UID: Record = { 'cc1a73d5-61e1-4416-af30-2b1eeb623a58': 'Capture Lead', '6b741e44-37f2-4bb0-90ab-ed139f03b7b3': 'Qualify Lead', 'da61aaa1-003b-4601-9f42-f93ff230497f': 'Assign Lead', 'fea6b3a8-846d-4f6f-8e92-033c4cf4b6e2': 'Log First Contact', 'c444d32f-ed6a-4e36-b8b7-b82e73a141d4': 'Schedule Meeting', 'c0e2004e-2d70-45f2-9cd5-734331010906': 'Recommend Product', 'a8eb0faa-17f9-4491-a161-bd6b533adfd1': 'Collect Documents', 'ba3d3e7f-0d3f-49f2-a156-6cace33c228a': 'Assess Eligibility', 'ed8ec0dc-4c2c-4d9e-b798-757f6a9d4730': 'Submit Underwriting', '7b8fb734-a780-4c6a-837b-c5e7ca28e489': 'Issue Policy', 'ae415f4e-a33e-432e-882f-733238de8bcc': 'Onboard Customer', 'ea99fc60-caba-45c1-afc1-7386a2fa9220': 'Disqualify', }; // AI employees (user_id -> identity). Used to label the decision stream and // to attribute the "owner" of an in-flight lead. export const AI_EMPLOYEES: Record = { '29180': { name: 'Aria', role: 'Lead Qualifier' }, '29181': { name: 'Aria Engage', role: 'Calls & Scheduling' }, '29182': { name: 'Aria Underwrite', role: 'Underwriting' }, '29188': { name: 'Aria Advisor', role: 'Product Recommendation' }, }; // Aria Advisor's internal recommendation activity. It fires from the Schedule // Meeting trigger (after the meeting is booked) and records the product // recommendation as a decision — surfaced in the Recommend Product form. Its // data{} carries the ai_* fields below. export const AI_RECOMMENDATION = { activityUid: '14d734da-f1e2-49af-9715-2146d26c0e1a', aiUserId: '29188', fields: { product: 'ai_recommended_product_input', // product name (string) sumAssured: 'ai_suggested_sum_assured_input', // number rationale: 'ai_recommendation_rationale_input', // text confidence: 'ai_recommendation_confidence_input', // 0–100 }, } as const; // Which AI employee is driving a given state (for owner attribution when no // human sales agent is set). export function aiEmployeeForState(stateName?: string): { name: string; role: string } | null { switch (stateName) { case 'New Lead': return AI_EMPLOYEES['29180']; case 'Assigned': case 'Contacted': case 'Meeting Scheduled': case 'Product Recommended': return AI_EMPLOYEES['29181']; case 'Documents Collected': case 'Eligibility Assessed': return AI_EMPLOYEES['29182']; default: return null; } } // --- Screen RBAC (mirrors tbl_appconfig_screens.permissions for app 385) --- // Roles that bypass screen gating entirely (platform admins see every screen). export const SUPER_ROLES = ['Super Admin', 'Admin']; // Route path → roles allowed to see the screen. A path absent here is open to // all authenticated users (e.g. the contextual Lead 360 detail). `/schedule` // is an Aria-only screen — managers oversee agent meetings, agents see theirs. export const SCREEN_ACCESS: Record = { '/pipeline': ['Sales Agent', 'Underwriter', 'Branch Manager'], '/qualify': ['Branch Manager'], '/engage': ['Sales Agent'], '/documents': ['Underwriter'], '/underwriting': ['Underwriter', 'Branch Manager'], '/schedule': ['Sales Agent', 'Branch Manager'], }; /** Whether `roles` may see the screen at `path`. Super-admins always can; * unlisted paths are open. */ export function canSeeScreen(roles: string[] | undefined, path: string): boolean { const allowed = SCREEN_ACCESS[path]; if (!allowed) return true; const r = roles ?? []; if (r.some((role) => SUPER_ROLES.includes(role))) return true; return r.some((role) => allowed.includes(role)); }