---
title: SAIF.Business.CustomerContact business rules
summary: Enumerated business rules with per-rule trigger, enforcement, and outcomes.
status: draft
owner: SAIF
last_reviewed: 2026-07-30
library: SAIF.Business.CustomerContact
reference_type: package
---

# SAIF.Business.CustomerContact business rules

This page captures the business rules enforced directly by `SAIF.Business.CustomerContact`.

## BR-001: Search requires at least one populated criterion

| Field | Value |
| --- | --- |
| Statement | At least one customer or contact search criterion must be populated before a combined search can execute. |
| Trigger | `SearchForCustomerContacts` is called. |
| Enforcement | `SearchRequest.IsCustomerSearchable`, `IsContactSearchable`, and `IsValidRequest` reject empty requests. |
| Outcome | The library returns `Invalid Search Request.` and does not execute SQL. |
| Confidence | Verified |
| Tables | [PERSON](database-tables.md#person), [COMPANY](database-tables.md#company), [COMPANY_PERSON](database-tables.md#company-person) |
| SQL | [SQL-031](sql/SQL-031.md), [SQL-037](sql/SQL-037.md) |
| Source | `Business\SAIF.Business.CustomerContact\clsCustomerContactSearch.vb` |

---

## BR-002: Search results are capped at 300

| Field | Value |
| --- | --- |
| Statement | Combined search results cannot exceed 300 rows and may be split between contact and customer quotas. |
| Trigger | A combined search executes or the split ratio changes. |
| Enforcement | `SearchRequest.ResultMax`, `ValidateSplitRatio`, and `CalcLimits` clamp the request and distribute result quotas. |
| Outcome | Search processing enforces a 300-row ceiling and applies the configured split ratio. |
| Confidence | Verified |
| Tables | [PERSON](database-tables.md#person), [COMPANY](database-tables.md#company), [COMPANY_PERSON](database-tables.md#company-person) |
| SQL | [SQL-031](sql/SQL-031.md), [SQL-037](sql/SQL-037.md) |
| Source | `Business\SAIF.Business.CustomerContact\clsCustomerContactSearch.vb` |

---

## BR-003: Contacts with customer relationships cannot be deleted

| Field | Value |
| --- | --- |
| Statement | A contact cannot be deleted while customer relationships still exist. |
| Trigger | A `clsContact` instance is deleted. |
| Enforcement | `ValidateOnDelete = True` and `ValidateNoRelationshipsOnDelete` check `CustomerRelationships.Count > 0` before delete. |
| Outcome | Delete validation fails until the contact is no longer linked to any customer. |
| Confidence | Verified |
| Tables | [PERSON](database-tables.md#person), [COMPANY_PERSON](database-tables.md#company-person), [COMPANY](database-tables.md#company) |
| SQL | [SQL-028](sql/SQL-028.md), [SQL-077](sql/SQL-077.md) |
| Source | `Business\SAIF.Business.CustomerContact\Contact\clsContact.vb` |

---

## BR-004: Customer-contact links require a relationship code

| Field | Value |
| --- | --- |
| Statement | A customer-contact relationship must include a non-blank relationship code. |
| Trigger | A `clsCustomerContact` record is inserted or updated. |
| Enforcement | `CommonBusinessRules.TrimTextRequired` is registered for `RelationshipCode`. |
| Outcome | Relationship saves fail until a valid relationship code is supplied. |
| Confidence | Verified |
| Tables | [COMPANY_PERSON](database-tables.md#company-person) |
| SQL | [SQL-010](sql/SQL-010.md), [SQL-069](sql/SQL-069.md) |
| Source | `Business\SAIF.Business.CustomerContact\Customer\clsCustomerContact.vb` |

---

## BR-005: Address records require an ID and address type

| Field | Value |
| --- | --- |
| Statement | Address records require a non-zero address identifier and an address type code. |
| Trigger | `clsAddressInfo` is validated before save. |
| Enforcement | `CommonBusinessRules.NonZeroDecimalRequired` and `TrimTextRequired` enforce `AddressID` and `AddressCode`. |
| Outcome | Invalid address objects surface explicit address-ID and address-type validation errors. |
| Confidence | Verified |
| Tables | [ADDRESS](database-tables.md#address) |
| SQL | [SQL-001](sql/SQL-001.md), [SQL-002](sql/SQL-002.md), [SQL-064](sql/SQL-064.md), [SQL-065](sql/SQL-065.md), [SQL-075](sql/SQL-075.md), [SQL-076](sql/SQL-076.md) |
| Source | `Business\SAIF.Business.CustomerContact\InfoItems\clsAddressInfo.vb` |

---

## BR-006: Email validation severity and depth are configurable

| Field | Value |
| --- | --- |
| Statement | Email validation can raise warnings or errors and can enforce syntax-only or syntax-plus-DNS checks. |
| Trigger | An email address is validated. |
| Enforcement | `EmailAddressValidation`, `EmailValidationType`, and the syntax/DNS rule methods determine how the value is checked. |
| Outcome | Email saves are accepted, warned, or rejected according to the configured validation mode. |
| Confidence | Verified |
| Tables | [EMAIL_ADDRESS](database-tables.md#email-address) |
| SQL | [SQL-014](sql/SQL-014.md), [SQL-015](sql/SQL-015.md), [SQL-071](sql/SQL-071.md), [SQL-072](sql/SQL-072.md), [SQL-088](sql/SQL-088.md), [SQL-089](sql/SQL-089.md) |
| Source | `Business\SAIF.Business.CustomerContact\InfoItems\clsEmailAddressInfo.vb` |

---

## BR-007: Phone records require an ID and formatted value

| Field | Value |
| --- | --- |
| Statement | Phone records require a non-zero phone identifier and a populated phone value. |
| Trigger | `clsPhoneInfo` is validated before save. |
| Enforcement | `CommonBusinessRules.NonZeroDecimalRequired` and `TrimTextRequired` enforce `PhoneID` and phone content. |
| Outcome | Missing IDs or empty phone values produce validation errors. |
| Confidence | Verified |
| Tables | [PHONE](database-tables.md#phone) |
| SQL | [SQL-016](sql/SQL-016.md), [SQL-017](sql/SQL-017.md), [SQL-073](sql/SQL-073.md), [SQL-074](sql/SQL-074.md), [SQL-090](sql/SQL-090.md), [SQL-091](sql/SQL-091.md) |
| Source | `Business\SAIF.Business.CustomerContact\InfoItems\clsPhoneInfo.vb` |

---

## BR-008: Generated agency, agent, and provider records can be forced inactive

| Field | Value |
| --- | --- |
| Statement | Agency, agent, and provider conversion flows mark generated customer or contact records inactive when source effective or expiration dates indicate a future or expired relationship. |
| Trigger | `CreateAgency`, `CreateAgent`, or `GetCustomerIDForProvider` creates a new local record. |
| Enforcement | Date checks against `Today.Date` set `IsActive = False` before save when the source record is not currently active. |
| Outcome | Generated records are persisted in an inactive state until the source date range becomes current or is corrected. |
| Confidence | Verified |
| Tables | [COMPANY](database-tables.md#company), [PERSON](database-tables.md#person), [CUSTOMER_AGENCY](database-tables.md#customer-agency), [CUSTOMER_AGENT](database-tables.md#customer-agent), [CUSTOMER_PROVIDER](database-tables.md#customer-provider) |
| SQL | [SQL-018](sql/SQL-018.md), [SQL-019](sql/SQL-019.md), [SQL-024](sql/SQL-024.md), [SQL-046](sql/SQL-046.md), [SQL-048](sql/SQL-048.md), [SQL-060](sql/SQL-060.md) |
| Source | `Business\SAIF.Business.CustomerContact\mdlCustomerContact.vb` |

## BR-009: Blank long notes are deleted rather than stored as empty text

| Field | Value |
| --- | --- |
| Statement | When a customer's or contact's long note text is blank at save time, the underlying note row is deleted instead of being persisted with empty text. |
| Trigger | `clsCustomer.Save()` or `clsContact.Save()` runs with an existing (non-new) note whose `NoteText` is blank. |
| Enforcement | `clsCustomer.OnSave()` and `clsContact.OnSave()` each check `Not <Note>.IsNew And <Note>.NoteText.Trim.Length = 0` and call `<Note>.Delete()` before the save cascade persists the note. |
| Outcome | The `COMPANY_NOTE` or `PERSON_NOTE` row is removed; a subsequent retrieve returns a fresh, clean note object with empty text rather than a stored blank row. |
| Confidence | Verified |
| Tables | [COMPANY_NOTE](database-tables.md#company-note), [PERSON_NOTE](database-tables.md#person-note) |
| SQL | [SQL-070](sql/SQL-070.md), [SQL-080](sql/SQL-080.md), [SQL-086](sql/SQL-086.md), [SQL-006](sql/SQL-006.md) |
| Source | `Business\SAIF.Business.CustomerContact\Customer\clsCustomer.vb`, `Business\SAIF.Business.CustomerContact\Contact\clsContact.vb` |

---

## BR-010: Changing an email address value clears its verified flag

| Field | Value |
| --- | --- |
| Statement | Setting a new value on an email address always resets `IsVerified` to `False`, even if the address had previously been verified. |
| Trigger | `clsEmailAddressInfo.Value` is assigned a new value. |
| Enforcement | `clsEmailAddressInfo.OnValueChange` calls the base value-change handler and then explicitly sets `IsVerified = False`. |
| Outcome | Any edit to an email address requires re-verification before the address can be treated as validated again. |
| Confidence | Verified |
| Tables | [EMAIL_ADDRESS](database-tables.md#email-address) |
| SQL | [SQL-071](sql/SQL-071.md), [SQL-072](sql/SQL-072.md) |
| Source | `Business\SAIF.Business.CustomerContact\InfoItems\clsEmailAddressInfo.vb` |

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Operations and processes](operations-processes.md)
- [Database tables](database-tables.md)
- [SQL catalog](sql/index.md)
- [API reference](api/index.md)
- [Configuration](configuration.md)

## Sources
- `Business\SAIF.Business.CustomerContact\Contact\clsContact.vb`
- `Business\SAIF.Business.CustomerContact\Contact\clsContactNote.vb`
- `Business\SAIF.Business.CustomerContact\Customer\clsCustomer.vb`
- `Business\SAIF.Business.CustomerContact\Customer\clsCustomerContact.vb`
- `Business\SAIF.Business.CustomerContact\Customer\clsCustomerNote.vb`
- `Business\SAIF.Business.CustomerContact\InfoItems\clsAddressInfo.vb`
- `Business\SAIF.Business.CustomerContact\InfoItems\clsEmailAddressInfo.vb`
- `Business\SAIF.Business.CustomerContact\InfoItems\clsPhoneInfo.vb`
- `Business\SAIF.Business.CustomerContact\clsCustomerContactSearch.vb`
- `Business\SAIF.Business.CustomerContact\mdlCustomerContact.vb`
