---
title: Address info CRUD and linking
summary: Persists standalone ADDRESS rows and maintains the customer/contact intersection rows (COMPANY_ADDRESS, PERSON_ADDRESS) that link them.
status: draft
owner: SAIF
last_reviewed: 2026-07-30
library: SAIF.Business.CustomerContact
reference_type: package
operation_id: OP-CC-05
trigger: Caller adds/edits a clsAddressInfo in a customer's or contact's Addresses collection and the parent saves
---

# Address info CRUD and linking

`clsAddressInfo` is a standalone `ADDRESS` row that either a customer or a contact can reference through an intersection table (`COMPANY_ADDRESS` or `PERSON_ADDRESS`). The owning `clsCustomerAddressCollection` / `clsContactAddressCollection` tracks which items were added or removed since retrieval and, on save, persists the address rows themselves before reconciling the link rows.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-CC-05 |
| **Trigger** | Caller adds/removes a `clsAddressInfo` in `clsCustomer.Addresses` or `clsContact.Addresses`, then the parent's `Save()` cascades into the collection |
| **Inputs** | `AddressID`, `AddressCode` (address type), address lines, city/state/zip, country, foreign-address flag |
| **Outputs** | Persisted `ADDRESS` row; persisted or removed `COMPANY_ADDRESS` / `PERSON_ADDRESS` link row |
| **Primary components** | `clsAddressInfo.Create`, `clsAddressInfo.AddBusinessRules`, `clsCustomerAddressCollection.GetAddresses`, `clsContactAddressCollection.GetAddresses`, `clsInfoCollection.OnInsert`/`OnRemove`/`Save` |
| **Entry point** | `Business\SAIF.Business.CustomerContact\InfoItems\clsAddressInfo.vb` |

## Steps

1. **Create a new address row.** `clsAddressInfo.Create()` assigns `AddressID` from `GetNextSeqNum("Address_SEQ")`, sets `CreateDate`/`CreateUser`, and marks the object new.
2. **Validate** ([BR-005](../business-rules.md#br-005-address-records-require-an-id-and-address-type)). `AddBusinessRules` registers `CommonBusinessRules.NonZeroDecimalRequired` for `AddressID` and `CommonBusinessRules.TrimTextRequired` for `AddressCode`.
3. **Retrieve linked addresses.**
   - Customer side: `clsCustomerAddressCollection.GetAddresses(dpi, customerID)` joins `COMPANY_ADDRESS` to `ADDRESS`, plus correlated subqueries against `COMPANY_ADDRESS_PHONE` to populate `ConnectedPhoneID`/`ConnectedFaxID` on each row ([SQL-036](../sql/SQL-036.md), [COMPANY_ADDRESS](../database-tables.md#company-address), [COMPANY_ADDRESS_PHONE](../database-tables.md#company-address-phone), [ADDRESS](../database-tables.md#address)).
   - Contact side: `clsContactAddressCollection.GetAddresses(dpi, contactID)` performs the equivalent join against `PERSON_ADDRESS` and `PERSON_ADDRESS_PHONE` ([SQL-030](../sql/SQL-030.md), [PERSON_ADDRESS](../database-tables.md#person-address), [PERSON_ADDRESS_PHONE](../database-tables.md#person-address-phone), [ADDRESS](../database-tables.md#address)).
   - `Retrieving = True` is set during this load so the collection's add-tracking (step 4) does not treat freshly loaded rows as newly added items.
4. **Track adds and removes.** `clsInfoCollection.OnInsert` adds a newly inserted item to `AddedItems` unless it is being removed from `RemovedItems` (a net no-op); `OnRemove` adds a removed item to `RemovedItems` unless it was only in `AddedItems` (in which case it is simply discarded, since it was never persisted).
5. **Persist the root `ADDRESS` row.** Each current `clsAddressInfo` saves itself through the standard insert/update/delete SQL ([SQL-001](../sql/SQL-001.md)/[SQL-002](../sql/SQL-002.md) insert, [SQL-064](../sql/SQL-064.md)/[SQL-065](../sql/SQL-065.md) update, [SQL-075](../sql/SQL-075.md)/[SQL-076](../sql/SQL-076.md) delete, [ADDRESS](../database-tables.md#address)).
6. **Reconcile link rows.** `clsInfoCollection.Save()` then, in order: deletes link rows for every item in `RemovedItems` (customer: [SQL-083](../sql/SQL-083.md) against [COMPANY_ADDRESS](../database-tables.md#company-address); contact: [SQL-078](../sql/SQL-078.md) against [PERSON_ADDRESS](../database-tables.md#person-address)), then inserts link rows for every item in `AddedItems` (customer: [SQL-009](../sql/SQL-009.md); contact: [SQL-004](../sql/SQL-004.md)); both lists are cleared afterward.

## Process flow

The following diagram visualizes the address collection's retrieve/track/save cycle.

```mermaid
flowchart TD
	Start([Parent retrieves address collection]) --> Retrieving[Retrieving = True;<br>load ADDRESS joined to link table]
	Retrieving --> Loaded[Populate ConnectedPhoneID/ConnectedFaxID;<br>Retrieving = False]
	Loaded --> Edit[Caller adds/removes clsAddressInfo items]
	Edit --> Track[OnInsert -> AddedItems<br>OnRemove -> RemovedItems]
	Track --> ParentSave([Parent Save cascades to collection])
	ParentSave --> SaveRows[Save each current ADDRESS row<br>insert/update per item]
	SaveRows --> DeleteLinks[Delete link rows for RemovedItems]
	DeleteLinks --> InsertLinks[Insert link rows for AddedItems]
	InsertLinks --> Done([Collection persisted; lists cleared])
```

## Failure and edge-case behavior
- Missing or zero `AddressID`, or a blank `AddressCode`, blocks the address save with a broken rule before the `ADDRESS` insert/update executes ([BR-005](../business-rules.md#br-005-address-records-require-an-id-and-address-type)).
- Removing an address from a customer's or contact's collection deletes only the `COMPANY_ADDRESS` / `PERSON_ADDRESS` link row; it does not call `Delete()` on the standalone `ADDRESS` row itself, so the row can remain orphaned if no other link references it.*
- No write path for `COMPANY_ADDRESS_PHONE` / `PERSON_ADDRESS_PHONE` was found in the examined source; those phone-connection associations appear to be read-only from this library's perspective.**

> **Note:** Statements marked with `*` were inferred from code structure, naming
> conventions, or contextual evidence where direct confirmation was not available
> in source artifacts. Statements marked with `**` could not be confirmed; treat
> as unknown until verified.

## 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\InfoItems\clsAddressInfo.vb`
- `Business\SAIF.Business.CustomerContact\InfoItems\clsInfoCollection.vb`
- `Business\SAIF.Business.CustomerContact\Customer\clsCustomerAddressCollection.vb`
- `Business\SAIF.Business.CustomerContact\Contact\clsContactAddressCollection.vb`
