---
title: Maintain provider contact
summary: Persists provider contact records requiring a contact type and at least one communication path.
status: draft
owner: SAIF
last_reviewed: 2026-07-30
library: SAIF.Business.Provider
reference_type: package
operation_id: OP-PRV-05
trigger: Caller creates or retrieves a clsProviderContact, edits properties, and calls Save() or Delete()
---

# Maintain provider contact

`clsProviderContact` maintains a named contact (phone/email) attached to a provider location. Unlike the identity, tax ID, MCO membership, and relationship classes, its own `Save()` path does **not** call the accompanying `clsContactApi` helper in this codebase — the API wrapper exists as a separate, caller-driven sync utility.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-PRV-05 |
| **Trigger** | Caller calls `NewProviderContact`/`GetProviderContact`, edits properties, and calls `Save()` or `Delete()` |
| **Inputs** | `ProviderLocationSeqNum`, `ProviderContactCode`, `Name`, `EmailAddress`, `PhoneNumber` |
| **Outputs** | Persisted [PROVIDER_CONTACT](../database-tables.md#provider_contact) row |
| **Primary components** | `clsProviderContact.Create`, `BeforeInsert/BeforeUpdate/BeforeDelete`, `ValidateContactType`, `ValidateEmailPhone`, `ValidatePhone` |
| **Entry point** | `Business\SAIF.Business.Provider\clsProviderContact.vb` |

## Steps

1. **Create.** `NewProviderContact(inDataportal, inProviderLocationSeqNum)` constructs with `RetrieveType.Location`; `Create()` assigns `ProviderLocationSeqNum` and a new seq num via `GetNextSeqNum("PROVIDER_CONTACT_SEQ")`.
2. **Retrieve single.** `BeforeRetrieve`/`AfterRetrieve` read [PROVIDER_CONTACT](../database-tables.md#provider_contact) by `PROVIDER_CONTACT_SEQ_NUM` ([SQL-045](../sql/SQL-045-retrieve-provider-contact.md)); `Fill(...)` runs only when a row is returned, otherwise the object is still marked old with empty fields.*
3. **Retrieve collection.** `GetContacts(inDPI, inProviderLocationSeqNum)` runs a direct `SqlHelper.ResultSetQuery` filtered by `PROVIDER_LOCATION_SEQ_NUM` to load every contact for a location.
4. **Validate before save.**
   - [BR-009](../business-rules.md#br-009-contacts-require-a-type-and-a-communication-path): `ValidateContactType` requires a nonblank `ProviderContactCode` (`"Error: Select a contact type."`); `ValidateEmailPhone` requires at least one of `EmailAddress` or `PhoneNumber` (`"Error: Either a phone # or an email address must be entered."`).
   - `CommonBusinessRules.IsValidEmailAddress` validates `EmailAddress` format when present.
   - `ValidatePhone`: hard error if `PhoneNumber` is present and its trimmed formatted length is less than 12 characters (`"Error: Invalid phone number."`).
   - A contact-name-required rule exists in source as a commented-out call and is **not** currently enforced.*
5. **Insert.** `BeforeInsert` writes `PROVIDER_CONTACT_SEQ_NUM`, `PROVIDER_LOCATION_SEQ_NUM`, `PROVIDER_CONTACT_CODE`, `NAME`, `EMAIL_ADDR`, `PHONE_NUM` (formatted via the `PhoneNumber` getter), and audit fields to [PROVIDER_CONTACT](../database-tables.md#provider_contact) ([SQL-021](../sql/SQL-021-insert-provider-contact.md)).
   - On a rows-affected value other than `1`: add `"Insert Failed"`; otherwise `MarkOld()`.
6. **Update.** `BeforeUpdate` writes the same fields to [PROVIDER_CONTACT](../database-tables.md#provider_contact) ([SQL-069](../sql/SQL-069-update-provider-contact.md)) with concurrency check `WHERE PROVIDER_CONTACT_SEQ_NUM = :ProviderContactSeqNum AND UPDATE_NUM = :UpdateNum`.
   - On a rows-affected value other than `1`: add the DB contention error; otherwise increment `m_UpdateNum` and `MarkOld()`.
7. **Delete.** `BeforeDelete` deletes the [PROVIDER_CONTACT](../database-tables.md#provider_contact) row by `PROVIDER_CONTACT_SEQ_NUM` ([SQL-091](../sql/SQL-091-delete-provider-contact.md)).
   - On a rows-affected value other than `1`: add the DB contention error; otherwise `MarkNew()`.
8. **Separate API sync utility.** `clsContactApi.AddContact/UpdateContact/RemoveContact` is a standalone wrapper that sends a `ContactDto` to Provider API routes `provider/{0}` (insert/update) and `provider/{0}/remove` (delete) via `DataApiAccessLayer.InsertObject/UpdateObject/DeleteObject`. Because `clsProviderContact.Save()`/`Delete()` never invoke this class, Contact Manager synchronization for contact records — if it happens at all — is driven by a caller outside this file rather than embedded in the persistence pipeline.*

## Process flow

```mermaid
flowchart TD
	Start([Caller edits contact]) --> TypeCheck{Contact type entered?}
	TypeCheck -->|No| Fail([Missing type or<br>communication path blocks save])
	TypeCheck -->|Yes| CommCheck{Phone or email present?}
	CommCheck -->|Neither| Fail
	CommCheck -->|At least one| PhoneCheck{Phone present and valid length?}
	PhoneCheck -->|No, too short| Fail
	PhoneCheck -->|Yes or blank| Persist[Insert/update/delete PROVIDER_CONTACT]
	Persist --> Rows{RowsAffected = 1?}
	Rows -->|No| Contention([Insert Failed / DB contention error])
	Rows -->|Yes| Done([Save complete;<br>no automatic Contact Manager sync])
```

## Failure and edge-case behavior

- `Save()`/`Delete()` do not call `clsContactApi`, so provider contacts are the one maintenance class in this library whose persistence path does not automatically synchronize to Contact Manager.*
- Contact name is not a required field despite a commented-out rule suggesting it once was.*
- Retrieving a nonexistent contact seq num returns an unpopulated object rather than an explicit not-found error.*

## 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.Provider\clsProviderContact.vb`
- `Business\SAIF.Business.Provider\Api\clsContactApi.vb`
