Skip to content

Commit 819ef80

Browse files
committed
Mirror getDefaultMileageRate selection in clone helper
Align cloneCustomUnitWithNewIDs with getDefaultMileageRate's exact default-rate selection: filter to enabled rates first, sort by index using CONST.DEFAULT_NUMBER_ID for missing indexes, then take the first. This guarantees the optimistic clone binds the new customUnitRateID to the same rate the expense flow will later treat as default — even in edge cases where source rates have an undefined index.
1 parent 1572326 commit 819ef80

2 files changed

Lines changed: 39 additions & 9 deletions

File tree

src/libs/PolicyUtils.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -309,12 +309,15 @@ function hasEligibleActiveAdminFromWorkspaces(policies: OnyxCollection<Policy> |
309309

310310
function cloneCustomUnitWithNewIDs(unit: CustomUnit, newCustomUnitID: string, newDefaultRateID?: string): CustomUnit {
311311
if (newDefaultRateID) {
312-
// The server-side DUPLICATE_POLICY assigns newDefaultRateID to the source's default rate
313-
// (lowest index, matching getDefaultMileageRate). Mirror that here so the optimistic data
314-
// doesn't get overwritten when the server response merges. Other source rates get fresh
315-
// server IDs, so we drop them from the optimistic state to avoid stale duplicates.
316-
const sortedRates = Object.values(unit.rates).sort((a, b) => (a.index ?? Number.MAX_SAFE_INTEGER) - (b.index ?? Number.MAX_SAFE_INTEGER));
317-
const defaultRate = sortedRates.find((rate) => rate.enabled !== false) ?? sortedRates.at(0);
312+
// The server-side DUPLICATE_POLICY assigns newDefaultRateID to the source's default rate.
313+
// Mirror getDefaultMileageRate's selection (enabled rates, sorted by index with
314+
// CONST.DEFAULT_NUMBER_ID for missing indexes) so the optimistic clone aligns with the
315+
// rate the expense flow will later treat as default. Other source rates get fresh server
316+
// IDs, so we drop them from the optimistic state to avoid stale duplicates.
317+
const defaultRate = Object.values(unit.rates)
318+
.filter((rate) => rate.enabled !== false)
319+
.sort((a, b) => (a.index ?? CONST.DEFAULT_NUMBER_ID) - (b.index ?? CONST.DEFAULT_NUMBER_ID))
320+
.at(0);
318321
return {
319322
...unit,
320323
customUnitID: newCustomUnitID,

tests/unit/PolicyUtilsTest.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ describe('PolicyUtils', () => {
386386
});
387387
});
388388

389-
it('falls back to the first rate when no enabled rate exists', () => {
389+
it('drops all rates when no enabled rate exists', () => {
390390
const distanceUnitAllDisabled = {
391391
customUnitID: 'srcDist',
392392
name: CONST.CUSTOM_UNITS.NAME_DISTANCE,
@@ -400,9 +400,36 @@ describe('PolicyUtils', () => {
400400
...createRandomPolicy(0),
401401
customUnits: {[distanceUnitAllDisabled.customUnitID]: distanceUnitAllDisabled},
402402
};
403-
const result = getCustomUnitsForDuplication(policyAllDisabled, true, false, {distanceCustomUnitID: 'newDist', perDiemCustomUnitID: 'newPerDiem', customUnitRateID: 'newRate'});
403+
const result = getCustomUnitsForDuplication(policyAllDisabled, true, false, {
404+
distanceCustomUnitID: 'newDist',
405+
perDiemCustomUnitID: 'newPerDiem',
406+
customUnitRateID: 'newRate',
407+
});
408+
expect(result?.newDist.rates).toEqual({});
409+
});
410+
411+
it('treats missing index as 0 when picking the default rate', () => {
412+
const distanceUnitWithMissingIndex = {
413+
customUnitID: 'srcDist',
414+
name: CONST.CUSTOM_UNITS.NAME_DISTANCE,
415+
enabled: true,
416+
attributes: {unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES},
417+
rates: {
418+
rateB: {customUnitRateID: 'rateB', name: 'Indexed Rate', rate: 100, currency: 'USD', enabled: true, index: 1},
419+
rateA: {customUnitRateID: 'rateA', name: 'No-Index Rate', rate: 70, currency: 'USD', enabled: true},
420+
},
421+
};
422+
const policyWithMissingIndex: Policy = {
423+
...createRandomPolicy(0),
424+
customUnits: {[distanceUnitWithMissingIndex.customUnitID]: distanceUnitWithMissingIndex},
425+
};
426+
const result = getCustomUnitsForDuplication(policyWithMissingIndex, true, false, {
427+
distanceCustomUnitID: 'newDist',
428+
perDiemCustomUnitID: 'newPerDiem',
429+
customUnitRateID: 'newRate',
430+
});
404431
expect(result?.newDist.rates).toEqual({
405-
newRate: {customUnitRateID: 'newRate', name: 'Disabled', rate: 50, currency: 'USD', enabled: false, index: 0},
432+
newRate: {customUnitRateID: 'newRate', name: 'No-Index Rate', rate: 70, currency: 'USD', enabled: true},
406433
});
407434
});
408435
});

0 commit comments

Comments
 (0)