@@ -352,7 +352,7 @@ describe('PolicyUtils', () => {
352352 ) . toBeUndefined ( ) ;
353353 } ) ;
354354
355- it ( 'clones the source default rate (lowest enabled index) under the API-known customUnitRateID' , ( ) => {
355+ it ( 'rebinds the default rate to the API-known customUnitRateID and keeps non-default rates with their source IDs ' , ( ) => {
356356 const distanceUnitWithMultipleRates = {
357357 customUnitID : 'srcDist' ,
358358 name : CONST . CUSTOM_UNITS . NAME_DISTANCE ,
@@ -377,13 +377,14 @@ describe('PolicyUtils', () => {
377377 ...distanceUnitWithMultipleRates ,
378378 customUnitID : 'newDist' ,
379379 rates : {
380+ rateB : { customUnitRateID : 'rateB' , name : 'New Rate 1' , rate : 100 , currency : 'USD' , enabled : true , index : 1 , attributes : { taxRateExternalID : 'tax_other' } } ,
380381 newRate : { customUnitRateID : 'newRate' , name : 'Default Rate' , rate : 70 , currency : 'USD' , enabled : true , index : 0 , attributes : { taxRateExternalID : 'tax_default' } } ,
381382 } ,
382383 } ,
383384 } ) ;
384385 } ) ;
385386
386- it ( 'drops all rates when no enabled rate exists' , ( ) => {
387+ it ( 'keeps source rates with their source IDs when no enabled rate exists' , ( ) => {
387388 const distanceUnitAllDisabled = {
388389 customUnitID : 'srcDist' ,
389390 name : CONST . CUSTOM_UNITS . NAME_DISTANCE ,
@@ -402,7 +403,9 @@ describe('PolicyUtils', () => {
402403 perDiemCustomUnitID : 'newPerDiem' ,
403404 customUnitRateID : 'newRate' ,
404405 } ) ;
405- expect ( result ?. newDist . rates ) . toEqual ( { } ) ;
406+ expect ( result ?. newDist . rates ) . toEqual ( {
407+ rateA : { customUnitRateID : 'rateA' , name : 'Disabled' , rate : 50 , currency : 'USD' , enabled : false , index : 0 } ,
408+ } ) ;
406409 } ) ;
407410
408411 it ( 'treats missing index as 0 when picking the default rate' , ( ) => {
@@ -426,9 +429,55 @@ describe('PolicyUtils', () => {
426429 customUnitRateID : 'newRate' ,
427430 } ) ;
428431 expect ( result ?. newDist . rates ) . toEqual ( {
432+ rateB : { customUnitRateID : 'rateB' , name : 'Indexed Rate' , rate : 100 , currency : 'USD' , enabled : true , index : 1 } ,
429433 newRate : { customUnitRateID : 'newRate' , name : 'No-Index Rate' , rate : 70 , currency : 'USD' , enabled : true } ,
430434 } ) ;
431435 } ) ;
436+
437+ it ( 'preserves source iteration order so getDefaultMileageRate still picks the original default when a non-default rate has a missing index' , ( ) => {
438+ // Source has 3 rates: Default (index 0), Indexed (index 1), and No-Index (no index field).
439+ // No-Index ties with Default at the index-0 sort key after `?? CONST.DEFAULT_NUMBER_ID`.
440+ // The optimistic clone must keep the source's iteration order so JavaScript's stable
441+ // sort still places Default before No-Index — otherwise getDefaultMileageRate would
442+ // pick whichever tied rate appeared first in iteration order, swapping the default.
443+ const sourceUnit = {
444+ customUnitID : 'srcDist' ,
445+ name : CONST . CUSTOM_UNITS . NAME_DISTANCE ,
446+ enabled : true ,
447+ attributes : { unit : CONST . CUSTOM_UNITS . DISTANCE_UNIT_MILES } ,
448+ rates : {
449+ rateA : { customUnitRateID : 'rateA' , name : 'Default Rate' , rate : 72.5 , currency : 'USD' , enabled : true , index : 0 } ,
450+ rateB : { customUnitRateID : 'rateB' , name : 'Indexed Rate' , rate : 100 , currency : 'USD' , enabled : true , index : 1 } ,
451+ rateC : { customUnitRateID : 'rateC' , name : 'No-Index Rate' , rate : 200 , currency : 'USD' , enabled : true } ,
452+ } ,
453+ } ;
454+ const policyWithTiedIndex : Policy = {
455+ ...createRandomPolicy ( 0 ) ,
456+ customUnits : { [ sourceUnit . customUnitID ] : sourceUnit } ,
457+ } ;
458+ const result = getCustomUnitsForDuplication ( policyWithTiedIndex , true , false , {
459+ distanceCustomUnitID : 'newDist' ,
460+ perDiemCustomUnitID : 'newPerDiem' ,
461+ customUnitRateID : 'newRate' ,
462+ } ) ;
463+
464+ const cloned = result ?. newDist ;
465+ if ( ! cloned ) {
466+ throw new Error ( 'Expected cloned distance unit' ) ;
467+ }
468+ // Iteration order: the rebound default should still be first, before the No-Index rate.
469+ const orderedKeys = Object . keys ( cloned . rates ) ;
470+ expect ( orderedKeys ) . toEqual ( [ 'newRate' , 'rateB' , 'rateC' ] ) ;
471+
472+ // Sanity-check that getDefaultMileageRate's selection (enabled, sorted by index ?? 0,
473+ // first) lands on the rebound default and not on No-Index Rate.
474+ const pickedDefault = Object . values ( cloned . rates )
475+ . filter ( ( rate ) => rate . enabled !== false )
476+ . sort ( ( a , b ) => ( a . index ?? CONST . DEFAULT_NUMBER_ID ) - ( b . index ?? CONST . DEFAULT_NUMBER_ID ) )
477+ . at ( 0 ) ;
478+ expect ( pickedDefault ?. customUnitRateID ) . toBe ( 'newRate' ) ;
479+ expect ( pickedDefault ?. name ) . toBe ( 'Default Rate' ) ;
480+ } ) ;
432481 } ) ;
433482 describe ( 'getRateDisplayValue' , ( ) => {
434483 it ( 'should return an empty string for NaN' , ( ) => {
0 commit comments