Skip to content

Commit 4754972

Browse files
committed
Fix custom unit ID mismatch when duplicating workspace
When duplicating a workspace, getCustomUnitsForDuplication() copied the source workspace's CustomUnit objects verbatim under new dictionary keys. This left the internal customUnitID and rate customUnitRateID properties pointing to the old workspace's IDs, creating a mismatch between the dictionary key and the object's own ID. When a user then created a distance manual expense in the duplicate workspace, the transaction was sent to the backend with the old customUnitID/customUnitRateID, which don't exist on the new workspace, causing an "Unexpected error" API failure. The fix clones each CustomUnit with updated customUnitID and regenerated rate IDs so the optimistic data is internally consistent.
1 parent 1dd01da commit 4754972

1 file changed

Lines changed: 23 additions & 3 deletions

File tree

src/libs/PolicyUtils.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import {getCategoryApproverRule} from './CategoryUtils';
4949
import {convertToBackendAmount} from './CurrencyUtils';
5050
import Navigation from './Navigation/Navigation';
5151
import {getIsOffline} from './NetworkState';
52+
import {generateHexadecimalValue} from './NumberUtils';
5253
import {formatMemberForList} from './OptionsListUtils';
5354
import type {MemberForList} from './OptionsListUtils';
5455
import {getAccountIDsByLogins, getLoginByAccountID, getLoginsByAccountIDs, getPersonalDetailByEmail} from './PersonalDetailsUtils';
@@ -307,6 +308,22 @@ function hasEligibleActiveAdminFromWorkspaces(policies: OnyxCollection<Policy> |
307308
return false;
308309
}
309310

311+
function cloneCustomUnitWithNewIDs(unit: CustomUnit, newCustomUnitID: string): CustomUnit {
312+
const newRates: Record<string, Rate> = {};
313+
for (const rate of Object.values(unit.rates)) {
314+
const newRateID = generateHexadecimalValue(13);
315+
newRates[newRateID] = {
316+
...rate,
317+
customUnitRateID: newRateID,
318+
};
319+
}
320+
return {
321+
...unit,
322+
customUnitID: newCustomUnitID,
323+
rates: newRates,
324+
};
325+
}
326+
310327
function getCustomUnitsForDuplication(
311328
policy: Policy,
312329
isDistanceRatesOptionSelected: boolean,
@@ -331,22 +348,25 @@ function getCustomUnitsForDuplication(
331348
return undefined;
332349
}
333350

334-
return {[distanceCustomUnitID]: distanceCustomUnit, [perDiemCustomUnitID]: perDiemUnit};
351+
return {
352+
[distanceCustomUnitID]: cloneCustomUnitWithNewIDs(distanceCustomUnit, distanceCustomUnitID),
353+
[perDiemCustomUnitID]: cloneCustomUnitWithNewIDs(perDiemUnit, perDiemCustomUnitID),
354+
};
335355
}
336356

337357
if (isDistanceRatesOptionSelected && distanceCustomUnitID) {
338358
const distanceCustomUnit = Object.values(customUnits).find((customUnit) => customUnit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE);
339359
if (!distanceCustomUnit) {
340360
return undefined;
341361
}
342-
return {[distanceCustomUnitID]: distanceCustomUnit};
362+
return {[distanceCustomUnitID]: cloneCustomUnitWithNewIDs(distanceCustomUnit, distanceCustomUnitID)};
343363
}
344364

345365
const perDiemUnit = Object.values(customUnits).find((customUnit) => customUnit.name === CONST.CUSTOM_UNITS.NAME_PER_DIEM_INTERNATIONAL);
346366
if (!perDiemUnit || !perDiemCustomUnitID) {
347367
return undefined;
348368
}
349-
return {[perDiemCustomUnitID]: perDiemUnit};
369+
return {[perDiemCustomUnitID]: cloneCustomUnitWithNewIDs(perDiemUnit, perDiemCustomUnitID)};
350370
}
351371

352372
/**

0 commit comments

Comments
 (0)