Legacy Pipeline Atlas worker-1snapshot 2026-07-15

MDClarity · task primitive

LogVisitChangeEventsBatch

Overview

LogVisitChangeEventsBatchTask detects and logs changes between visits in the batch table (VisitBatch) and their corresponding production versions (Visit). It compares key visit properties, charges, quotes, insurance, and custom properties to identify what changed, then creates VisitChangeEvent records in VisitChangeEventBatch with detailed reasons for each change.

Use this task when: You need to track which visits have changed before determining GFE compliance, auditing visit modifications, or triggering downstream processes that depend on specific types of visit changes.

Don't use this task when: You only need to sync batch data to production (use SyncBatchTask), or you're working with entirely new visits that have no production counterparts (all new visits are automatically flagged as VisitChange.Initial).


Parameters

Parameter Type Required Default Description
None - - - This task has no configurable parameters. It automatically compares all visits in VisitBatch against their corresponding records in the Visit table.

Execution Flow

flowchart TD
    A[Start LogVisitChangeEventsBatch] --> B[Clear VisitChangeEventBatch table]
    B --> C[Load all visits from VisitBatch]
    C --> D[Load all existing visits from Visit table that exist in batch]
    D --> E[Load custom properties for batch visits]
    E --> F[Load custom properties for existing visits]
    F --> G[Load all quotes from QuoteBatch]
    G --> H[Load all quotes from Quote table for batch visits]
    H --> I[Join quotes to their respective visits]
    I --> J[Create visit mapping dictionary by Key]

    J --> K[For each batch visit]
    K --> L{Visit exists in production?}
    L -->|No| M[Create VisitChangeEvent with Initial reason]
    L -->|Yes| N[Compare visit properties and quotes]

    N --> O[Check for property changes]
    O --> P[Check: Charges, Insurance, Deposit, Patient Amount, Estimate, Facility, Provider, Status, Scheduled Time, Visit Type]
    P --> Q[Check: Auto Benefits Status, Custom Properties]
    Q --> R[Check: Quote changes - New/Deleted/Modified]
    R --> S{Any changes detected?}

    S -->|Yes| T[Create VisitChangeEvent with specific reasons]
    S -->|No| U[Skip - no event created]

    M --> V[Add event to list]
    T --> V
    U --> W{More visits?}
    V --> W
    W -->|Yes| K
    W -->|No| X[Assign entity keys to all events]
    X --> Y[Bulk save all VisitChangeEvents to VisitChangeEventBatch]
    Y --> Z[End]

    style B fill:#ffe1e1
    style N fill:#fff4e1
    style X fill:#fff4e1
    style Y fill:#e1f5ff

Processing Algorithm

The task uses a property-by-property comparison algorithm to detect changes:

Change Detection Logic

For each visit in VisitBatch:

  1. Check if visit exists in production (Visit table)

    • If not found: Create event with VisitChange.Initial reason
    • If found: Proceed to detailed comparison
  2. Compare basic visit properties:

    • Charges collection → VisitChange.ChargesChanged
    • InsuranceKeyVisitChange.InsuranceChanged
    • DepositAmountVisitChange.DepositAmountChanged
    • AmountPatientVisitChange.PatientAmountChanged
    • AmountAllowedVisitChange.EstimateChanged
    • FacilityKeyVisitChange.FacilityChanged
    • ProviderIDVisitChange.ProviderChanged
    • StatusVisitChange.StatusChanged
    • ScheduledTimeVisitChange.ScheduledTimeChanged
    • VisitTypeVisitChange.VisitTypeChanged
    • Benefits.AutoBenefitsStatusesVisitChange.VisitAutoBenefitsStatusChanged
  3. Compare custom properties:

    • Count mismatch OR any property key/value difference → VisitChange.CustomPropertiesChanged
  4. Compare quotes:

    • New quotes added → VisitChange.NewQuote
    • Quotes removed → VisitChange.QuoteDeleted
    • Quote benefits status changed → VisitChange.QuoteAutoBenefitsStatusChanged
  5. Create event only if changes detected:

    • Events with zero reasons are filtered out
    • Only visits with actual changes get VisitChangeEvent records

Example Execution

For a visit where insurance and provider changed:

Batch Visit V-1001:
  InsuranceKey: "INS-BCBS"
  ProviderID: "PRV-102"
  AmountPatient: $850.00

Production Visit V-1001:
  InsuranceKey: "INS-AETNA"  (different)
  ProviderID: "PRV-101"       (different)
  AmountPatient: $850.00      (same)

Comparison Result:
  ✓ InsuranceKey changed: INS-AETNA → INS-BCBS
  ✓ ProviderID changed: PRV-101 → PRV-102
  ✗ AmountPatient unchanged: $850.00

Created VisitChangeEvent:
  Key: "VC-0001" (auto-generated)
  Visit: "V-1001"
  Reasons: [InsuranceChanged, ProviderChanged]
  Saved to: VisitChangeEventBatch table

