Calculation type
Configurable bilateral-surgery adjustment: finds charges whose schedule-type rollup indicator has a configured percentage multiplier, confirms bilaterality either via a modifier-50 code or via matching left/right (LT/RT) modifier pairs on the same procedure code, then scales each affected charge's already-computed allowed amount by the configured percentage; the LT/RT partner charge is zeroed and its AmountCharged folded onto the primary.
FindAffectedCharges: for i=1..NumCharges-1, charge qualifies if not IgnoreCharge, not matching any ExclusionCodes, and rollups[i]?.Code has an entry in BilateralPercentageMultipliers. ProcessChargeModifiers walks qualifying charges in ascending charge-index order: if charge has modifier 50 -> added to affectedCharges (primary). Else if it has LT or RT and a LATER (higher-index) qualifying charge with the same procedure code has the missing counterpart modifier -> that later charge is added to pairedCharges[laterIndex] = earlierIndex (earlier index becomes primary/150%, later becomes zeroed). allCharges = affectedCharges union pairedCharges.Keys, then reordered by SortBy for processing (SortBy only controls adjustment-application order, NOT which twin becomes primary -- that is fixed by ascending charge-array index at pairing time). GetAdjustedAmount: if chargeIndex in pairedCharges (i.e. it is the secondary) -> 0; else if in affectedCharges -> originalAmount * percentage/100 via MoneyMath.Multiply, where percentage comes from rollups.Where(r => r.Member == procedureCode).First().Code looked up via TryGetIndicatorPercentage (unguarded First(), no OrDefault). AdjustCharges: for each processed index, if adjustedAmount != detail.Amount, Adjust(); AND if that index is a key in pairedCharges, fold: primary.AmountCharged += secondary.AmountCharged; secondary.AmountCharged = 0.
| Key | Meaning |
|---|---|
| ScheduleType | Schedule type key used to look up the code-rollup indicator (bilateral surgery status) per charge, via IBilateralSurgeryScheduleRepo (adjustment as IScheduleTypeCalculation). |
| ModifierDimension | Dimension key (default "Modifier") on which codes "50" (bilateral), "RT", and "LT" are looked up on each charge. |
| SortBy | SortChargesBy enum controlling only the ORDER charges are processed for the Adjust() call (default AmountAllowed) -- does not affect which of a LT/RT pair is the 150% primary vs the zeroed secondary. |
| BilateralPercentageMultipliers | Dictionary mapping rollup indicator string to a percentage multiplier; V2's own constructor sets the default to {"1": 150.0} (overwriting the base class MedicareBilateralSurgeryAdjustment's default of {"0":150,"1":150,"2":150,"3":200} which runs first in the ctor chain). |
| ExclusionCodes | List of DimensionMember codes; any charge carrying one of these is excluded from eligibility entirely (checked in DoesAdjustmentApply). |
| BaseCodes/Percentages | Obsolete legacy fields retained only for backward compatibility; SetIndicatorPercentages() converts them into BilateralPercentageMultipliers only if that dictionary is empty. |
Gotchas
1) Percentage lookup in GetAdjustedAmount uses rollups.Where(r => r?.Member == procedureCode).First() -- an UNGUARDED First() with no fallback, unlike the eligibility check (rollups[chargeIndex]?.Code, index-based and null-safe). If no rollup record's Member matches the charge's procedure code exactly, this throws InvalidOperationException at adjustment time rather than skipping the charge. 2) The AmountCharged fold-in and the actual zeroing of the secondary/paired charge both happen only when the SECONDARY index is processed in AdjustCharges (pairedCharges.TryGetValue only succeeds for the key/secondary, not the primary) and are gated by `adjustedAmount != detail.Amount`; if the secondary charge's allowed amount is already exactly 0 going into this adjustment, that guard is false and the fold/zero never fires even though pairing was detected. 3) Which charge of an LT/RT pair becomes the 150% 'primary' vs. the zeroed 'secondary' is fixed by ascending Charges[] array index at pairing time (ProcessChargeModifiers iterates potentialAffectedCharges in that order), NOT by the configurable SortBy -- SortBy only reorders the later Adjust() application.
class · /home/cary/src/mdc/MdClarity/MDClarity/MDClarityCore/Backend/PricingEngine/Adjustments/BilateralSurgeryV2Adjustment.cs:10
calculator · /home/cary/src/mdc/MdClarity/MDClarity/MDClarityCore/Backend/PricingEngine/Adjustments/Calculators/BilateralSurgeryV2AdjustmentCalculator.cs:11
dispatch · CalculationEngine.cs:803
CORRECTED: draft's high-level formula (percentage lookup, modifier-50 vs LT/RT pairing, AmountCharged fold) was directionally right and confirmed against BilateralSurgeryV2Adjustment.cs, MedicareBilateralSurgeryAdjustment.cs and BilateralSurgeryV2AdjustmentCalculator.cs, but it omitted: the unguarded First() percentage lookup, the ascending-index (not SortBy) determination of primary vs. secondary, and the fold-in's dependence on the secondary's own zero-guard. Draft's key_attrs default of BilateralPercentageMultipliers={"1":150.0} is correct for V2 specifically (V2's own ctor overwrites the base class's {"0":150,"1":150,"2":150,"3":200} default) -- worth stating explicitly since a reader skimming only the base class would get the wrong default.