# SaveClaimLedgerEntry - Advanced Documentation ## Overview Creates or updates a ledger entry on a claim with validation of amounts, staff assignments, and billing rates. Removes check approvals if the total changes beyond the approval threshold. Prevents modification of entries on sent invoices, completed disbursals, or approved checks (except accounting identifiers). Enforces billing rate visibility restrictions for users without ViewBillingRates permission. ## Business Rules - Entry Date is required - Total minus Reduction minus Paid cannot be negative - A Contact is required - Entry Type Sign must be "+" (income) or "-" (expense) - Time-based entries require a staff member, non-negative hourly rate, and non-negative hours - Entries that have been used on a sent invoice (Invoiced > 0) cannot be edited (caught in validation) - Total, Paid, and Reduction each have a maximum of $999,999,999.99 - All staff IDs (primary and from ad hoc fields) must be verified as current or former members of the firm - The claim must belong to the current company - Existing entries on sent invoices or completed disbursals are blocked from modification (`ValidateLedgerEntryGEForUseOnInvoicesAndDisbursals`) - Entries with an issued check date cannot have field values changed (except AccountingExternalID for external accounting sync) - New entries cannot be created with CheckApproved=true - If the total changes on an approved check and the new total exceeds the approval threshold, the check approval is deleted and CheckApproved is reset to false - If the total due is at or below the approval threshold and a check is requested, the entry is auto-approved - For time-based entries, if the hourly rate differs from the staff member's default rate and the rate field is dirty, a RateChangeReason is required - Users without ViewBillingRates permission have billing fields enforced: new entries get default rates calculated from the staff/claim-type combination; existing entries have rate/reduction/total reverted to stored values - After save, if the user lacks ViewBillingRates, the returned Total, HourlyRate, and RateChangeReason are zeroed/cleared - Ad hoc fields on the ledger entry are synced via `AdHocField.SyncSubTypeToAdHocFieldValues` - Claim ledger entry payments (sub-payments) are synced via SubType sync - After save, `spUpdateInvoiceTotal` is called for existing entries to recalculate any associated invoice totals - The claim's `CreateEntityFinishClaimRun` is triggered on save (claim finalization) ## Permissions & Security - Caller must be logged in (`curPerson.ID >= 1`) - Caller must have a company selected (`curCompany.ID >= 1`) - Caller must be a member of the current company - The claim must belong to the caller's firm (cross-firm write is blocked) - **ViewBillingRates** permission controls whether the caller can set or see hourly rates, totals, and reductions on time-based entries. Without this permission, these values are server-controlled. ## Data Flow 1. Validate ledger entry fields (date, amounts, contact, entry type, time-based rules) 2. Validate all referenced staff were part of the firm 3. Load the parent Claim entity and verify firm ownership 4. Find existing ledger entry in the ClaimLedgerEntries subtype (or prepare to create) 5. For existing entries: validate not on sent invoice or completed disbursal 6. Enforce billing permissions (override rate/total for users without ViewBillingRates) 7. For new entries: set FirmID, ClaimID, RequestorID, Invoiced=0; reject if CheckApproved 8. Validate monetary amount ceiling ($1B) 9. Load check approval settings for the firm 10. If total changed on an approved check above threshold: delete the approval record, reset CheckApproved 11. If total due is at/below threshold and check requested: auto-approve 12. Set all field values on the entity (dates, type, amounts, contact, staff, codes, etc.) 13. Validate hourly rate change reason if rate differs from default 14. Sync ad hoc fields to the ledger entry subtype 15. Sync claim ledger entry payments subtype 16. Save the parent Claim entity (triggers `CreateEntityFinishClaimRun`) 17. For existing entries: execute `spUpdateInvoiceTotal` to recalculate invoice 18. If user lacks ViewBillingRates on a time-based entry: zero out sensitive fields in the return object ## Side Effects - **Database write**: Claim entity saved (includes ledger entry as subtype) - **Database write**: Ad hoc fields synced (creates, updates, or removes subtype records) - **Database write**: Claim ledger entry payments synced - **Database delete**: Check approval record deleted if total changed beyond threshold - **Database update**: `spUpdateInvoiceTotal` recalculates invoice totals for existing entries - **Claim finalization**: `CreateEntityFinishClaimRun` is triggered on claim save ## Error Conditions - `RadoloException("You must be logged in to perform this action.")` - not authenticated - `RadoloException("You must have a company selected to perform this action.")` - no company - `RadoloException("You can only perform this action for a company you are a member of.")` - not in company - `RadoloException` with validation messages (date, amounts, contact, entry type, staff, hours, rate) - from `ValidateLedgerEntry` - `RadoloException("You tried to create a ledger entry in a firm[{X}] that you are not logged in to[{Y}].")` - claim belongs to different firm - `InvalidOperationException("This ledger entry is currently being used on a sent invoice...")` - entry on sent invoice - `InvalidOperationException("This ledger entry is currently being used on a completed disbursal...")` - entry on completed disbursal - `InvalidOperationException("A new entry for an approved check cannot be created.")` - new entry with CheckApproved - `InvalidOperationException("You cannot update fields values for items that have had checks issued.")` - modifying issued check fields - `RadoloException("Total, Paid, and Reduction must each be less than $1,000,000,000.")` - amount ceiling exceeded - `RadoloException("There was a problem retrieving your check approval settings.")` - settings lookup failed - `RadoloException("Changing a staff member's rate requires a Reason For Rate Change.")` - rate changed without reason - `RadoloException("Entry Type was missing")` - null entry type - `RadoloException("There was a problem saving this ledger entry : {error}")` - entity save failed - `RadoloException("There was a problem deleting this check person approval : {error}")` - approval delete failed ## Usage Notes - The `TotalChanged` parameter is a client-side hint indicating whether the user modified the total. It drives the check approval revocation logic. If the total changed but this flag is false, approvals will not be revoked. - Users without ViewBillingRates permission effectively have their rate/total fields server-calculated. The UI should reflect this by making those fields read-only for such users. - The check auto-approval threshold comes from company-level check approval settings. Entries below the threshold are auto-approved when a check is requested. - Ad hoc fields and payments are synced as subtypes, meaning records not present in the input are removed. Callers must send the complete set of ad hoc fields and payments. - The `AccountingExternalID` field (and related external check fields) can be updated even on entries with issued checks, enabling QuickBooks sync to update accounting references.