Usage Examples

Example 1: Basic task execution

var task = new LogVisitChangeEventsBatchTask();
// Task automatically compares VisitBatch vs Visit table
// No parameters needed

Example 2: Use before GFE compliance checking

// Common pattern: Log changes first, then check GFE compliance
var job = new Job
{
    Key = "JOB-GFE-COMPLIANCE",
    DisplayName = "Check GFE Compliance with Change Tracking",
    Tasks = new List<Task>
    {
        // First, log what changed
        new LogVisitChangeEventsBatchTask(),

        // Then, check GFE compliance (uses change events to determine resends)
        new CheckVisitGFEComplianceBatchTask()
    }
};

Example 3: Audit visit changes in data pipeline

// Track changes during batch processing workflow
var job = new Job
{
    Tasks = new List<Task>
    {
        // Load visits into batch
        new LoadVisitsTask
        {
            VisitQuery = "SELECT * FROM ExternalVisits WHERE ModifiedDate > @LastRun"
        },

        // Process visits and calculate estimates
        new BatchProcessAccountDataTask
        {
            Facilities = new List<string> { "FAC-001" },
            Insurances = new List<string>() // All insurances
        },

        // Log what changed for audit trail
        new LogVisitChangeEventsBatchTask(),

        // Sync to production
        new SyncBatchTask()
    }
};

Example 4: Query change events after execution

// After running the task, query the events
var task = new LogVisitChangeEventsBatchTask();
task.Execute(customerDataStore);

// Query the logged events
var visitChangeEventRepo = new VisitChangeEventRepo(customerDataStore);
var visit = visitRepo.GetByKey("V-1001");
List<VisitChangeEvent> events = visitChangeEventRepo.GetVisitChangeEventsBatchForVisit(visit);

foreach (var evt in events)
{
    Console.WriteLine($"Visit {evt.Visit} changed:");
    foreach (var reason in evt.Reasons)
    {
        Console.WriteLine($"  - {reason}");
    }
}

Related Tasks Comparison

Task Purpose Input → Output Tracks Changes Creates Events
LogVisitChangeEventsBatch Log visit changes between batch and production VisitBatch vs Visit → VisitChangeEventBatch Yes (property-by-property) Yes (batch table)
CheckVisitGFEComplianceBatch Determine GFE compliance and resend requirements VisitBatch + VisitChangeEventBatch → VisitGFEComplianceBatch No (uses change events) No (compliance records)
SyncBatch Promote batch data to production Batch tables → Production tables No No
AutoSendLettersTask Generate and send patient letters VisitBatch → Letters/Emails No Yes (send events)
LoadVisitsTask Load visits from external sources SQL queries → VisitBatch No No

Key Differentiators:


Performance Considerations

Database Impact

Optimization Tips

Scenario Recommendation Rationale
Large batch sizes (10K+ visits) Run during off-hours Task loads all visits and quotes into memory for comparison
Frequent unchanged visits Task is still efficient Only creates events for visits with actual changes
Chained with GFE compliance Always run LogVisitChangeEventsBatch first CheckVisitGFEComplianceBatch depends on VisitChangeEventBatch table
Audit trail requirements Query VisitChangeEventBatch after task Events are cleared on each run, so save to audit table if needed

Memory Considerations


Error Handling

The task uses a fail-fast approach with no error aggregation:

// If any repository operation fails, the entire task fails
// No try-catch blocks around individual visit comparisons
// All database operations use standard ADO.NET exception propagation

Important Behaviors:

No Partial Results: If the task fails midway through:


Common Pitfalls

Issue Problem Solution
Events cleared on each run VisitChangeEventBatch is truncated at task start, losing previous change history Save events to a permanent audit table if you need historical change tracking beyond the current batch
IsResendRequired not triggering CheckVisitGFEComplianceBatchTask returns false for IsResendRequired even though visit changed Run LogVisitChangeEventsBatchTask BEFORE CheckVisitGFEComplianceBatchTask in your job task list
Change events for unchanged visits Seeing VisitChangeEvents when you expect no changes Check for subtle differences: decimal precision in amounts, date/time precision, quote ordering, or benefits collection differences
Missing change events Expected VisitChangeEvents but none were created Verify visits exist in both VisitBatch and Visit tables. Only existing visits with differences create events (new visits get "Initial" reason)
Memory issues with large batches Task crashes or runs slowly with 50K+ visits Split large batches into smaller chunks, or run during low-memory periods. Consider pagination if processing extremely large visit sets
Custom property changes not detected CustomPropertiesChanged reason not appearing despite property changes Verify custom properties are in CustomPropertyMapBatch and CustomPropertyMap tables. Task only compares properties that are actually stored in these tables
Quote changes not detected NewQuote or QuoteDeleted reasons not appearing Ensure quotes are in QuoteBatch and Quote tables. Task compares quotes by Key, so quote keys must match for change detection

Code References