BatchProcessAccountData
Overview
BatchProcessAccountDataTask calculates estimates for patient accounts by iterating through all combinations of specified facilities, insurances, and bill types. It processes real patient account data with actual charges to generate or update account estimates in the AccountEstimate and AccountEstimateCharge tables.
Use this task when: You need to recalculate account estimates across multiple facilities, insurance types, or bill types simultaneously, such as after pricing configuration changes or contract updates.
Don't use this task when:
- Processing a single facility/insurance combination (use a custom approach or manual trigger instead)
- You need custom SQL-based account selection (this task uses facility/insurance/bill type list iteration)
- Processing scenario-based "what-if" estimates rather than actual account data
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
FacilityChoice |
FacilityChoice |
Yes | new FacilityChoice() |
Specifies which facilities to process. Can use AllFacilities = true, IndividualFacilities list, or FacilityGroups list. |
InsuranceSet |
List<string> |
No | Empty list (all insurances) | List of insurance keys to process. If empty, processes accounts for all insurances (represented as single null entry). |
BillTypeSet |
List<string> |
No | Empty list (all bill types) | List of bill type keys to process. If empty, processes accounts for all bill types (represented as single null entry). |
MinServiceDate |
DateTime? |
No | null |
Minimum service date filter for accounts to process. Null means no lower bound. |
MaxServiceDate |
DateTime? |
No | null |
Maximum service date filter for accounts to process. Null means no upper bound. |
ScenarioInsurance |
string |
No | null |
Optional insurance key for scenario-based "what-if" calculations. When set, calculates estimates as if accounts used this insurance instead of their actual insurance. |
Incremental |
bool |
No | true |
When true, only processes accounts without existing estimates (faster). When false, reprocesses all matching accounts regardless of existing estimates. |
UpdateDimensions |
bool |
No | false |
When true, updates dimension tables during processing. Set to true when dimension data may have changed. |
Execution Flow
flowchart TD
A[Start BatchProcessAccountDataTask] --> B{InsuranceSet empty?}
B -->|Yes| C[Add null to InsuranceSet<br/>all insurances]
B -->|No| D{BillTypeSet empty?}
C --> D
D -->|Yes| E[Add null to BillTypeSet<br/>all bill types]
D -->|No| F[Get facilities from FacilityChoice]
E --> F
F --> G[Initialize error counter]
G --> H[Loop: For each facility]
H --> I[Loop: For each insurance]
I --> J[Loop: For each bill type]
J --> K[Create ProcessAccountDataTask<br/>with current combination]
K --> L[Build SQL query with filters:<br/>facility, insurance, bill type,<br/>date range, incremental]
L --> M[Execute ProcessAccountData]
M --> N[Process accounts in batches]
N --> O{More bill types?}
O -->|Yes| J
O -->|No| P{More insurances?}
P -->|Yes| I
P -->|No| Q{More facilities?}
Q -->|Yes| H
Q -->|No| R{errorCount > 0?}
R -->|Yes| S[Log aggregated error message]
R -->|No| T[Complete successfully]
S --> T
style K fill:#e1f5ff
style M fill:#e1f5ff
style N fill:#fff4e1
style S fill:#ffe1e1
Processing Algorithm
The task uses a triple-nested loop to process all combinations of facilities, insurances, and bill types:
Total Iterations = Facilities × Insurances × Bill Types
For each combination:
1. Build SQL query to select matching accounts
2. Apply incremental filter (skip accounts with existing estimates if Incremental = true)
3. Process accounts in batches (batch size: determined by AccountProcessor)
4. Calculate estimates for each account using pricing engine
5. Save results to AccountEstimate and AccountEstimateCharge tables
6. Aggregate errors across all iterations
Example Execution
Scenario: Process accounts for 3 facilities, 2 insurances, 2 bill types
Facilities: ["FAC001", "FAC002", "FAC003"]
Insurances: ["INS-MEDICARE", "INS-BCBS"]
BillTypes: ["131", "141"]
Total Iterations: 3 × 2 × 2 = 12 separate processing runs
Each iteration processes:
- Accounts matching: specific facility + specific insurance + specific bill type
- With service dates in range (if MinServiceDate/MaxServiceDate set)
- Excluding accounts with estimates (if Incremental = true)
Usage Examples
Example 1: Reprocess all accounts for specific facilities
var task = new BatchProcessAccountDataTask
{
FacilityChoice = new FacilityChoice
{
IndividualFacilities = new List<string> { "FAC001", "FAC002" }
},
Incremental = false, // Reprocess everything, ignore existing estimates
UpdateDimensions = true // Update dimensions during processing
};
Example 2: Incremental processing for specific insurance and bill type
var task = new BatchProcessAccountDataTask
{
FacilityChoice = new FacilityChoice
{
AllFacilities = true // Process all facilities
},
InsuranceSet = new List<string> { "INS-MEDICARE" },
BillTypeSet = new List<string> { "131" },
Incremental = true, // Only process accounts without existing estimates
MinServiceDate = new DateTime(2024, 1, 1),
MaxServiceDate = new DateTime(2024, 12, 31)
};
Example 3: Scenario analysis with "what-if" insurance
var task = new BatchProcessAccountDataTask
{
FacilityChoice = new FacilityChoice
{
IndividualFacilities = new List<string> { "FAC001" }
},
ScenarioInsurance = "INS-BCBS", // Calculate estimates as if using BCBS insurance
Incremental = false
};
Example 4: Process recent accounts across facility groups
var task = new BatchProcessAccountDataTask
{
FacilityChoice = new FacilityChoice
{
FacilityGroups = new List<string> { "HOSPITAL-GROUP-1", "CLINIC-GROUP-A" }
},
MinServiceDate = DateTime.Now.AddMonths(-3), // Last 3 months
Incremental = true
};
Example 5: Full reprocessing after pricing update
var task = new BatchProcessAccountDataTask
{
FacilityChoice = new FacilityChoice
{
AllFacilities = true
},
InsuranceSet = new List<string> { "INS-MEDICARE", "INS-MEDICAID" },
Incremental = false, // Reprocess all, even with existing estimates
UpdateDimensions = true, // Ensure dimension tables are current
MinServiceDate = new DateTime(2024, 1, 1)
};
Related Tasks Comparison
| Task | Use Case | Granularity | Custom SQL | Batch Mode |
|---|---|---|---|---|
| BatchProcessAccountData | Bulk account recalculation across multiple facilities/insurances/bill types | Nested loops (facilities × insurances × bill types) | No | Yes (nested iteration) |
ProcessAccountData |
Single facility/insurance/bill type combination | Single combination | No | No |
ProcessAccountDataFromQuery |
Custom SQL-driven account selection | Query-defined | Yes | No |
LoadAccountData |
Import account data from external sources | File/table import | Yes | Import only (no calculation) |
When to choose:
- BatchProcessAccountData: Need to process multiple facility/insurance/bill type combinations systematically
- ProcessAccountData: Processing a single, specific facility/insurance/bill type combination
- ProcessAccountDataFromQuery: Need custom SQL logic to select accounts (complex business rules)
- LoadAccountData: Importing account data from files or external systems (no estimate calculation)
Performance Considerations
Database Impact
Tables Read:
Account- Source data for account selectionCharge- Associated charge data for estimatesAccountEstimate- Checked for existing estimates (if Incremental = true)- Dimension tables (if UpdateDimensions = true)
- Pricing configuration tables (FeeLogic, ContractMaps, Schedules)
Tables Written:
AccountEstimate- Account-level estimate resultsAccountEstimateCharge- Charge-level estimate details- Dimension tables (if UpdateDimensions = true)
Bulk Operations:
- Uses
SqlBulkCopywith batch size of 100,000 records for estimate inserts - Processes accounts in configurable batches (default varies by processor configuration)
- Uses
Optimization Tips
| Scenario | Recommendation | Rationale |
|---|---|---|
| First-time processing | Incremental = true |
Skips accounts with existing estimates, much faster |
| After pricing changes | Incremental = false |
Ensures all estimates reflect new pricing |
| Large account volumes | Limit facilities or use date filters | Reduces total iteration count and processing time |
| Scenario testing | Use ScenarioInsurance with single facility |
Enables "what-if" analysis without affecting production estimates |
| Scheduled nightly jobs | Incremental = true with recent date range |
Processes only new/changed accounts efficiently |
Expected Performance
- Iteration overhead: Each facility/insurance/bill type combination incurs setup time
- Account processing: Linear with account count per combination
- Bulk inserts: 100,000 records per batch provides efficient database writes
- Total time: Multiplicative based on combinations (3 facilities × 5 insurances × 2 bill types = 30 iterations)
Error Handling
The task uses error aggregation rather than fail-fast behavior:
// Errors are counted but don't stop processing
int errorCount = 0;
foreach (facility, insurance, billType combination) {
errorCount += ProcessAccountData(combination); // Returns error count
}
if (errorCount > 0) {
// Log single aggregated error after all iterations complete
LogException($"Total of {errorCount} errors when batch processing account data.");
}
Important:
- Individual combination failures don't prevent other combinations from processing
- Errors are logged but processing continues for remaining iterations
- Final error count is logged only if > 0
- Partial results are saved even if some iterations fail
- Check logs after execution to identify which facility/insurance/bill type combinations had issues
Common Pitfalls
| Issue | Problem | Solution |
|---|---|---|
| Empty InsuranceSet/BillTypeSet treated as "all" | Setting empty lists doesn't mean "skip" - it means "process all" | Explicitly set lists to null or omit the parameter if you want default behavior. Understand that empty = add single null entry = all. |
| Incremental mode skips updates | Incremental = true won't update existing estimates even if pricing changed |
Set Incremental = false when reprocessing after pricing/config changes |
| Massive iteration counts | All facilities × all insurances × all bill types can be hundreds of iterations | Use specific facility/insurance lists or date filters to reduce scope |
| Dimension tables out of date | Estimates use stale dimension data | Set UpdateDimensions = true when dimension data may have changed |
| Scenario insurance confusion | ScenarioInsurance overrides actual account insurance for calculations |
Only use for "what-if" analysis; remove for normal processing |
| Date filters miss accounts | MinServiceDate/MaxServiceDate filter on service date, not account creation date | Ensure date range covers the accounts you intend to process |
| Mixed bill type accounts | Accounts with multiple bill types may be processed in multiple iterations | This is expected - accounts appear in results for each matching bill type |
Code References
- Task Definition:
MDClarityCore/Backend/Tasks/BatchProcessAccountData/BatchProcessAccountDataTask.cs:13 - Task Fields:
MDClarityCore/Backend/Tasks/BatchProcessAccountData/BatchProcessAccountDataTaskField.cs:3 - Execution Logic:
MDClarityCore/Backend/Accounts/AccountProcessor.cs:97(BatchProcessAccountData method) - Single Account Processing:
MDClarityCore/Backend/Accounts/AccountProcessor.cs:28(ProcessAccountData method called per iteration) - Account Query Generation:
MDClarityCore/Backend/Accounts/AccountProcessorBase.cs(GetAccountSelectQuery method) - Bulk Copy Configuration:
MDClarityCore/Backend/Tasks/BatchProcessAccountData/BatchProcessAccountDataTask.cs:39(batch size: 100000) - Integration Tests:
MDClarityCore/MDClarityTest/Tests/IntegrationTests/Backend/Tasks/BatchProcessAccountDataIntegrationTests.cs:17