Skip to content

Long-note persistence

clsCustomerNote and clsContactNote are near-identical wrappers that store a customer's or contact's long-form note in COMPANY_NOTE or PERSON_NOTE, keyed by the owner's ID rather than embedded in the root COMPANY/PERSON row. Both classes are merged into this single operation page because they share the same retrieve/fill/save/delete pattern and differ only in their owning table and key column.

Field Value
Operation ID OP-CC-08
Trigger Caller sets clsCustomer.LongNoteText or clsContact.LongNoteText, or reads it after a retrieve; persistence happens when the parent's Save() cascades to the note child in OnSaveComplete
Inputs NoteText (up to 4000 characters), owning CustomerID or ContactID
Outputs Persisted or deleted COMPANY_NOTE / PERSON_NOTE row
Primary components clsCustomerNote.GetNote, clsCustomerNote.AfterRetrieve, clsContactNote.GetNote, clsContactNote.AfterRetrieve, clsCustomer.OnSave, clsContact.OnSave
Entry point Business\SAIF.Business.CustomerContact\Customer\clsCustomerNote.vb, Business\SAIF.Business.CustomerContact\Contact\clsContactNote.vb

Steps

  1. Expose the note through the parent. clsCustomer.LongNoteText proxies CustomerNote.NoteText; clsContact.LongNoteText proxies ContactNote.NoteText. The CustomerNote/ContactNote property lazily calls clsCustomerNote.GetNote(dpi, customerID) / clsContactNote.GetNote(dpi, contactID) on first access.
  2. Retrieve the note row. GetNote runs the BOF retrieve pipeline keyed by the owner ID (customer: SQL-040 against COMPANY_NOTE; contact: SQL-033 against PERSON_NOTE).
  3. Synthesize a note object when none exists. AfterRetrieve fills every returned row and calls MarkOld(); if the resulting NoteText is blank (no row was found, or the row's text is empty), it calls Create() and MarkClean() so callers always receive a usable, non-dirty note object even when no COMPANY_NOTE/PERSON_NOTE row exists.
  4. Delete-on-blank at parent save time (BR-009). In the parent's OnSave(), if the note Is Not IsNew and NoteText.Trim.Length = 0, the parent calls CustomerNote.Delete() / ContactNote.Delete() before the save cascade runs, so an existing note that has been cleared out is removed rather than persisted as an empty string.
  5. Persist the note in the parent's save cascade. OnSaveComplete calls SaveChildObject(CustomerNote) / SaveChildObject(ContactNote) alongside the other child collections, which runs whichever of insert/update/delete applies:
  6. Insert: NOTE_TEXT and the owner key (customer: SQL-012; contact: SQL-006).
  7. Update: NOTE_TEXT by owner key (customer: SQL-070; contact: SQL-067).
  8. Delete: by owner key (customer: SQL-086; contact: SQL-080).
  9. On failure (rows affected ≠ 1) for any of insert/update/delete: the corresponding InsertError/UpdateError/DeleteError broken rule is added and the note object is not marked clean/new.

Process flow

The following diagram visualizes the shared retrieve/blank-delete/save cycle used by both note types.

flowchart TD
    Start([Parent retrieves CustomerNote/ContactNote]) --> Query[Query COMPANY_NOTE/PERSON_NOTE<br>by owner key]
    Query --> Found{Row found with<br>non-blank NoteText?}
    Found -->|Yes| MarkOld[Fill row; MarkOld]
    Found -->|No| Synthesize[Create blank note object; MarkClean]
    MarkOld --> Ready([Note object ready for LongNoteText access])
    Synthesize --> Ready
    Ready --> ParentSave([Parent Save runs])
    ParentSave --> BlankCheck{Note not new<br>and NoteText blank?}
    BlankCheck -->|Yes| DeleteNote[Delete note row]
    BlankCheck -->|No| SaveNote[Insert or update note row]
    DeleteNote --> Done([Save cascade continues])
    SaveNote --> Done

Failure and edge-case behavior

  • A blank note is deleted rather than persisted as empty text, so a customer/contact that never had a note (or whose note was cleared) always retrieves a fresh, clean note object rather than a stored blank row (BR-009).
  • The blank-delete check only applies when the note Is Not IsNew; a brand-new note that is created and left blank is simply never inserted, since OnSaveComplete's SaveChildObject call has nothing dirty to persist.*
  • A contention failure (rows affected ≠ 1) on note insert/update/delete surfaces as a broken rule on the note object, not on the parent customer/contact, so callers must check both objects' broken-rule collections after Save().*

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.CustomerContact\Customer\clsCustomerNote.vb
  • Business\SAIF.Business.CustomerContact\Contact\clsContactNote.vb
  • Business\SAIF.Business.CustomerContact\Customer\clsCustomer.vb
  • Business\SAIF.Business.CustomerContact\Contact\clsContact.vb