Customer-contact relationship CRUD¶
clsCustomerContact is the row object for COMPANY_PERSON, the many-to-many relationship between a customer and a contact. It always saves its associated clsContact first, then persists its own relationship fields. Related parties can be retrieved from either direction: a customer's contacts, or a contact's customer relationships.
| Field | Value |
|---|---|
| Operation ID | OP-CC-04 |
| Trigger | clsCustomerContactCollection.NewCustomerContact() / AddContact(inContact) followed by Save(), or a read of clsCustomer.Contacts / clsContact.CustomerRelationships |
| Inputs | RelationshipCode, Title, the associated contact object (new or existing), the owning customer ID and contact ID |
| Outputs | Persisted COMPANY_PERSON row; collection of related contacts for a customer, or related companies for a contact |
| Primary components | clsCustomerContact.OnSave, clsCustomerContact.AddBusinessRules, clsCustomerContactCollection.GetCustomerContacts, clsContact.GetRelationships |
| Entry point | Business\SAIF.Business.CustomerContact\Customer\clsCustomerContact.vb |
Steps¶
- Create the relationship wrapper.
clsCustomerContactCollection.NewCustomerContact()creates a newclsContact(clsContact.NewContact(...)) and wraps it in a newclsCustomerContact;AddContact(inContact)wraps an existing contact instead. - Validate the relationship code (BR-004).
AddBusinessRulesregistersCommonBusinessRules.TrimTextRequiredagainstRelationshipCode. - Save pre-processing.
clsCustomerContact.OnSave()always callsSaveChildObject(Contact)first, so the underlyingPERSONrow is inserted or updated before theCOMPANY_PERSONrow is written — this matters for a brand-new contact, whosePERSON_SEQ_NUMmust exist before it can be referenced by the relationship insert (see Contact CRUD with child persistence). - Insert the relationship row.
BeforeInsertbindsCOMPANY_SEQ_NUM,PERSON_SEQ_NUM,RELATIONSHIP_CODE, andTITLE(SQL-010, COMPANY_PERSON). - Update the relationship row.
BeforeUpdatesetsRELATIONSHIP_CODEandTITLE(SQL-069, COMPANY_PERSON). - Uncertain / likely defect: the update statement's
WHEREclause matches onCOMPANY_SEQ_NUM,PERSON_SEQ_NUM, andRELATIONSHIP_CODE = :RelationshipCodeusing the same (new) parameter value used in theSETclause — if the relationship code is being changed, theWHEREclause will not match the existing row (which still has the old code), so the update may affect zero rows.* - Delete the relationship row. The delete criteria match on customer ID, contact ID, and relationship code (SQL-084, COMPANY_PERSON).
- Retrieve related contacts for a customer.
clsCustomer.ContactsloadsclsCustomerContactCollection.GetCustomerContacts(...), which joinsCOMPANY_PERSONtoPERSONfor the customer's ID (SQL-038, COMPANY_PERSON, PERSON); each row is loaded into aclsCustomerContactwith an embeddedclsContact.Load(...). - Retrieve related companies for a contact.
clsContact.CustomerRelationshipscallsGetRelationships(), which joinsCOMPANY_PERSONtoCOMPANYfor the contact's ID (SQL-028, COMPANY_PERSON, COMPANY) and returns a read-onlyclsCustomerRelationshiplist (used by Contact CRUD with child persistence to enforce the no-relationships-on-delete rule). - Resolve the relationship description.
RelationshipcallsGetRelationshipCode(dpi)(RelationshipCode).Descriptionfrom the sharedSAIF.Businesscode-map module for display purposes only.
Process flow¶
The following diagram visualizes relationship persistence and the two retrieval directions.
flowchart TD
Start([Caller adds/edits clsCustomerContact]) --> Validate{RelationshipCode populated?}
Validate -->|No| Blocked([Broken rule: relationship code required])
Validate -->|Yes| SaveContact[SaveChildObject: save PERSON row first]
SaveContact --> SaveLink[Insert or update COMPANY_PERSON row]
SaveLink --> Done([Relationship persisted])
RetrieveA([clsCustomer.Contacts]) --> QueryA[Join COMPANY_PERSON to PERSON<br>by CustomerID]
QueryA --> ResultA([Collection of related contacts])
RetrieveB([clsContact.CustomerRelationships]) --> QueryB[Join COMPANY_PERSON to COMPANY<br>by ContactID]
QueryB --> ResultB([Collection of related companies])
Failure and edge-case behavior¶
- Blank or whitespace-only
RelationshipCodeblocks the save with a broken rule before any SQL executes (BR-004). - Because
OnSavealways saves the contact child first, a failed contact save (for example, a contention error onPERSON) can leave the relationship row unsaved even though the contact's own save reported the specific error; callers must inspect both objects' broken-rule collections.* - The relationship-code update binding described in step 5 is a suspected defect: changing
RelationshipCodeon an existing relationship may silently fail to match any row because theWHEREclause still filters on the new code value instead of the original.* clsContact.CustomerRelationshipsis read-only from the contact side; there is no delete/insert entry point for the relationship exposed throughclsContactitself — relationship maintenance goes through the customer'sContactscollection.*
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¶
Sources¶
Business\SAIF.Business.CustomerContact\Customer\clsCustomerContact.vbBusiness\SAIF.Business.CustomerContact\Contact\clsContact.vb