---
title: Agency, agent, and provider customer resolution
summary: Resolves agency, agent, and provider identifiers into local customer/contact records, creating and linking them on first use and marking generated records inactive when the source data is not currently effective.
status: draft
owner: SAIF
last_reviewed: 2026-07-30
library: SAIF.Business.CustomerContact
reference_type: package
operation_id: OP-CC-09
trigger: Caller invokes GetCustomerInfoForAgent or GetCustomerIDForProvider with an external agency/agent/provider identifier
---

# Agency, agent, and provider customer resolution

`mdlCustomerContact` resolves agency, agent, and provider identifiers from source reference tables into local `clsCustomer`/`clsContact` records, caching the mapping in `CUSTOMER_AGENCY`, `CUSTOMER_AGENT`, or `CUSTOMER_PROVIDER` so future lookups skip record creation. These three flows are grouped on one page because agent resolution directly calls agency resolution, and all three apply the same source-effective/expiration-date check to decide whether the generated local record should be created inactive.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-CC-09 |
| **Trigger** | `mdlCustomerContact.GetCustomerInfoForAgent(dpi, agentID, create)` or `mdlCustomerContact.GetCustomerIDForProvider(dpi, providerID)` |
| **Inputs** | `AgentID` (resolves its owning `AgencyID` first), `AgencyID`, or `ProviderID`; optional `create` flag (agent/agency path only) |
| **Outputs** | `AgentInfo` (agency/agent customer and contact IDs) or a provider `CustomerID`; newly created `COMPANY`/`PERSON` rows and their `CUSTOMER_AGENCY`/`CUSTOMER_AGENT`/`CUSTOMER_PROVIDER` mapping rows when no mapping existed |
| **Primary components** | `mdlCustomerContact.GetCustomerInfoForAgent`, `mdlCustomerContact.CreateAgency`, `mdlCustomerContact.CreateAgent`, `mdlCustomerContact.GetCustomerIDForProvider` |
| **Entry point** | `Business\SAIF.Business.CustomerContact\mdlCustomerContact.vb` |

## Steps

