---
title: Provider address retrieval
summary: Retrieves all addresses for a provider location and resolves the active correspondence, physical, and payment addresses, including a cross-location parent-address fallback used for billing.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-07-30
library: SAIF.Business.View.Provider
reference_type: package
operation_id: OP-VP-03
trigger: Caller reads clsProviderLocation.Addresses, or calls clsProviderAddressCollection.GetProviderAddressCollection(dpi, providerLocationID) directly, or calls clsProviderAddress.GetAddress(...)
---

# Provider address retrieval

Provider address retrieval reads every `PROVIDER_ADDRESS` row for a location (left-outer-joined so a location with no addresses still returns a placeholder row of location-only data), then offers in-memory filtering to find the active correspondence, physical, or payment address as of a given date. A separate set of shared `GetAddress` overloads resolves a billing address across a child/parent location pair, preferring the parent's address when one is set.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-VP-03 |
| **Trigger** | `clsProviderAddressCollection.GetProviderAddressCollection(inDataportal, inProviderLocationID)` — invoked directly or via `clsProviderLocation.Addresses`; `clsProviderAddress.GetAddress(dpi, providerLocationID, parentProviderLocationID, addressType)` and its overloads |
| **Inputs** | Provider location ID (Decimal); for `GetAddress`, a child provider location or ID, a parent provider location ID, and an optional `AddressTypes` (Correspondence, Payment, or Physical; defaults to Correspondence) |
| **Outputs** | A populated `clsProviderAddressCollection`; `ActiveAddresses()`/`ActiveAddresses(date)`, `CorrespondenceAddress`, `PhysicalAddress`, `PaymentAddress` derived subsets; a single resolved `clsProviderAddress` from `GetAddress` |
| **Primary components** | `clsProviderAddressCollection.GetProviderAddressCollection`, `clsProviderAddressCollection.CalculatePrimaryAddress`, `clsProviderAddressCollection.FilterAddress`, `clsProviderAddress.GetAddress` (three overloads) |
| **Entry point** | `Business\SAIF.Business.View.Provider\clsProviderAddressCollection.vb` |

## Steps

1. **Request all addresses for a location.** `GetProviderAddressCollection` builds a criteria carrying the provider location ID and retrieves through the standard data-portal path.
2. **Bind the location parameter.** `BeforeRetrieve` binds `:ProviderLocationID` and caches it on the collection instance.
3. **Execute the join query.** The shared retrieve SQL left-outer-joins `PROVIDER_LOCATION` to `PROVIDER_ADDRESS` on location ID, so a location with zero addresses still returns exactly one row of `NULL` address fields ([SQL-005](../sql/SQL-005-saif-business-view-provider.md), [SQL-006](../sql/SQL-006-saif-business-view-provider.md), [PROVIDER_LOCATION](../database-tables.md#provider_location), [PROVIDER_ADDRESS](../database-tables.md#provider_address)). The same SQL constant (`clsProviderAddress.RETRIEVE_SQL`) is shared by both the collection's own criteria and a per-item `clsProviderAddress.Criteria`, so [SQL-005](../sql/SQL-005-saif-business-view-provider.md) and [SQL-006](../sql/SQL-006-saif-business-view-provider.md) are the same statement catalogued from each criteria's perspective.*
4. **Populate the collection.** `AfterRetrieve` iterates the result set, constructs a `clsProviderAddress` per row (seeded with the location ID), calls `Load`, and adds each to the collection.
   - Each row's termination date defaults to `Date.MaxValue` when the underlying `EXP_DATE` is null, so an address with no expiration is treated as open-ended rather than immediately expired.
5. **Filter to active addresses.** `ActiveAddresses()` (defaulting to now) or `ActiveAddresses(activeDate)` returns the subset whose start/termination window contains the target date, and records that date on the returned sub-collection.
6. **Resolve a primary address by type.** `CorrespondenceAddress`, `PhysicalAddress`, and `PaymentAddress` each call `CalculatePrimaryAddress` with their respective `AddressTypes` value:
   - Correspondence: prefer an active row coded `COR`; if none, fall back to a `PHY` (physical) row.
   - Payment: prefer an active row coded `PAY`; if none, fall back to `PHY`; if still none, fall back to `COR`.
   - Physical (or any other type): prefer `PHY`; if none, fall back to `COR`.
   - If no address matches any fallback: return a new, empty `clsProviderAddress` rather than `Nothing`.
7. **Resolve a billing address across a location pair.** `GetAddress` (single-provider-location overload) retrieves both the child and parent provider locations and delegates to the pair-based overload.

   > **Origin**: The child and parent location retrievals delegate to the [Provider location retrieval](provider-location-retrieval.md) operation implemented in this library.
8. **Compare child and parent addresses.** The pair-based `GetAddress` reads each location's `ActiveAddresses.PrimaryAddress(addressType)` (step 6, evaluated against each location independently) and prefers the parent's address when it is not empty, cloning it and overwriting its display name and location ID with the child's business name and location ID so the billing address still reads as belonging to the child.
   - If the parent's resolved address is empty: return the child's own resolved address unchanged.

## Process flow

The following diagram visualizes this operation's control flow.

```mermaid
flowchart TD
	Start([Addresses requested for a location]) --> Exec[Execute left outer join query<br>PROVIDER_LOCATION to PROVIDER_ADDRESS]
	Exec --> Loop[For each row: create clsProviderAddress,<br>Load, add to collection]
	Loop --> Done([Populated collection returned])
	Done --> Use{Consumer request}
	Use -->|ActiveAddresses at date| Filter[Filter to rows whose window<br>covers the target date]
	Use -->|CorrespondenceAddress| CalcC[Prefer COR; fallback to PHY]
	Use -->|PaymentAddress| CalcP[Prefer PAY; fallback to PHY;<br>fallback to COR]
	Use -->|PhysicalAddress| CalcPh[Prefer PHY; fallback to COR]
	CalcC --> Found{Match found?}
	CalcP --> Found
	CalcPh --> Found
	Found -->|No| Empty([Empty clsProviderAddress returned])
	Found -->|Yes| Return([Matching address returned])
	Use -->|GetAddress child/parent| Pair[Resolve primary address<br>for each location independently]
	Pair --> ParentSet{Parent address empty?}
	ParentSet -->|No| UseParent[Clone parent address;<br>overwrite name and location ID with child's]
	ParentSet -->|Yes| UseChild[Return child's own resolved address]
```

## Failure and edge-case behavior

- A location with zero address rows still returns a one-row result (via the left outer join), so callers cannot distinguish "no addresses" from "one placeholder row" without inspecting the address fields for null.*
- `CalculatePrimaryAddress` never returns `Nothing`; a location with no address of any matching type receives a new, empty `clsProviderAddress`, so downstream code must check for emptiness rather than a null reference.
- The billing-address resolution in `GetAddress` always prefers a non-empty parent address over the child's own, even when the child has its own valid address of the requested type — this is by design for locations that bill through a parent.
- `IsActive` on `clsProviderAddress` is hard-coded to always return `True` ("For now, only worry about Active Addresses"), so it does not reflect the address's actual effective/termination window; callers rely on `ActiveAddresses(date)` filtering instead.*

## Related documentation

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

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

## Sources

- `Business\SAIF.Business.View.Provider\clsProviderAddress.vb`
- `Business\SAIF.Business.View.Provider\clsProviderAddressCollection.vb`
