Calculation type

MultipleRadiologyAdjustment

Implements the CMS Multiple Procedure Payment Reduction (MPPR) for diagnostic imaging: qualifying charges are split per-unit into Technical (TC) and/or Professional (26) synthetic components, each component family is independently re-priced and ranked, and a rank-based percentage schedule reduces all but the top-ranked component in each family.

16
uses
2
customers
Adjustment
category
Charge
applies at

What it computes

rollups = IMultipleProcedureScheduleRepo.GetCodeScheduleRecords(context, ScheduleType); a charge qualifies if !IgnoreCharge, rollup!=null, no IgnoredModifierTypes code present on ModifierDimensionKey, rollup.Code is in BaseCodes, and it does NOT already carry BOTH TC and 26 modifiers. Each qualifying charge is split per-unit (unit=1..charge.Units) into a componentChargeNumber-tagged entry: charges with neither TC nor 26 ('global') are duplicated into BOTH the TC and 26 component lists; charges with only TC go to the TC list; only-26 go to the 26 list. Each component list is re-priced (CalculateSplitCharges builds a synthetic charge per component using ONLY the resolved base-code dimension member plus the TC/26 modifier, dropping every other code on the original charge, then runs calculationEngine.CalculateAmountAllowedBase) and independently sorted descending by per-unit AmountAllowed or AmountCharged via SortCharges (PEAmount throws 'Unrecognized sort order'). For each affected (original, pre-split) charge: reductionFraction(list, rank) = 1 - percentages[rank]/100 if rank < percentages.Count, else 1 - percentages[last]/100, else 0 if percentages is empty; adjustmentAmount = reductionFraction(TC list, TC rank) * TC-component AmountAllowed + reductionFraction(26 list, 26 rank) * 26-component AmountAllowed (both terms only for global charges; only the relevant term for TC-only/26-only charges); this is raw decimal multiplication with no MoneyMath rounding call. Then: if adjustmentAmount > detail.Amount, Adjust(0.0M); else if adjustmentAmount > 0.0M, Adjust(detail.Amount - adjustmentAmount); else if adjustmentAmount==0.0M and more than one charge was affected overall, Adjust(detail.Amount) unchanged (re-stamp).

Parameters

KeyMeaning
ScheduleTypeFee/rollup schedule used to fetch each charge's rollup CodeScheduleRecord and, via IScheduleCache.GetScheduleType, the ChargeCodeDimension used to rebuild the split TC/26 synthetic charges (default ProcedureCode).
BaseCodesList of rollup codes (default "4" / DefaultBaseCode) a charge's rollup Code must be in to qualify for the MPPR split.
AdjustmentTCPercentagesOrdered percent-to-pay list by rank for Technical-component charges (default [100, 50]); consumed as `1 - value/100` reduction fraction, i.e. it is the fraction to SUBTRACT, not the fraction paid.
Adjustment26PercentagesSame as AdjustmentTCPercentages but for Professional-component (26 modifier) charges (default [100]).
IgnoredModifierTypesModifier codes (default "59") that exempt a charge entirely from this adjustment when present on ModifierDimensionKey.
ModifierDimensionKeyDimension key used to test for TC/26/ignored modifiers on a charge (set via a DimensionReferenceField named 'ModifierDimension' in the UI).
SortBySortChargesBy enum (AmountAllowed or AmountCharged only) ranking the TC and 26 component lists independently, per-unit, descending; this class implements its own local SortCharges rather than calling the shared CalculationEngine.GetChargeOrder, but with the same per-unit-descending semantics and the same PEAmount-throws behavior.

Gotchas

1) Unlike MultipleProcedureAdjustment/MultipleSurgeryAdjustment/MultipleEndoscopyAdjustment, this calculator never calls MoneyMath - the percentage*AmountAllowed products and their sum are raw, unrounded decimal arithmetic, another entry in the engine's inconsistent-rounding surface. 2) The percentages configured are payment percentages by name ("100, 50") but GetPercentageFromCalculationPercentagesByUnitNumber converts them to `1 - value/100` REDUCTION fractions before use - a reimplementer who treats the returned value as 'fraction to keep' will invert the adjustment. 3) An empty percentages list yields reduction fraction 0 for every rank (the `else percentages.Any()` branch is skipped, `retPercentage` stays at its 0 initializer) - meaning an unconfigured TC or 26 percentage list results in NO reduction at all (full payment), not a zero payment. 4) The synthetic charge built for re-pricing each TC/26 component carries ONLY the resolved base-code dimension member plus the TC/26 modifier - every other code/dimension on the original charge (other modifiers, place-of-service, etc.) is dropped, so fee-schedule rules keyed on those other dimensions silently don't apply to the re-priced component amount. 5) componentChargeNumber is a single counter shared and incremented across ALL qualifying charges' units (not reset per charge), used only to correlate entries between the AffectedCharges/TC/26 lists after independent sorting - it is not the same as the original charge index.

In the source

class · /home/cary/src/mdc/MdClarity/MDClarity/MDClarityCore/Backend/PricingEngine/Adjustments/MultipleRadiologyAdjustment.cs:6
calculator · /home/cary/src/mdc/MdClarity/MDClarity/MDClarityCore/Backend/PricingEngine/Adjustments/Calculators/MultipleRadiologyAdjustmentCalculator.cs:13
dispatch · /home/cary/src/mdc/MdClarity/MDClarity/MDClarityCore/Backend/CalculationEngine.cs:794-798

CONFIRMED - re-read MultipleRadiologyAdjustment.cs and MultipleRadiologyAdjustmentCalculator.cs in full; the draft's formula was unusually precise for this batch and matched the code including the percentage-inversion semantics and the empty-list fallback, apart from omitting the rounding, dimension-dropping, and shared-counter details now added as gotchas.

Who uses it