LoadVisits
Overview
LoadVisitsTask loads and prepares visits for pricing estimation by executing custom SQL queries to populate the VisitBatch table. It retrieves visit demographics, optionally adds charges, coverages, and service types, then synchronizes with existing visit data and includes visits from the reprocess queue.
Use this task when: You need to load scheduled visits into the batch processing pipeline with flexible custom queries, particularly when integrating with scheduling systems or databases with custom schemas.
Don't use this task when: You're importing visit data from HL7 feeds (use LoadHL7FilesTask), need to reprocess existing visits without reloading (use ReprocessChangedConfigurationsTask), or want to process visits already in the batch without reloading.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
VisitQuery |
string |
Yes | None | SQL query returning visit data. Must include columns matching Visit entity properties (e.g., Key, SourceID, ScheduledTime, Facility, PatientKey, Insurance). See field instructions for full list of supported columns. |
ChargeQuery |
string |
No | None | SQL query returning charges for visits. Must select SourceID, ChargeNumber, ChargeTypeID, Units, Codes. Joins to visits by SourceID. If omitted, visits load with zero charge only. |
ServiceTypeQuery |
string |
No | None | SQL query returning service types for quote generation. Must select SourceID, ServiceType. Creates quotes for each service type and COB combination. |
CoverageQuery |
string |
No | None | SQL query returning insurance coverages. Must select SourceID, Insurance, COB, and optionally MemberID, GroupID, GroupName, SourceInsuranceID, CoverageStatus. Updates or adds coverages to visits. |
AddZeroCharge |
bool |
No | true |
When true, automatically adds a zero-charge (ChargeType.ZeroID) as the first charge for each visit if not already present. Required for proper pricing calculations. |
Execution Flow
flowchart TD
A[Start LoadVisits] --> B[Truncate VisitBatch, QuoteBatch, CustomPropertyMapBatch tables]
B --> C[Execute VisitQuery]
C --> D[Load visits from query results]
D --> E[Track which columns were provided]
E --> F[Sync with existing Visit table data]
F --> G{ChargeQuery provided?}
G -->|Yes| H[Execute ChargeQuery for each visit by SourceID]
G -->|No| I{AddZeroCharge = true?}
H --> J[Add queried charges to visits]
I -->|Yes| K[Add zero charge to each visit]
I -->|No| L[Skip charge processing]
J --> M{CoverageQuery provided?}
K --> M
L --> M
M -->|Yes| N[Execute CoverageQuery]
M -->|No| O{ServiceTypeQuery provided?}
N --> P[Load all coverages into memory]
P --> Q[Update/add coverages to visits by SourceID]
Q --> O
O -->|Yes| R[Execute ServiceTypeQuery]
O -->|No| S[Check VisitReprocessQueue]
R --> T[Load service types into memory map]
T --> U[Create quotes for each service type x COB]
U --> S
S --> V[Load visits from reprocess queue]
V --> W[Merge queue visits with batch visits]
W --> X[Save all visits to VisitBatch table]
X --> Y[Fill CustomPropertyMapBatch]
Y --> Z[Load existing quotes for visits without new quotes]
Z --> AA[End]
style B fill:#ffe1e1
style D fill:#e1f5ff
style F fill:#e1f5ff
style J fill:#e1f5ff
style K fill:#e1f5ff
style Q fill:#e1f5ff
style U fill:#e1f5ff
style X fill:#e1f5ff
Processing Algorithm
The task follows a multi-phase approach to build complete visit data:
- Visit Loading Phase: Execute
VisitQueryand populate visit entities, tracking which properties were provided by the query - Synchronization Phase: For visits with existing
Keyvalues, load the existing visit from theVisittable and merge only the properties NOT provided by the query (preserving existing data for unprovided fields) - Charge Addition Phase:
- If
AddZeroCharge = true, insert a zero charge at position 0 for visits without one - If
ChargeQueryprovided, execute it once per visit using the visit'sSourceID, and replace charges 1+ with query results
- If
- Coverage Update Phase: If
CoverageQueryprovided, load all coverages into memory, then update visits bySourceID, removing coverages not in the query results - Quote Generation Phase: If
ServiceTypeQueryprovided, create quotes for each (ServiceType, COB) combination per visit - Reprocess Queue Merge: Add visits from
VisitReprocessQueuethat aren't already in the batch
Example Execution
Given 3 visits where Visit 1 and 2 have existing Keys:
- Visit 1: Query provides
ScheduledTimeandFacility; existing data providesPatientKey,Insurance,Charges,Quotes - Visit 2: Query provides
ScheduledTime,Facility,Insurance; existing data providesPatientKey,Charges,Quotes - Visit 3: No Key (new visit); query provides all fields
Result: All 3 visits are saved with merged data, preserving existing information not specified in the query.
Usage Examples
Example 1: Load visits with basic demographics only
var task = new LoadVisitsTask
{
VisitQuery = @"
select
VisitID as [Key],
VisitID as SourceID,
ScheduledDateTime as ScheduledTime,
FacilityCode as Facility,
PatientMRN as PatientKey,
PrimaryInsuranceCode as Insurance
from SchedulingSystem.Appointments
where ScheduledDateTime >= GETDATE()
and AppointmentStatus = 'Scheduled'",
AddZeroCharge = true // Will add zero charge automatically
};
Example 2: Load visits with charges from CPT code table
var task = new LoadVisitsTask
{
VisitQuery = @"
select
v.VisitID as [Key],
v.VisitID as SourceID,
v.ScheduledDateTime as ScheduledTime,
v.FacilityCode as Facility,
v.PatientMRN as PatientKey,
v.PrimaryInsuranceCode as Insurance
from SchedulingSystem.Visits v",
ChargeQuery = @"
select
VisitID as SourceID,
ROW_NUMBER() OVER (PARTITION BY VisitID ORDER BY LineNumber) as ChargeNumber,
ChargeType as ChargeTypeID,
Quantity as Units,
CPTCode as Codes
from SchedulingSystem.VisitCharges",
AddZeroCharge = true
};
Example 3: Load visits with multiple coverages and service types
var task = new LoadVisitsTask
{
VisitQuery = @"
select
VisitID as [Key],
VisitID as SourceID,
ScheduledDateTime as ScheduledTime,
FacilityCode as Facility,
PatientMRN as PatientKey
from SchedulingSystem.Visits",
CoverageQuery = @"
select
c.VisitID as SourceID,
c.InsuranceCode as Insurance,
c.COB,
c.MemberID,
c.GroupNumber as GroupID
from SchedulingSystem.VisitCoverages c
order by c.VisitID, c.COB",
ServiceTypeQuery = @"
select
VisitID as SourceID,
ServiceTypeCode as ServiceType
from SchedulingSystem.VisitServiceTypes",
AddZeroCharge = true
};
Example 4: Incremental load of visits scheduled in date range
var task = new LoadVisitsTask
{
VisitQuery = $@"
select
VisitID as [Key],
VisitID as SourceID,
ScheduledDateTime as ScheduledTime,
FacilityCode as Facility,
PatientMRN as PatientKey,
PrimaryInsuranceCode as Insurance
from SchedulingSystem.Visits
where ScheduledDateTime between '{DateTime.Today:yyyy-MM-dd}'
and '{DateTime.Today.AddDays(30):yyyy-MM-dd}'
and Status = 'Scheduled'",
AddZeroCharge = true
};
Related Tasks Comparison
| Task | Use Case | Query Flexibility | Data Source | Synchronization |
|---|---|---|---|---|
| LoadVisits | Custom SQL-based visit loading | Full SQL flexibility | Any queryable database | Merges with existing visits |
LoadHL7FilesTask |
Import from HL7 message files | Fixed HL7 format | HL7 ADT/SIU/DFT files | Creates new visits |
ReprocessChangedConfigurationsTask |
Queue existing visits for reprocessing | No queries (uses existing data) | Visit table | Adds to reprocess queue |
AutoApplyBundlesToVisitBatchTask |
Apply bundles to batch visits | No queries (operates on VisitBatch) | VisitBatch table | No synchronization |
Performance Considerations
Database Impact
- Batch Tables Truncated:
VisitBatch,QuoteBatch,CustomPropertyMapBatchare truncated at start - Per-Visit Queries: If
ChargeQueryis provided, executes one query per visit (can be expensive for large batches) - Tables Read:
Visit,Quote,VisitReprocessQueue,Insurance,ChargeType - Tables Written:
VisitBatch,QuoteBatch,CustomPropertyMapBatch - Bulk Operations: Uses
SaveManywith bulk copy for efficient batch inserts
Optimization Tips
| Scenario | Recommendation | Rationale |
|---|---|---|
| Large visit counts | Use indexed columns in WHERE clauses and avoid SELECT * | Reduces query execution time and network transfer |
| Per-visit charge lookups | Consider embedding charges in VisitQuery using PIVOT or FOR XML if possible | Eliminates N+1 query pattern (one query per visit) |
| Multiple coverages per visit | Ensure CoverageQuery has index on SourceID | Improves join performance during coverage updates |
| Service type explosion | Limit service types to those actually needed | Each service type creates quotes for each COB (multiplicative) |
| Reprocessing existing visits | Use Key column in VisitQuery to trigger synchronization | Preserves existing charges, quotes, and custom properties |
| Frequent execution | Schedule during off-hours | Truncate operations briefly lock tables |
Error Handling
The task uses fail-fast error handling for critical operations but continues processing where appropriate:
// Charge validation - fail fast if invalid
if (chargeNumber != visit.Charges.Count)
{
throw new Exception("Invalid charge number for visit: " + visit.Key);
}
Important Behaviors:
- Query Failures: Task fails immediately if any SQL query fails (VisitQuery, ChargeQuery, etc.)
- Charge Number Validation: Throws exception if ChargeQuery returns non-sequential charge numbers
- Duplicate COB Warning: Logs warning but continues if CoverageQuery attempts to add duplicate COB to a visit
- Missing Insurance: Creates VisitCoverage with null insurance if InsuranceKey not found in Insurance table
- Reprocess Queue: Always processes queue visits, even if VisitQuery fails to load them (provides fallback)
Common Pitfalls
| Issue | Problem | Solution |
|---|---|---|
| VisitQuery missing required columns | Task fails with "column not found" error | Include at minimum: SourceID, ScheduledTime, Facility, PatientKey. See VisitField for full list. |
| ChargeNumber not sequential | Exception: "Invalid charge number for visit" | ChargeQuery must return ChargeNumber as 1, 2, 3... for each visit. Use ROW_NUMBER() to generate sequential numbers. |
| ChargeQuery returns no results | Visits load with only zero charge (if enabled) | Verify SourceID values match between VisitQuery and ChargeQuery. Check for NULL SourceID values. |
| Duplicate COBs in CoverageQuery | Warning logged, first COB kept, duplicates ignored | Ensure CoverageQuery returns one row per (SourceID, COB) combination. |
| ServiceTypeQuery creates too many quotes | Performance degrades, database bloat | Each service type × each COB = one quote. Limit service types to those needed for estimation. |
| VisitQuery includes Key but visit doesn't exist | Visits treated as new (Key not found in Visit table) | Verify Key values exist in Visit table. Consider omitting Key for new visits (system generates automatically). |
| Existing charges/quotes lost | Synchronization overwrites data provided in queries | Only provide columns in VisitQuery that you want to update. Omitted columns preserve existing data. |
| AddZeroCharge = false breaks pricing | Pricing calculations fail without zero charge | Always use AddZeroCharge = true unless you provide zero charge explicitly in ChargeQuery. |
Code References
- Task Definition:
MDClarityCore/Backend/Scheduling/LoadVisitsTask.cs:14 - Task Field Definition:
MDClarityCore/Backend/Scheduling/LoadVisitsTask.cs:62 - Execute Method:
MDClarityCore/Backend/Scheduling/LoadVisitsTask.cs:28 - VisitLoader Class:
MDClarityCore/Backend/Scheduling/VisitLoader.cs:23 - Main Load Method:
MDClarityCore/Backend/Scheduling/VisitLoader.cs:68 - Visit Loading Logic:
MDClarityCore/Backend/Scheduling/VisitLoader.cs:121 - Charge Addition Logic:
MDClarityCore/Backend/Scheduling/VisitLoader.cs:152 - Coverage Loading Logic:
MDClarityCore/Backend/Scheduling/VisitLoader.cs:326 - Service Type Processing:
MDClarityCore/Backend/Scheduling/VisitLoader.cs:240 - Visit Synchronization:
MDClarityCore/Backend/Scheduling/VisitLoader.cs:480 - Reprocess Queue Integration:
MDClarityCore/Backend/Scheduling/VisitLoader.cs:461 - Integration Tests:
MDClarityCore/MDClarityTest/Tests/IntegrationTests/Backend/Tasks/LoadVisitsIntegrationTests.cs:18