import MermaidChart from '../../../components/MermaidChart';
Developer Guide: Sales for Mortgages Architecture 🏗️
This document outlines the technical architecture, data model, automation triggers, and configuration patterns for the Valstorm Sales for Mortgages system.
This system relies heavily on App Metadata for configuration and Record Triggers for business logic. It is designed to minimize hardcoding and maximize bulk-processing efficiency.
1. Entity Relationship Model (ERD)
The system distinguishes between the human (Contact), the sales opportunity (Lead), and the financial product (Loan).
<MermaidChart chart={` erDiagram CONTACT ||--o{ LEAD : "Applied For" CONTACT ||--o{ LOAN : "Primary Borrower" CONTACT ||--o{ LOAN : "Co-Borrower" LEAD |o--|| LOAN : "Converted To" LEAD }o--|| CONTACT : "Referred By"
CONTACT { string id PK string email string phone string salesforce_id } LEAD { string id PK string status string loan_purpose date in_contract_date json contact FK } LOAN { string id PK string status json originating_lead FK json primary_borrower FK currency loan_amount percent ltv } `} />
2. Configuration: App Metadata
Business logic is controlled via two primary App Metadata configurations. Modifying these JSON objects allows you to alter system behavior without deploying code.
Config A: Sales for Mortgages
Controls timestamp mapping, lead-to-loan conversion, and loan status logic.
{ "lead_status_date_time_mapping": { "New": "new_lead_date", "In Contract": "in_contract_date", "Won": "won_date" // ... maps Status (Key) to Field API Name (Value) }, "lead_status_date_time_mapping_active": true, "lead_to_loan_creation_active": true, "lead_to_loan_creation_mapping": { "originating_lead": "id", "primary_borrower": "contact", "purpose": "loan_purpose", "property_address": "address" }, "loan_status_date_time_mapping": { "Contract Received": "contract_received_date", "Funded": "funded_date" }, "loan_status_date_time_mapping_active": true }
Config B: Lead - Contact Sync
Controls the identity resolution strategy when new Leads are ingested via API or Webhooks.
{ "active": true, "identity_priority": ["email", "phone"], "sync_mapping": { "first_name": "first_name", "email": "email", "phone": "phone" }, "options": { "create_missing_contact": true } }
3. Automation Logic (Triggers)
Trigger 1: Lead Identity Resolution
File: lead_before_upsert.py (Logic branch)
Context: Create/Update
Before a Lead is saved, the system attempts to link it to an existing Contact to prevent data duplication.
- Extraction: Scans incoming payload for
emailorphonebased onidentity_priority. - Bulk Query: Executes a single SQL query to find matches in the
Contactcollection using optimized buckets. - Linking:
- Match Found: Links the Lead to the existing Contact ID.
- No Match: Creates a new Contact record (using
create_recordsbypassing recursion triggers) and links the Lead to it.
Trigger 2: Lead Lifecycle & Conversion
File: lead_sales_for_mortgages_upsert.py
Context: Before Create/Update
This trigger handles timestamping and the critical hand-off to the Loan object.
A. Status Timestamping
- Logic: Checks
lead_status_date_time_mapping. - Condition: If
statuschanges AND the target date field is empty. - Action: Sets current UTC timestamp to the target field.
B. Loan Generation ("In Contract")
- Condition: Lead Status changes to "In Contract".
- Action: Prepares a
Loanrecord payload. - Mapping: Uses
lead_to_loan_creation_mappingmetadata. If metadata is missing, falls back to hardcoded defaults. - Naming: Generates Loan Name:
{Last Name} - {Street Address} - {Purpose}. - Execution: Collects all Loan payloads and executes
create_records('loan', ...)in a bulk operation outside the processing loop.
Trigger 3: Loan Processing Logic
File: loan_before_upsert.py
Context: Before Create/Update
Handles the underwriting data integrity and feedback loop to the originating Lead.
A. Auto-Naming
- Condition: If
property_addressorpurposechanges. - Format:
{Borrower Name} - {Line1, City, State} - {Purpose}. - Fallbacks: Handles missing addresses gracefully (
No Street,No City).
B. Financial Calculations (LTV)
- Condition: If
loan_amountandpurchase_priceare present and valid (>0). - Formula:
- Output: Stored as a decimal (e.g.,
0.80).
C. Feedback Loop (Loan Lead)
-
Condition: Loan Status changes to Funded or Canceled.
-
Action: Updates the
originating_leadrecord. -
Funded Lead Status:
Won -
Canceled Lead Status:
Preapproved -
Execution: Uses
update_records('lead', ...)in a bulk operation.
4. Picklist Dependencies
The system relies on specific Picklist values to trigger logic. If you modify these Tags, ensure you update the App Metadata JSON accordingly.
Lead Statuses
| Value | Trigger Effect |
|---|---|
| New | Timestamps new_lead_date |
| In Contract | Creates Loan Record |
| Won | Timestamps won_date |
Loan Statuses
| Value | Trigger Effect |
|---|---|
| Contract Received | Default status on creation |
| Funded | Updates originating Lead to Won |
| Canceled | Updates originating Lead to Preapproved |
5. Maintenance & Troubleshooting
Disabling Automation
To stop specific automations without deploying code, set the following keys to false in App Metadata:
lead_status_date_time_mapping_activelead_to_loan_creation_activeloan_status_date_time_mapping_active
Common Errors
- Missing Loan Name: Usually caused by a Lead having no
Contactlinked or noAddresswhen moved to "In Contract". The system defaults to "Unnamed Lead" or "No Street". - Duplicate Contacts: Check the
Lead - Contact Syncmetadata. Ensureidentity_priorityincludes unique identifiers like email or phone. - LTV Calculation Fails: Ensure
purchase_priceis not 0. The trigger catchesZeroDivisionErrorsilently, so the field will simply remain empty.