# SaveIntake - Advanced Documentation ## Overview Creates or updates an intake claim with contact, claim group, staff, ad hoc fields, notes, and decision data. Enforces decision-dependent validations (e.g. decision maker required for accept/reject/refer), field-level permissions, and timestamp concurrency. Generates claim name, creates expected event tasks, sends notifications to person in charge, and closes associated leads. ## Business Rules - The claimant contact is saved first (via `Contact.Save`), and the result is attached to the claim before further processing. - The claim group is saved with elevated `EditClaimGroups` permissions so that intake users without explicit group-edit access can still create/update groups during intake. - New claims default `BillingBasis` to `"Contingency"`, set `CreatedBy` to the current person, and stamp `CreatedDate` with `DateTime.UtcNow`. - The claim's `IncidentDate` is always synced from the claim group's `IncidentDate`. - Claim name is auto-generated via `GenerateClaimName` based on entity field patterns, then re-saved after the record ID is assigned (for new claims). - The claim status is computed by `GetClaimStatus`, which derives the status from the Decision field and other state. - Decision-dependent validation: - **Stop Chase**: requires DecisionMaker and StopChaseReason. - **Accept**: requires DecisionMaker; if ContractDate is set, at least one staff member is required. - **Reject**: requires DecisionMaker and RejectionReason. - **Refer**: requires DecisionMaker, ReferralStatus, and ReferralContact. - If the claim has a ClosedDate, a ClosedNote is required. - Sticky note changes auto-stamp `DateStickyNoteSaved` and `StickyNoteAddedBy`. - Estimated value changes auto-stamp `DateEstimatedValueSaved` and `EstimatedValueAddedBy`. - Staff assignments are synced via `SyncSubType` on the `ClaimStaffs` sub-entity, keyed by `CompanyStaffRoleID`. - Tags are synced via `SyncSubType` on `ClaimTags`. - Ad hoc fields are synced using `AdHocField.SyncSubTypeToAdHocFieldValues`, which enforces per-field staff permissions. - Field-level permissions are enforced by `RevertSystemFieldsWithPermissions`, which reverts unauthorized changes made by the current user. - `SetLastReviewDates` updates review date tracking fields on the claim entity. - If the `SignUpAppointment` field changes and the decision is "Accept" with a PersonInCharge, a signup calendar event is auto-created and an "in charge" notification email is sent. - If a Lead is associated and the claim is new, the lead is closed via `Lead.CloseLead`. - Claim notes are handled individually: new notes call `CreateClaimNote`, existing notes call `UpdateClaimNote`. Deleted notes are removed from the sub-entity and their AWS images are cleaned up. - For existing claims, linked calendar events for removed ad hoc fields are disconnected before save to avoid foreign key violations. - After commit, if `SaveLinkedEvents` is true, linked calendar events are updated via `UpdateLinkedCalendarEvent`. - A correspondence email address is generated from the claim name and returned on the result. ## Permissions & Security - Requires `ManageIntake` permission (validated via `ValidateCurrentPersonAndCompany`). - All staff person IDs referenced in the claim (CreatedBy, DecisionMaker, PersonInCharge, staff assignments, ad hoc field staff, note authors, claim contact ad hoc staff) are validated as current or former members of the firm via `ValidatePersonWasPartOfFirm`. - For existing claims, `HasSecurityAccessToClaim` is checked during validation to enforce private claim access. - The claim type must belong to the current firm. - Contract date and claim type changes are blocked for claims shared with (but not owned by) the current firm. - FirmID changes on existing claims throw a critical error directing the user to contact support. - Field-level system permissions revert unauthorized field changes silently (the save proceeds with original values). - The claim group save is wrapped in `ElevatePermissions` for `EditClaimGroups` so the intake user does not need that permission directly. ## Data Flow 1. `ValidateCurrentPersonAndCompany` confirms authentication and `ManageIntake` permission. 2. All referenced person IDs are collected and validated against the firm. 3. A database transaction is started. 4. The claimant contact is saved via `Contact.Save`. 5. The claim group is saved (with permission elevation) via `SaveClaimGroup`. 6. For new claims, FirmID, CreatedBy, CreatedDate, and BillingBasis are initialized. 7. `Validate` checks required fields and decision-dependent rules. 8. Claim status is computed and the Aptify generic entity is loaded. 9. Timestamp concurrency is validated against the database record. 10. All scalar fields are written to the generic entity. 11. Staff, tags, and ad hoc field sub-entities are synced. 12. System field permissions are enforced (unauthorized changes reverted). 13. Notes sub-entity is synced: deleted notes removed, new/updated notes processed. 14. If the entity is dirty, expected event tasks are created and the timestamp is updated. 15. Linked events for removed ad hoc fields are disconnected. 16. The claim is saved via `SaveClaimGeAndEnsureName`, which persists the entity and ensures the name is set. 17. For new claims with tasks, task records are re-saved with the new claim ID. 18. The claim name is updated via `spClaimUpdateName` stored procedure (bypasses record history logging). 19. If signup appointment triggers, a calendar event is created and an email is sent. 20. If a lead is associated and the claim is new, the lead is closed. 21. The transaction is committed. 22. AWS images for deleted notes are cleaned up (post-commit, failures logged but not thrown). 23. Linked calendar events are updated if `SaveLinkedEvents` is true. 24. Correspondence email is generated and the claim is returned. ## Side Effects - **Database writes**: Claim record, claim group, claimant contact, claim staff sub-entities, claim tags, claim ad hoc fields, claim notes, expected event tasks, claim name (via stored procedure). - **Calendar events**: Signup appointment calendar event created when SignUpAppointment changes on an accepted claim with a PersonInCharge. - **Linked calendar events**: Updated post-commit if `SaveLinkedEvents` is true. - **Email**: "In charge of claim" notification sent to PersonInCharge when signup appointment is set. - **Lead closure**: Associated lead is closed for new claims. - **AWS S3**: Note images uploaded for new notes; images deleted for removed notes (post-commit). - **Cache**: No explicit cache invalidation in this method (claim group dirty state does not trigger JSON upload in SaveIntake, unlike SaveClaim). ## Error Conditions - `RadoloException("You must be logged in...")` if not authenticated (checked at API layer). - `RadoloException("You must have a company selected...")` if no company context (checked at API layer). - `RadoloException` from `ValidateCurrentPersonAndCompany` if user lacks `ManageIntake` permission. - `RadoloException` from `ValidatePersonWasPartOfFirm` if any referenced staff is not a member of the firm. - `RadoloException` from `Validate` for missing required fields or decision-dependent validation failures. - `RadoloException` from `ValidateTimestamp` if the claim was modified by another user since it was loaded (optimistic concurrency). - `RadoloException("You cannot change the contract date...")` if a non-owning firm tries to modify ContractDate. - `RadoloException("You cannot change the [claim] type...")` if a non-owning firm tries to modify ClaimTypeID. - `RadoloException("The system is attempting to change the CompanyID...")` if FirmID is being changed on an existing claim. - `RadoloException("This ClaimNoteID[...] could not be found...")` if an existing note ID does not match the claim. - If note creation/update fails, all newly created note images are rolled back from AWS before rethrowing. - Entity save failures throw `RadoloException` with the entity's `LastError`. ## Ad Hoc Fields Ad hoc fields are custom fields defined per claim type. They are passed as a list of `AdHocFieldInfo` objects on the claim. ### Setting Values **Critical: `FieldValue` is read-only.** It is a computed getter that returns the appropriate typed value as a string. Setting `FieldValue` does nothing. You must set the specific typed property that matches the field's `DataType`: | DataType | Property to Set | Notes | |----------|----------------|-------| | Number | `NumberValue` | Integer | | Decimal | `DecimalValue` | Decimal number | | TextShort | `TextShortValue` | Short text (single line) | | TextLong | `TextLongValue` | Long text (multi-line) | | Date | `DateValue` | Date only | | DateTime | `DateTimeValue` | Date and time | | Yes/No | `YesNoValue` | Boolean | | Dropdown | `DropdownValue` | Selected option text | | Document | `Document` | Document reference | | Contact | `BaboContact` | Contact reference | | Staff | `Staff` | Staff person reference | | Hyperlink | `TextShortValue` | URL stored as short text | | State | `DropdownValue` | US state stored as dropdown | ### Required Fields Each `AdHocFieldInfo` must include: - `AdHocFieldDefinitionID` - identifies which custom field this is - The appropriate typed value property for the field's DataType ### Permission Enforcement Ad hoc fields are synced via `SyncSubTypeToAdHocFieldValues`, which enforces role-based permissions: - **Edit permissions**: If the current user's roles do not include the field's `EditRoles`, the field value is preserved unchanged (the caller's value is ignored silently). - **View permissions**: Fields the user cannot view are filtered from the response. - **API users**: Bypass all edit/view restrictions - API callers can read and write all ad hoc fields regardless of role configuration. ### Calendar Event Linking Ad hoc fields of type Date or DateTime can be linked to calendar events. When `SaveLinkedEvents` is true (always true from SecureApi), linked calendar events are updated after the save. If an ad hoc field is removed, its linked calendar event is disconnected before save to avoid foreign key violations. ## Usage Notes - The SecureApi layer delegates directly to `Claim.SaveIntake` without additional auth checks beyond the controller's own `ValidateCurrentPersonAndCompany`. - This method wraps everything in a single long-lived transaction, which the codebase acknowledges as a scalability concern (see inline TODO comment from 2019). - The contact save, claim group save, and claim save all happen within the same transaction scope. - The claim name is saved twice: once before the entity save (to populate the field) and once after (via stored procedure, to include the generated record ID). - Unlike `SaveClaim`, this method does not handle claim privacy (IsPrivate, ClaimAllowedStaff, ClaimAllowedFirmRoles) or claim contacts -- those are managed separately. - The `SaveLinkedEvents` parameter is always `true` when called from the SecureApi. - Phone/email verification on the claimant contact happens as part of `Contact.Save` within the transaction.