---
title: Contact info helper factories and code-map lookups
summary: Convenience factory functions that build and attach phone/email info items from raw values, and the cached code-map lookups they and other operations depend on for type/description resolution.
status: draft
owner: SAIF
last_reviewed: 2026-07-30
library: SAIF.Business.CustomerContact
reference_type: package
operation_id: OP-CC-11
trigger: Caller invokes mdlCustomerContact.CreatePhone/CreateEmail, or any operation resolves a code/type description via mdlCodeMaps
---

# Contact info helper factories and code-map lookups

`mdlCustomerContact.CreatePhone` and `CreateEmail` are convenience factories that build a `clsPhoneInfo`/`clsEmailAddressInfo` from raw caller-supplied values and attach it to a contact's collection, saving callers from constructing and configuring the info-item objects by hand. Both depend on this library's own `mdlCodeMaps` module to resolve phone/email type codes, with a `Static`-backed in-memory cache per code-map function; the info items themselves also call the separate, shared `mdlCodeMaps` module in `SAIF.Business` to resolve human-readable code descriptions for display. This page documents the factories and both code-map caching layers they rely on.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-CC-11 |
| **Trigger** | `mdlCustomerContact.CreatePhone(dpi, contact, phoneNumber, isForeignPhone, isPrimary, phoneType)` or `mdlCustomerContact.CreateEmail(dpi, contact, emailAddress, isPrimary, emailType)` |
| **Inputs** | Raw phone number / email address string, a type identifier (`phoneType`/`emailType`), `isPrimary`, and (phone only) `isForeignPhone` |
| **Outputs** | A constructed `clsPhoneInfo`/`clsEmailAddressInfo`, attached to the contact's `Phones`/`EmailAddresses` collection when valid |
| **Primary components** | `mdlCustomerContact.CreatePhone`, `mdlCustomerContact.CreateEmail`, `mdlCodeMaps.GetPhoneTypes`, `mdlCodeMaps.GetEmailTypes` |
| **Entry point** | `Business\SAIF.Business.CustomerContact\mdlCustomerContact.vb` |

## Steps

1. **`CreatePhone` builds a phone info item.**
   - Calls `clsPhoneInfo.NewPhone(dpi)` to construct a new instance.
   - Sets `Value = phoneNumber`, `IsPrimary = isPrimary`, `IsForeign = isForeignPhone`.
   - Resolves `PhoneCode` from this library's own `mdlCodeMaps.GetPhoneTypes(dpi)(phoneType).Code`, which is cached in a `Static` local per the caching behavior described in step 3.
   - Adds the phone to `contact.Phones` **only when `phone.IsValid`** — this is the gate described in [Phone info CRUD and linking](phone-info-crud-and-linking.md).
2. **`CreateEmail` builds an email info item.**
   - Calls `clsEmailAddressInfo.NewEmailAddress(dpi)`.
   - Sets `Value = emailAddress`, `IsPrimary = isPrimary`.
   - Resolves `EmailTypeCode` from this library's own `mdlCodeMaps.GetEmailTypes(dpi)(emailType).Code`.
   - Adds the email to `contact.EmailAddresses` **unconditionally**, with no `IsValid` gate — in contrast to `CreatePhone`, an email that fails validation is still attached to the collection and will surface its broken rule at save time instead of at construction time (see [Email info CRUD and linking](email-info-crud-and-linking.md)).*
3. **Code-map resolution and caching.** This library's `mdlCodeMaps.GetPhoneTypes`/`GetEmailTypes` each hold a `Static` local map (`m_Map`) and a `Static` local culture marker (`m_culture`), populated on first call and reused on subsequent calls; the map is rebuilt whenever the requested culture differs from `m_culture` or `inRefresh` is passed as `True`. The separate, shared `mdlCodeMaps` module in `SAIF.Business` (used for `GetPhoneCode`/`GetEmailCode`/`GetAddressCode`/`GetCustomerCode`/`GetRelationshipCode` description lookups elsewhere in this library) is assumed to follow a similar per-culture caching pattern, but its internal implementation lives outside this library and was not verified as part of this analysis.*

## Process flow

The following diagram visualizes the two factories' diverging validity handling.

```mermaid
flowchart TD
	StartPhone([CreatePhone called]) --> BuildPhone[Construct clsPhoneInfo;<br>resolve PhoneCode via mdlCodeMaps]
	BuildPhone --> PhoneValid{phone.IsValid?}
	PhoneValid -->|Yes| AttachPhone([Add to contact.Phones])
	PhoneValid -->|No| DiscardPhone([Object returned, not attached])
	StartEmail([CreateEmail called]) --> BuildEmail[Construct clsEmailAddressInfo;<br>resolve EmailTypeCode via mdlCodeMaps]
	BuildEmail --> AttachEmail([Always added to contact.EmailAddresses])
	CodeMapCall([Any code-map lookup]) --> CacheCheck{Static cache populated<br>for current culture?}
	CacheCheck -->|Yes| ReturnCached([Return cached collection])
	CacheCheck -->|No| LoadCache[Query code table; populate cache]
	LoadCache --> ReturnCached
```

## Failure and edge-case behavior
- `CreateEmail` has no validity gate, so a caller using this helper with a malformed email address gets back an attached-but-invalid item whose broken rule only surfaces when the parent is saved — unlike `CreatePhone`, which silently drops an invalid phone before it is ever attached.*
- Code-map caching means a code table change made after the cache is warmed (for a given culture, within the running process) is not reflected until a locale change or process restart triggers a reload.*

> **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\mdlCodeMaps.vb`