1. **Resolve an agent's owning agency.** `GetCustomerInfoForAgent(dpi, agentID, create)` reads `AGENT` to find the agent's `AgencyID` ([SQL-063](../sql/SQL-063.md), [AGENT](../database-tables.md#agent)). If no agency ID is found, `GetCustomerInfoForAgent` returns an `AgentInfo` with a zero `AgencyCustomerID` and does not attempt agent creation.
2. **Resolve or create the agency customer (`CreateAgency`).**
   - Look up an existing mapping in `CUSTOMER_AGENCY` for the agency ID ([SQL-047](../sql/SQL-047.md), [CUSTOMER_AGENCY](../database-tables.md#customer-agency)).
   - If none exists and `create` is true: read `AGENCY` for the source record ([SQL-046](../sql/SQL-046.md), [AGENCY](../database-tables.md#agency)), then build a new `clsCustomer` with `CustomerTypeCode = "AGNCY"`, `InternalReferenceID` set to the agency ID, `CustomerName` from `agency_name`, and `TaxID` from `agency_feid`.
   - **Determine active status** ([BR-008](../business-rules.md#br-008-generated-agency-agent-and-provider-records-can-be-forced-inactive)): if `agency_exp_date` is empty and `agency_eff_date` is in the future, or if `agency_exp_date` is in the past, set `Cust.IsActive = False` before saving.
   - Save the new customer ([SQL-018](../sql/SQL-018.md) inserts the `CUSTOMER_AGENCY` mapping row, [CUSTOMER_AGENCY](../database-tables.md#customer-agency), after the `COMPANY` row itself saves through [Customer CRUD with child persistence](customer-crud-with-child-persistence.md)).
     - On a mapping-insert row count ≠ 1: roll back by calling `Cust.Delete()` and `Cust.Save()`, leaving `CustID = 0`.
3. **Resolve or create the agent contact (`CreateAgent`).**
   - Look up an existing mapping in `CUSTOMER_AGENT` for the agent ID ([SQL-049](../sql/SQL-049.md), [CUSTOMER_AGENT](../database-tables.md#customer-agent)).
     - **Uncertain / likely defect:** the lookup query binds `inAgentInfo.AgencyID` to the `agent_num = :ID` filter instead of the agent's own ID, which could return the wrong (or no) mapping when agency and agent identifiers differ.*
   - If none exists and `create` is true: ensure the agency customer exists via `CreateAgency(...)` (step 2), then read `AGENT` for the source record ([SQL-048](../sql/SQL-048.md), [AGENT](../database-tables.md#agent)).
   - Add a new `clsCustomerContact` under the agency customer's `Contacts` with `RelationshipCode = "AGENT"` and `Title` set from `agent_name`; set the contact's `AlternateName`, `EffectiveDate`, and (if present) `ExpirationDate`.
   - **Determine active status** ([BR-008](../business-rules.md#br-008-generated-agency-agent-and-provider-records-can-be-forced-inactive)): if `EffectiveDate` is in the future, or a real `ExpirationDate` has already passed, set `Contact.IsActive = False`.
   - Save the customer cascade — this persists the new `PERSON` row and the `COMPANY_PERSON` relationship (see [Customer-contact relationship CRUD](customer-contact-relationship-crud.md)) — then insert the `CUSTOMER_AGENT` mapping row ([SQL-019](../sql/SQL-019.md), [CUSTOMER_AGENT](../database-tables.md#customer-agent)).
     - On a mapping-insert row count ≠ 1: roll back by removing the new contact from `Cust.Contacts` and re-saving, leaving the agent unresolved.
4. **Resolve or create the provider customer.** `GetCustomerIDForProvider(dpi, providerID)`:
   - Look up an existing mapping in `CUSTOMER_PROVIDER` for the provider ID ([SQL-059](../sql/SQL-059.md), [CUSTOMER_PROVIDER](../database-tables.md#customer-provider)).
   - If none exists: read `PROVIDER` joined to `PROVIDER_TAX_ID` on the provider's primary tax ID ([SQL-060](../sql/SQL-060.md), [PROVIDER](../database-tables.md#provider), [PROVIDER_TAX_ID](../database-tables.md#provider-tax-id)), and build a new `clsCustomer` with `CustomerTypeCode = "MPROV"`, `CustomerName` from `registered_name`, and `TaxID` from `primary_tax_id`.
   - **Determine active status** ([BR-008](../business-rules.md#br-008-generated-agency-agent-and-provider-records-can-be-forced-inactive)): the same effective/expiration date check as agency resolution sets `Cust.IsActive = False` when the source tax-ID record is not currently effective.
   - Provider address creation code exists in source but is commented out, so no address is created from the provider's registered address even when the customer is active.*
   - Save the new customer, then insert the `CUSTOMER_PROVIDER` mapping row ([SQL-024](../sql/SQL-024.md), [CUSTOMER_PROVIDER](../database-tables.md#customer-provider)).
     - On a mapping-insert row count ≠ 1: roll back by calling `Cust.Delete()` and `Cust.Save()`, leaving `CustID = 0`.

## Process flow

The following diagram visualizes the agency → agent chain and the parallel provider flow.

```mermaid
flowchart TD
	Start([GetCustomerInfoForAgent]) --> AgentAgency[Read AGENT to find AgencyID]
	AgentAgency --> AgencyFound{AgencyID > 0?}
	AgencyFound -->|No| ReturnEmpty([Return AgentInfo with no customer])
	AgencyFound -->|Yes| CreateAgent[CreateAgent]
	CreateAgent --> AgentMapped{CUSTOMER_AGENT<br>mapping exists?}
	AgentMapped -->|Yes| ReturnAgent([Return existing customer/contact IDs])
	AgentMapped -->|No, create=true| CreateAgency[CreateAgency]
	CreateAgency --> AgencyMapped{CUSTOMER_AGENCY<br>mapping exists?}
	AgencyMapped -->|Yes| AgencyReady
	AgencyMapped -->|No, create=true| ReadAgency[Read AGENCY; check dates;<br>save new COMPANY; insert mapping]
	ReadAgency --> AgencyReady[AgencyCustomerID resolved]
	AgencyReady --> ReadAgent[Read AGENT; add clsCustomerContact<br>RelationshipCode=AGENT; check dates]
	ReadAgent --> SaveAgent[Save customer cascade;<br>insert CUSTOMER_AGENT mapping]
	SaveAgent --> RowsAgent{Mapping insert rows = 1?}
	RowsAgent -->|No| RollbackAgent([Remove contact; re-save; unresolved])
	RowsAgent -->|Yes| ReturnAgent
	StartP([GetCustomerIDForProvider]) --> ProviderMapped{CUSTOMER_PROVIDER<br>mapping exists?}
	ProviderMapped -->|Yes| ReturnProvider([Return existing CustomerID])
	ProviderMapped -->|No| ReadProvider[Read PROVIDER + PROVIDER_TAX_ID;<br>check dates; save new COMPANY]
	ReadProvider --> InsertProviderMap[Insert CUSTOMER_PROVIDER mapping]
	InsertProviderMap --> RowsProvider{Rows = 1?}
	RowsProvider -->|No| RollbackProvider([Delete and re-save; CustID = 0])
	RowsProvider -->|Yes| ReturnProvider
```

## Failure and edge-case behavior
- All three creation paths roll back the newly created `clsCustomer` (or, for agent, remove the new contact) when the mapping-table insert fails, so a failed mapping insert never leaves an orphaned mapping-less customer with `CustID` reported as non-zero — but the customer/contact row itself may still exist in an unmapped state pending garbage collection by a later process.*
- Generated agency, agent, and provider records can be persisted inactive from the moment of creation when the source effective/expiration dates indicate the relationship is not currently in force ([BR-008](../business-rules.md#br-008-generated-agency-agent-and-provider-records-can-be-forced-inactive)).
- The `CreateAgent` existing-mapping lookup is suspected to bind the wrong identifier (`AgencyID` instead of the agent's own ID) to its `agent_num` filter, which could cause repeated re-creation attempts or a false match against an unrelated agent record.*
- Provider address creation is present in source as commented-out code, so provider-based customers are never given an address through this flow even though the query already retrieves address fields.*

> **Note:** Statements marked with `*` were inferred from code structure, naming
> conventions, or contextual evidence where direct confirmation was not available
> in source artifacts.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [SQL catalog](../sql/index.md)
- [Database tables](../database-tables.md)

## Sources
- `Business\SAIF.Business.CustomerContact\mdlCustomerContact.vb`
- `Business\SAIF.Business.CustomerContact\AgentInfo.vb`
