Hearing scheduling and calendar sync¶
Persists a legal-case hearing schedule and keeps the assigned attorney/ALJ Outlook/Exchange calendars in sync with the schedule's lifecycle.
| Field | Value |
|---|---|
| Operation ID | hearing-scheduling-and-calendar-sync |
| Trigger | Save or delete on clsLegalSchedule |
| Inputs | Schedule fields (type, status, date/time, attorney, ALJ, hearing location, duration) |
| Outputs | Persisted schedule row, Outlook/Exchange calendar appointments |
| Primary components | clsLegalSchedule.BeforeInsert/AfterInsert/BeforeUpdate/AfterUpdate/BeforeDelete/AfterDelete, clsLegalSchedule.UpdateCalendars, clsLegalSchedule.RemoveFromCalendars |
| Entry point | SAIF.Business.Claim.Legal\LegalNet\clsLegalSchedule.vb |
Steps¶
- Rule validation runs on
Save(), checking these real conditions on the object's fields: Rule_ValidTypeCode/Rule_ValidStatusCode—ScheduleTypeCode.Trim.Length > 0andScheduleStatusCode.Trim.Length > 0; either field left blank blocks the save (BR-SC-001).Rule_ValidDate— only runs whenIsSetorReset(status isSetorReset) and the object is dirty; requiresScheduleDate > Today(BR-SC-002).Rule_ValidAttorney— fails when bothIsAssignedToOutsideCounsel(an other-firm ID is set) andIsAssignedToSaifAttorney(a non-blank, non-"0" attorney employee ID is set) are true at the same time (BR-SC-003).Rule_ValidOutsideCounsel— fails whenAssignedOtherFirmID > 0butAssignedPersonID = 0, or whenAssignedOtherFirmID = 0butAssignedPersonID > 0; a firm and an attorney-at-the-firm must both be set or both be blank (BR-SC-004).Rule_ValidScheduleDate/Rule_ValidTime— requireScheduleDate > Date.MinValueandScheduleTime.TimeOfDay.Hours > 0(BR-SC-005).Rule_ValidTimeRange(LevelTwo) — requires the scheduled hour to be between 7 and 18 inclusive (BR-SC-006).Rule_ValidTimeDuration(LevelTwo) — whenDurationCode = "AM", the hour must be in[7, 12); whenDurationCode = "PM", the hour must be in[12, 18]; any other duration code passes without a time-range check (BR-SC-007).Rule_ValidDuration— requiresDurationCode.Trim.Length > 0(BR-SC-008).- On insert:
Create()(invoked beforeBeforeInsert) setsLegalCaseIDfrom the criteria, assigns a newScheduleIDfrom theSCHEDULE_SEQsequence, stampsCreateDate = NowandCreateUserfrom the portal user, and defaultsScheduleStatusCode = "SET".BeforeInsertthen builds the insert parameter list from every schedule field (type, status, keep-attorney flag, date/time, ALJ, hearing location, attorney/other-firm/person IDs, duration, assigned/reminder dates, schedule text), clearing the attorney employee ID to empty when it is"0"and clearing the other-firm/person IDs to empty when they are0so blank associations aren't stored as literal zeros. The framework issues the insert stored procedure, andAfterInsertchecks theRowsAffectedreturn parameter: if it is not exactly1it adds anInsertError, otherwise it marks the object no longer new. Calendar sync is not triggered directly fromAfterInsertin this class —UpdateCalendars()is available as a public method that callers (such as the assignment process) invoke to push schedule data into Outlook/Exchange. - On update:
BeforeUpdatebuilds the same field list asBeforeInsert(minus create-only fields, plusUpdateUser/UpdateDate, whichOnSave()stamps toNow/the current portal user just before the base save runs) for the update stored procedure.AfterUpdateperforms the identicalRowsAffected <> 1check, adding anInsertErrormessage (the same error key/text as the insert path is reused here) on failure or marking the object clean on success.UpdateCalendars()callsclsAssignmentProcess.updateOutlook("CASE", LegalCaseID, UserID), which re-reads the case'sclsOutlookFieldsrows and, for each one, rebuilds asaifApptkeyed by"S" + ScheduleID(zero padded to 8 digits): the subject is composed from schedule-type initial, status code (when notHearingSet), claim number, claimant name, schedule time, and either the ALJ name or the free-text schedule note, with" - WT 3"appended for weight-3 cases and" - CONSOL"appended for consolidated cases; the appointment's mailbox (UserName) is the assigned attorney's mail ID; start and end times are derived fromDurationCode(AM→ 8:00 AM–12:00 PM,PM→ 1:00 PM–5:00 PM,FULL→ 8:00 AM–5:00 PM same day, any multi-day duration code → 8:00 AM start through 5:00 PM onScheduleDate + (DaysNum - 1)days); if the schedule date is in the future and the status isPostponed,Cancelled, orDismissedthe appointment is deleted instead of upserted. The actual Exchange call (clsAssignmentProcess.callSaifExUpdate) then creates or updates the appointment viaclsExchangeUpdate.UpdateAppointment— there's no separate lookup step to decide create-vs-update; the Exchange service resolves that by the appointment'sExID. In non-production environments the mailbox ID is remapped to one of several fixed test mailboxes (ztstatt2/ztstatt3/ztstatt4) based on the first letter of the real mail ID, so calendar writes never reach a real attorney's mailbox outside production (BR-SC-009). - On delete:
BeforeDeletebuilds the delete stored-procedure call keyed byScheduleIDand writes an audit-log entry ("Deleting Schedule Record From Oracle") recording the schedule ID and user ID; it performs no additional business validation beyond that.AfterDeletechecksRowsAffected <> 1(adds aDeleteErroron failure) and otherwise marks the object new and callsRemoveFromCalendars().RemoveFromCalendars()only acts if the object is dirty and the attorney employee ID's original (pre-change) value is non-blank: it resolves that original attorney's mail ID, builds an appointment ID of"S" + ScheduleID, and callscallSaifExUpdatewith the delete flag set, searching an Exchange window from the original schedule date through 7 days later. For multi-day durations (DurationCodeending in"DAY"), it repeats the delete once per day, appending a zero-padded day suffix (-00,-01, and so on) to each appointment ID so every day's calendar entry is individually removed. GetLegalSecretaryID(inAssignedAttorneyID)runsSELECT secretary_emplid FROM attorney_profile WHERE profile_emplid = :IDfor the given attorney ID (or theOUTSIDECNCLconstant when the schedule is assigned to outside counsel), returning an empty string if no row is found; it backs theLegalSecretaryproperty, not the calendar appointment itself.GetALJName()runsSELECT last_name || ', ' || first_name FROM legal_person WHERE legal_person_seq_num = <ALJPersonID>and returns an empty string if no match is found; its result is whatupdateOutlookuses to append" - <ALJName>"to the appointment subject when an ALJ is assigned.
Process flow¶
flowchart TD
Start(["Save or delete<br>clsLegalSchedule"]) --> Rules{"Rules valid?"}
Rules -->|No| Err(["Save blocked,<br>errors surfaced"])
Rules -->|Yes| Op{"Operation?"}
Op -->|Insert| BI["BeforeInsert:<br>build insert params,<br>blank zero IDs"] --> Ins["Framework insert"] --> AI["AfterInsert:<br>check RowsAffected,<br>MarkNew"]
Op -->|Update| BU["BeforeUpdate:<br>build update params"] --> Upd["Framework update"] --> AU["AfterUpdate:<br>check RowsAffected"] --> UC["UpdateCalendars:<br>rebuild Outlook appt<br>via updateOutlook"]
Op -->|Delete| BD["BeforeDelete:<br>audit log entry"] --> Del["Framework delete"] --> AD["AfterDelete:<br>check RowsAffected,<br>MarkNew"] --> RC["RemoveFromCalendars:<br>delete Exchange appt(s)"]
AI --> EndOk(["Save complete"])
UC --> EndOk
RC --> EndOk
Failure and edge-case behavior¶
AfterInsert/AfterUpdate/AfterDeletecheckRowsAffected <> 1and raise an error message (InsertErroron insert/update,DeleteErroron delete) when the check fails, consistent with the library's optimistic-concurrency pattern.RemoveFromCalendars()only removes an appointment when the object is dirty and the attorney employee ID had a non-blank original value; a delete with no prior attorney assignment leaves no Exchange appointment to remove and is a no-op.- Non-production environments redirect calendar mailbox operations to one of several fixed test
mailboxes (
ztstatt2/ztstatt3/ztstatt4, selected by the first letter of the real mail ID) rather than the real attorney/ALJ mailbox. callSaifExUpdateandfindAppointmentswallow Exchange-service exceptions withclsLogErrorwhen running in a web request context, but re-throw them when running in batch mode (noHttpContext.Current).*
Related documentation¶
Note: Statements marked with
*were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts.
Sources¶
SAIF.Business.Claim.Legal\LegalNet\clsLegalSchedule.vbSAIF.Business.Claim.Legal\LegalNet\clsAssignmentProcess.vb