LoadFeeSchedules
Overview
LoadFeeSchedulesTask generates fee schedules from pricing records stored in the database for specified schedule types. It processes pricing records containing fee amounts with optional modifiers and creates or updates Schedule, FeeScheduleRecord, and ScheduleMap tables to establish effective date ranges for facility/insurance combinations.
Use this task when: You need to create or update fee schedules from uploaded or modified pricing records, converting pricing data into time-based schedules that can be applied to billing and estimation.
Don't use this task when:
- Working with code schedules or case rate schedules (use
LoadCodeSchedulesTaskfor code schedules) - Building pricing records from existing schedules (use
BuildFeePricingFromSchedulesTaskfor reverse conversion) - Modifying individual schedule entries through the UI
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
ScheduleTypes |
List<string> |
Yes | Empty list | List of schedule type keys to process. Only pricing records matching these schedule types will be converted to schedules. Must contain at least one schedule type. |
Execution Flow
flowchart TD
A[Start LoadFeeSchedulesTask] --> B[Validate ScheduleTypes is not empty]
B --> C{Valid input?}
C -->|No| Z1[Throw exception - Schedule type not set]
C -->|Yes| D[For each ScheduleType in ScheduleTypes]
D --> E[Get all FeePricingRecords for ScheduleType with InUse=true]
E --> F[RemoveOutdatedSchedules: Get schedules without pricing records]
F --> G{Orphaned schedules exist?}
G -->|Yes| H[Delete orphaned schedules, schedule maps, and schedule records]
G -->|No| I[Continue]
H --> I
I --> J{For each FeePricingRecord}
J --> K{Schedule exists for this pricing record?}
K -->|No| L[Create new Schedule with generated key]
K -->|Yes| M[Use existing Schedule]
L --> N[Save Schedule]
M --> N
N --> O[Delete existing ScheduleMaps for this schedule]
O --> P[For each Insurance in pricing record]
P --> Q[For each Facility in pricing record]
Q --> R[Calculate end date based on next start date]
R --> S[Create ScheduleMapRecord]
S --> T{More facilities?}
T -->|Yes| Q
T -->|No| U{More insurances?}
U -->|Yes| P
U -->|No| V[Save all ScheduleMapRecords]
V --> W{New schedule?}
W -->|Yes| X[Create FeeScheduleRecords from FeePricingEntries]
W -->|No| Y[Skip schedule record creation]
X --> AA{More pricing records?}
Y --> AA
AA -->|Yes| J
AA -->|No| BB[Log summary]
BB --> CC{More schedule types?}
CC -->|Yes| D
CC -->|No| Z2[End]
Z1 --> Z2
style E fill:#e1f5ff
style L fill:#fff4e1
style V fill:#e1f5ff
style X fill:#e1f5ff
style H fill:#ffe1e1
Processing Algorithm
The task uses the ScheduleUpdateController with generic type parameters <FeePricingRecord, FeeScheduleRecord> to manage schedule generation:
- Input Validation: Verify that at least one schedule type is specified
- For Each Schedule Type:
- Retrieve all active fee pricing records (
InUse = true) matching the schedule type - Remove Outdated Schedules: Find schedules whose
PricingRecordKeyno longer exists in the pricing records and delete them along with their schedule maps and fee schedule records
- Retrieve all active fee pricing records (
- For Each Pricing Record:
- Find or Create Schedule: Look up existing schedule by
PricingRecordKey, or create new with generated key - Delete Old Schedule Maps: Remove existing schedule map records to rebuild with current data
- Calculate End Dates: For each facility/insurance combination:
- Query all start dates for the facility/insurance pair in the schedule type
- If this is the most recent start date, set end date to
2999-12-31(arbitrary far future) - Otherwise, set end date to one day before the next start date
- Create Schedule Maps: Generate
ScheduleMapRecordfor each facility/insurance combination with calculated date ranges - Create Fee Schedule Records (new schedules only): For each
FeePricingEntryin the pricing record's collection, create aFeeScheduleRecordwith Member, Modifier, Amount, Filter, and Priority values
- Find or Create Schedule: Look up existing schedule by
- Logging: Track counts of processed schedules and newly created schedules
Example Execution
Given two pricing records for schedule type Fixed-Fees:
Pricing Record 1:
- StartDate:
2025-01-01 - Facilities:
["FAC-A", "FAC-B"] - Insurances:
["INS-1"] - Entries:
[{Member: "99213", Modifier: "", Amount: 100}, {Member: "99213", Modifier: "26", Amount: 50}]
Pricing Record 2:
- StartDate:
2025-06-01 - Facilities:
["FAC-A"] - Insurances:
["INS-1"] - Entries:
[{Member: "99213", Modifier: "", Amount: 110}]
The task creates:
Schedule 1 (for Pricing Record 1):
- 2 ScheduleMapRecords:
FAC-A / INS-1: StartDate=2025-01-01, EndDate=2025-05-31(one day before next start)FAC-B / INS-1: StartDate=2025-01-01, EndDate=2999-12-31(no subsequent schedule for FAC-B)
- 2 FeeScheduleRecords:
{Member: "99213", Filter: "%", Priority: 1, Amount: 100}(base fee){Member: "99213", Filter: "%+26%", Priority: 2, Amount: 50}(modifier)
Schedule 2 (for Pricing Record 2):
- 1 ScheduleMapRecord:
FAC-A / INS-1: StartDate=2025-06-01, EndDate=2999-12-31(most recent for FAC-A)
- 1 FeeScheduleRecord:
{Member: "99213", Filter: "%", Priority: 1, Amount: 110}
Usage Examples
Example 1: Load schedules for a single schedule type
var task = new LoadFeeSchedulesTask
{
ScheduleTypes = new List<string> { "Fixed-Fees" }
};
// Processes all FeePricingRecords with ScheduleType = "Fixed-Fees" and InUse = true
// Creates or updates schedules for all facilities/insurances in the pricing records
Example 2: Load schedules for multiple schedule types
var task = new LoadFeeSchedulesTask
{
ScheduleTypes = new List<string> { "Fixed-Fees", "Profee-Clinic", "Surgery-Fees" }
};
// Processes all pricing records for all three schedule types
// Useful for bulk schedule updates across multiple fee types
Example 3: Rebuild schedules after uploading new pricing data
// Step 1: Upload pricing records using UpdateFeePricingRecordsTask or similar
var uploadTask = new UpdateFeePricingRecordsTask { ... };
// Step 2: Generate schedules from the new pricing records
var loadSchedulesTask = new LoadFeeSchedulesTask
{
ScheduleTypes = new List<string> { "Fixed-Fees" }
};
// Creates new schedules with proper effective date ranges
// Automatically handles date range calculations to prevent overlaps or gaps
Example 4: Updating schedules for specific facility types
// The task respects facility filters in the pricing records
// If pricing records were created for only "Hospital" facilities,
// only those facilities will have schedule maps created
var task = new LoadFeeSchedulesTask
{
ScheduleTypes = new List<string> { "Hospital-Fees" }
};
// Schedule maps are created only for facilities specified in the pricing records
Related Tasks Comparison
| Task | Direction | Input | Output | Use Case |
|---|---|---|---|---|
| LoadFeeSchedulesTask | Pricing → Schedules | FeePricingRecord + FeePricingEntry | Schedule + FeeScheduleRecord + ScheduleMap | Create/update time-based fee schedules from pricing data |
BuildFeePricingFromSchedulesTask |
Schedules → Pricing | Schedule + FeeScheduleRecord + ScheduleMap | FeePricingRecord + FeePricingEntry | Extract pricing records from existing schedules for updates or migration |
LoadCodeSchedulesTask |
Pricing → Schedules | CodePricingRecord + CodePricingEntry | Schedule + CodeScheduleRecord + ScheduleMap | Create/update code schedules (uses same controller but different record types) |
UpdateFeePricingRecordsTask |
Upload → Pricing | External data file | FeePricingRecord + FeePricingEntry | Import pricing data before schedule generation |
Key Differentiators:
- LoadFeeSchedulesTask is forward-looking: converts pricing records into active schedules
- BuildFeePricingFromSchedulesTask is backward-looking: extracts pricing records from existing schedules
- Both code and fee schedule tasks use the same
ScheduleUpdateControllerbut with different generic type parameters - End date calculation logic prevents schedule overlaps and automatically adjusts when new schedules are added
- Only processes pricing records marked as
InUse = true
Performance Considerations
Database Impact
- Tables Read:
FeePricingRecord: All records matching specified schedule types withInUse = trueFeePricingEntryCollection: All entries for each pricing recordSchedule: Lookup byPricingRecordKeyto find existing schedulesScheduleMap: Query to get all start dates for facility/insurance pairs (cached per schedule type)
- Tables Written:
Schedule: Insert for new schedulesScheduleMap: Delete old maps + bulk insert new maps for each scheduleFeeScheduleRecord: Insert for new schedules only (not updated for existing schedules)
- Query Patterns:
- Bulk retrieval of pricing records per schedule type
- Individual schedule lookups by pricing record key
- Start date queries for end date calculation (cached to avoid repeated queries)
- Bulk save operations for schedule maps
Optimization Tips
| Scenario | Recommendation | Rationale |
|---|---|---|
| Many pricing records | Process by schedule type separately | Reduces memory footprint and allows incremental progress tracking |
| Frequent schedule updates | Ensure pricing records have unique start dates per facility/insurance | Simplifies end date calculations and reduces schedule map churn |
| Initial schedule load | Run during off-hours | Creates many new records; schedule maps always deleted and recreated |
| Schedule map updates | Existing schedules only recreate maps, not records | Schedule records are immutable once created for a schedule key |
Important Performance Notes:
- Start date cache is built once per schedule type to avoid repeated database queries
- Schedule maps are always deleted and recreated, even for existing schedules, to ensure end date accuracy
- Fee schedule records are only created for new schedules, reducing write operations on updates
- Only pricing records with
InUse = trueare processed, allowing inactive records to be skipped
Error Handling
The task uses fail-fast error handling at the input validation stage:
if (scheduleTypes?.FirstOrDefault() == null)
{
throw new Exception("Schedule type not set.");
}
Important:
- If no schedule types are specified, the task immediately throws an exception
- Individual pricing record failures do not stop processing of other records
- Orphaned schedule deletion is logged but does not fail the task
- Schedule creation/update operations are transactional per pricing record
- The task logs summary information about processed and created schedules
Common Pitfalls
| Issue | Problem | Solution |
|---|---|---|
| No schedules created | ScheduleTypes list is empty | Verify that at least one schedule type is specified in the ScheduleTypes parameter |
| Pricing records ignored | Records have InUse = false |
Only pricing records with InUse = true are processed; set InUse = true for records that should be converted to schedules |
| Schedule maps have wrong end dates | Multiple schedules with same start date for facility/insurance | Ensure each pricing record has a unique start date per facility/insurance combination |
| Modifier entries missing | Modifiers not properly specified in pricing entries | Verify that pricing entries with modifiers have the modifier field populated; task will create Filter = %+MODIFIER% and Priority > 1 |
| Schedules not updated | Existing schedule records not refreshed | Schedule records are immutable once created; only schedule maps are updated for existing schedules. To update fee amounts, create a new pricing record with a different start date. |
| Orphaned schedules remain | Schedules exist for deleted pricing records | Task automatically removes orphaned schedules if the schedule type is in ScheduleTypes; run the task to clean up |
| Facilities excluded unexpectedly | Schedule type filter excludes facilities | Verify that pricing records include all desired facilities in their Facilities list; task only creates schedule maps for facilities specified in pricing records |
Code References
- Task Definition:
MDClarityCore/Backend/LoadSchedule/Tasks/FeeScheduleTasks/LoadFeeSchedulesTask.cs:12 - Task Field Definition:
MDClarityCore/Backend/LoadSchedule/Tasks/FeeScheduleTasks/LoadFeeSchedulesTask.cs:48 - Execution Entry Point:
MDClarityCore/Backend/LoadSchedule/Tasks/FeeScheduleTasks/LoadFeeSchedulesTask.cs:22 - Main Processing Logic:
MDClarityCore/Backend/LoadSchedule/Controllers/ScheduleUpdateController.cs:60 - Schedule Creation/Update:
MDClarityCore/Backend/LoadSchedule/Controllers/ScheduleUpdateController.cs:117 - Schedule Map Creation:
MDClarityCore/Backend/LoadSchedule/Controllers/ScheduleUpdateController.cs:149 - End Date Calculation:
MDClarityCore/Backend/LoadSchedule/Controllers/ScheduleUpdateController.cs:173 - Orphaned Schedule Removal:
MDClarityCore/Backend/LoadSchedule/Controllers/ScheduleUpdateController.cs:97 - Fee Schedule Record Creator:
MDClarityCore/Backend/LoadSchedule/Implementations/Creators/FeeScheduleRecordCreator.cs - Integration Tests:
MDClarityCore/MDClarityTest/Tests/IntegrationTests/Backend/LoadSchedule/LoadSchedules/FeeScheduleUpdateControllerIntegrationTests.cs:25 - Unit Tests:
MDClarityCore/MDClarityTest/Tests/UnitTests/Backend/LoadSchedule/LoadSchedules/ScheduleUpdateControllerTests.cs:18