Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8d1888c
Add workspace.copyPolicySettings.error translation key
fedirjh May 7, 2026
9424295
Add CopyPolicySettings action file
fedirjh May 7, 2026
a48557e
Add unit tests for buildCopyPolicySettingsData
fedirjh May 7, 2026
87c6860
Merge branch 'Expensify:main' into copy-policy-settings-actions
fedirjh May 7, 2026
861c2b2
Merge branch 'Expensify:main' into copy-policy-settings-actions
fedirjh May 7, 2026
486f960
Use named type import in CopyPolicySettings
fedirjh May 7, 2026
b9e8d88
Use named imports in CopyPolicySettings action file
fedirjh May 7, 2026
4f46baf
Replace test custom unit IDs with hex digits to satisfy cspell
fedirjh May 7, 2026
fe3643f
Merge branch 'Expensify:main' into copy-policy-settings-actions
fedirjh May 7, 2026
51fdba8
Merge branch 'Expensify:main' into copy-policy-settings-actions
fedirjh May 8, 2026
0f3a8ae
Add documentation for sourcePolicyID in CopyPolicySettingsParams
fedirjh May 8, 2026
66bd695
Merge remote-tracking branch 'upstream' into copy-policy-settings-act…
fedirjh May 8, 2026
99e0b03
Merge remote-tracking branch 'upstream' into copy-policy-settings-act…
fedirjh May 8, 2026
a02be50
fix: update copyPolicySettings translations
fedirjh May 8, 2026
f4b1516
Merge branch 'Expensify:main' into copy-policy-settings-actions
fedirjh May 8, 2026
496ee58
Merge branch 'Expensify:main' into copy-policy-settings-actions
fedirjh May 8, 2026
502c5c0
Merge branch 'Expensify:main' into copy-policy-settings-actions
fedirjh May 10, 2026
b4e295a
Merge branch 'Expensify:main' into copy-policy-settings-actions
fedirjh May 11, 2026
0540b92
Merge branch 'Expensify:main' into copy-policy-settings-actions
fedirjh May 12, 2026
fb87736
Update Spanish translation for copy policy settings error message to …
fedirjh May 12, 2026
a2a17c7
chore: add source policy RBR error on failure in CopyPolicySettings
fedirjh May 12, 2026
a9c5913
chore: add description field to overview part in CopyPolicySettings
fedirjh May 12, 2026
cb3e661
refactor: clean up customUnits snapshot and add lifecycle comment
fedirjh May 12, 2026
3652170
test: add distanceRates and perDiem to per-part field patch test
fedirjh May 12, 2026
9fee396
test: add multi-target and empty source collection edge cases
fedirjh May 12, 2026
ad5dfc9
style: use consistent .at(0) array access in customUnits test
fedirjh May 12, 2026
883216b
Merge remote-tracking branch 'upstream' into copy-policy-settings-act…
fedirjh May 15, 2026
5d48595
fix: use null instead of undefined to clear currentStep in failureData
fedirjh May 15, 2026
06ac496
refactor: address PR review feedback for CopyPolicySettings
fedirjh May 15, 2026
8e749a2
Merge branch 'Expensify:main' into copy-policy-settings-actions
fedirjh May 15, 2026
e004d1a
Merge branch 'Expensify:main' into copy-policy-settings-actions
fedirjh May 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5991,6 +5991,7 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU
`Sie sind dabei, ${newWorkspaceName ?? ''} mit ${totalMembers ?? 0} Mitgliedern aus dem ursprünglichen Workspace zu erstellen und zu teilen.`,
error: 'Beim Duplizieren deines neuen Workspace ist ein Fehler aufgetreten. Bitte versuche es erneut.',
},
copyPolicySettings: {error: 'Beim Kopieren der Arbeitsbereichseinstellungen ist ein Fehler aufgetreten. Bitte versuche es erneut.'},
emptyWorkspace: {
title: 'Du hast keine Arbeitsbereiche',
subtitle: 'Belege erfassen, Auslagen erstatten, Reisen verwalten, Rechnungen versenden und mehr.',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6011,6 +6011,9 @@ const translations = {
`You’re about to create and share ${newWorkspaceName ?? ''} with ${totalMembers ?? 0} members from the original workspace.`,
error: 'An error occurred while duplicating your new workspace. Please try again.',
},
copyPolicySettings: {
error: 'An error occurred while copying workspace settings. Please try again.',
},
emptyWorkspace: {
title: 'No workspaces yet',
subtitle: 'Create a workspace to manage your expenses, reimbursements, and company cards.',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5826,6 +5826,9 @@ ${amount} para ${merchant} - ${date}`,
`Estás a punto de crear y compartir ${newWorkspaceName ?? ''} con ${totalMembers ?? 0} miembros del espacio de trabajo original.`,
error: 'Se produjo un error al duplicar tu nuevo espacio de trabajo. Inténtalo de nuevo.',
},
copyPolicySettings: {
error: 'Se produjo un error al copiar la configuración del espacio de trabajo. Por favor, inténtalo de nuevo.',
},
emptyWorkspace: {
title: 'Aún no hay espacios de trabajo',
subtitle: 'Crea un espacio de trabajo para gestionar tus gastos, reembolsos y tarjetas de empresa.',
Expand Down
1 change: 1 addition & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6011,6 +6011,7 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST.
`Vous êtes sur le point de créer et de partager ${newWorkspaceName ?? ''} avec ${totalMembers ?? 0} membres de l’espace de travail d’origine.`,
error: 'Une erreur s’est produite lors de la duplication de votre nouvel espace de travail. Veuillez réessayer.',
},
copyPolicySettings: {error: 'Une erreur s’est produite lors de la copie des paramètres de l’espace de travail. Veuillez réessayer.'},
emptyWorkspace: {
title: 'Vous n’avez aucun espace de travail',
subtitle: 'Suivez les reçus, remboursez les dépenses, gérez les voyages, envoyez des factures, et plus encore.',
Expand Down
1 change: 1 addition & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5981,6 +5981,7 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST.
`Stai per creare e condividere ${newWorkspaceName ?? ''} con ${totalMembers ?? 0} membri dello spazio di lavoro originale.`,
error: 'Si è verificato un errore durante la duplicazione del tuo nuovo workspace. Riprova.',
},
copyPolicySettings: {error: 'Si è verificato un errore durante la copia delle impostazioni dello spazio di lavoro. Riprova.'},
emptyWorkspace: {
title: 'Non hai nessuna area di lavoro',
subtitle: 'Tieni traccia delle ricevute, rimborsa le spese, gestisci i viaggi, invia le fatture e altro ancora.',
Expand Down
1 change: 1 addition & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5912,6 +5912,7 @@ _詳しい手順については、[ヘルプサイトをご覧ください](${CO
`元のワークスペースから ${totalMembers ?? 0} 人のメンバーと一緒に、${newWorkspaceName ?? ''} を作成して共有しようとしています。`,
error: '新しいワークスペースの複製中にエラーが発生しました。もう一度お試しください。',
},
copyPolicySettings: {error: 'ワークスペース設定のコピー中にエラーが発生しました。もう一度お試しください。'},
emptyWorkspace: {
title: 'ワークスペースがありません',
subtitle: '領収書を管理し、経費を精算し、出張を管理し、請求書を送信するなど、さまざまなことができます。',
Expand Down
1 change: 1 addition & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5959,6 +5959,7 @@ _Voor meer gedetailleerde instructies, [bezoek onze help-site](${CONST.NETSUITE_
`Je staat op het punt ${newWorkspaceName ?? ''} te maken en te delen met ${totalMembers ?? 0} leden van de oorspronkelijke werkruimte.`,
error: 'Er is een fout opgetreden bij het dupliceren van je nieuwe werkruimte. Probeer het opnieuw.',
},
copyPolicySettings: {error: 'Er is een fout opgetreden bij het kopiëren van de werkruimtainstellingen. Probeer het opnieuw.'},
emptyWorkspace: {
title: 'Je hebt geen werkruimtes',
subtitle: 'Volg bonnen, vergoed uitgaven, beheer reizen, verstuur facturen en meer.',
Expand Down
1 change: 1 addition & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5954,6 +5954,7 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy
`Za chwilę utworzysz i udostępnisz ${newWorkspaceName ?? ''} ${totalMembers ?? 0} członkom oryginalnego obszaru roboczego.`,
error: 'Wystąpił błąd podczas duplikowania Twojego nowego obszaru roboczego. Spróbuj ponownie.',
},
copyPolicySettings: {error: 'Wystąpił błąd podczas kopiowania ustawień przestrzeni roboczej. Spróbuj ponownie.'},
emptyWorkspace: {
title: 'Nie masz żadnych przestrzeni roboczych',
subtitle: 'Śledź paragony, rozliczaj wydatki, zarządzaj podróżami, wysyłaj faktury i wiele więcej.',
Expand Down
1 change: 1 addition & 0 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5960,6 +5960,7 @@ _Para instruções mais detalhadas, [visite nossa central de ajuda](${CONST.NETS
`Você está prestes to criar e compartilhar ${newWorkspaceName ?? ''} com ${totalMembers ?? 0} membros do workspace original.`,
error: 'Ocorreu um erro ao duplicar seu novo espaço de trabalho. Tente novamente.',
},
copyPolicySettings: {error: 'Ocorreu um erro ao copiar as configurações do workspace. Tente novamente.'},
emptyWorkspace: {
title: 'Você não tem nenhum workspace',
subtitle: 'Controle recibos, reembolse despesas, gerencie viagens, envie faturas e muito mais.',
Expand Down
1 change: 1 addition & 0 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5814,6 +5814,7 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM
`您即将创建并共享 ${newWorkspaceName ?? ''},其中包含来自原始工作区的 ${totalMembers ?? 0} 位成员。`,
error: '复制您的新工作区时发生错误。请重试。',
},
copyPolicySettings: {error: '复制工作区设置时发生错误。请重试。'},
emptyWorkspace: {
title: '你还没有工作区',
subtitle: '跟踪收据、报销费用、管理差旅、发送发票等。',
Expand Down
1 change: 1 addition & 0 deletions src/libs/API/parameters/CopyPolicySettingsParams.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
type CopyPolicySettingsParams = {
/** Source policy ID we're copying settings from */
sourcePolicyID: string;

/** CSV list of target policy IDs to copy settings into */
Expand Down
301 changes: 301 additions & 0 deletions src/libs/actions/Policy/CopyPolicySettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
import type {OnyxCollection, OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import {write} from '@libs/API';
import type {CopyPolicySettingsParams} from '@libs/API/parameters';
import {WRITE_COMMANDS} from '@libs/API/types';
import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils';
import {generateHexadecimalValue} from '@libs/NumberUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {CopyPolicySettings as CopyPolicySettingsState, Policy, PolicyCategories, PolicyTagLists} from '@src/types/onyx';
import type {CustomUnit} from '@src/types/onyx/Policy';

type Part = 'overview' | 'members' | 'reports' | 'accounting' | 'categories' | 'tags' | 'taxes' | 'workflows' | 'rules' | 'distanceRates' | 'perDiem' | 'invoices' | 'travel';

const PARTS_TO_POLICY_FIELDS = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick question: is codingRules intentionally not exposed as its own part?

The Auth treats codingRules as a separate part from rules (which covers workspace-wide violation thresholds like maxExpenseAmount, eReceipts, prohibitedExpenses). (Auth LOC ref)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, intentional per the design doc. The detailed implementation section does not list codingRules as a separate frontend part — it is folded into rules.

overview: ['outputCurrency', 'address', 'description'],
members: ['employeeList'],
reports: ['fieldList', 'areReportFieldsEnabled'],
accounting: ['connections', 'areConnectionsEnabled'],
categories: ['areCategoriesEnabled'],
tags: ['areTagsEnabled'],
taxes: ['tax', 'taxRates'],
// achAccount is intentionally excluded — the backend remaps bankAccountID per-caller
// (see Auth PR #21638). We rely on the server push for that field.
workflows: ['areWorkflowsEnabled', 'autoReportingFrequency', 'autoReporting', 'autoReportingOffset', 'harvesting', 'approvalMode', 'autoApproval', 'reimbursementChoice'],
rules: [
'areRulesEnabled',
'maxExpenseAmount',
'maxExpenseAge',
'maxExpenseAmountNoReceipt',
'maxExpenseAmountNoItemizedReceipt',
'defaultBillable',
'prohibitedExpenses',
'eReceipts',
'isAttendeeTrackingEnabled',
'preventSelfApproval',
'shouldShowAutoApprovalOptions',
'shouldShowAutoReimbursementLimitOption',
],
distanceRates: ['areDistanceRatesEnabled', 'customUnits'],
perDiem: ['arePerDiemRatesEnabled', 'customUnits'],
invoices: ['areInvoicesEnabled', 'invoice'],
travel: ['isTravelEnabled', 'travelSettings'],
} as const satisfies Record<Part, ReadonlyArray<keyof Policy>>;

type PolicyFieldsForPart = (typeof PARTS_TO_POLICY_FIELDS)[Part][number];

function setCopyPolicySettingsData(data: Partial<CopyPolicySettingsState>): void {
Onyx.merge(ONYXKEYS.COPY_POLICY_SETTINGS, data);
}

function clearCopyPolicySettings(): void {
Onyx.set(ONYXKEYS.COPY_POLICY_SETTINGS, {});
}

function requestCopyPolicySettingsNotification(): void {
write(WRITE_COMMANDS.COPY_POLICY_SETTINGS_NOTIFY, {});
}

function findCustomUnitByName(policy: Policy | undefined, unitName: string): CustomUnit | undefined {
if (!policy?.customUnits) {
return undefined;
}
return Object.values(policy.customUnits).find((unit) => unit.name === unitName);
}

/**
* Returns the customUnits patch to merge into the target policy when distanceRates and/or perDiem are
* being copied. The source unit data is written under the target's existing unit ID — a new ID is
* generated only when the target has no unit of that type yet.
*/
function buildCustomUnitsPatch(sourcePolicy: Policy, targetPolicy: Policy, isDistanceSelected: boolean, isPerDiemSelected: boolean): {customUnits: Record<string, CustomUnit>} | undefined {
if (!isDistanceSelected && !isPerDiemSelected) {
return undefined;
}

const patch: Record<string, CustomUnit> = {};

if (isDistanceSelected) {
const sourceDistance = findCustomUnitByName(sourcePolicy, CONST.CUSTOM_UNITS.NAME_DISTANCE);
if (sourceDistance) {
const targetDistance = findCustomUnitByName(targetPolicy, CONST.CUSTOM_UNITS.NAME_DISTANCE);
const targetUnitID = targetDistance?.customUnitID ?? generateHexadecimalValue(13);
patch[targetUnitID] = {...sourceDistance, customUnitID: targetUnitID};
}
}

if (isPerDiemSelected) {
const sourcePerDiem = findCustomUnitByName(sourcePolicy, CONST.CUSTOM_UNITS.NAME_PER_DIEM_INTERNATIONAL);
if (sourcePerDiem) {
const targetPerDiem = findCustomUnitByName(targetPolicy, CONST.CUSTOM_UNITS.NAME_PER_DIEM_INTERNATIONAL);
const targetUnitID = targetPerDiem?.customUnitID ?? generateHexadecimalValue(13);
patch[targetUnitID] = {...sourcePerDiem, customUnitID: targetUnitID};
}
}

if (Object.keys(patch).length === 0) {
return undefined;
}
return {customUnits: patch};
}

/**
* Returns the partial Policy patch derived from the selected `parts`, excluding fields whose
* mapping is handled separately (customUnits, categories, tags collection keys).
*/
function buildPolicyFieldPatch(sourcePolicy: Policy, parts: Part[]): Partial<Policy> {
const patch: Partial<Policy> = {};
for (const part of parts) {
for (const field of PARTS_TO_POLICY_FIELDS[part]) {
if (field === 'customUnits') {
continue;
}
// The PARTS_TO_POLICY_FIELDS values are typed as keyof Policy, so this assignment is safe.
(patch as Record<string, unknown>)[field] = sourcePolicy[field as keyof Policy];
}
}
return patch;
}

function buildExpandedPendingFields(parts: Part[]): Partial<Record<PolicyFieldsForPart, typeof CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE>> {
const pendingFields: Partial<Record<PolicyFieldsForPart, typeof CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE>> = {};
for (const part of parts) {
for (const field of PARTS_TO_POLICY_FIELDS[part]) {
pendingFields[field] = CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE;
}
}
return pendingFields;
}

function buildClearedPendingFields(parts: Part[]): Partial<Record<PolicyFieldsForPart, null>> {
const cleared: Partial<Record<PolicyFieldsForPart, null>> = {};
for (const part of parts) {
for (const field of PARTS_TO_POLICY_FIELDS[part]) {
cleared[field] = null;
}
}
return cleared;
}

type CopyPolicySettingsOnyxKeys =
| typeof ONYXKEYS.COLLECTION.POLICY
| typeof ONYXKEYS.COLLECTION.POLICY_CATEGORIES
| typeof ONYXKEYS.COLLECTION.POLICY_TAGS
| typeof ONYXKEYS.COPY_POLICY_SETTINGS;

function buildCopyPolicySettingsData(
sourcePolicy: Policy,
targetPolicies: Policy[],
parts: Part[],
allPolicyCategories: OnyxCollection<PolicyCategories>,
allPolicyTags: OnyxCollection<PolicyTagLists>,
): {
optimisticData: Array<OnyxUpdate<CopyPolicySettingsOnyxKeys>>;
successData: Array<OnyxUpdate<CopyPolicySettingsOnyxKeys>>;
failureData: Array<OnyxUpdate<CopyPolicySettingsOnyxKeys>>;
} {
const optimisticData: Array<OnyxUpdate<CopyPolicySettingsOnyxKeys>> = [];
const successData: Array<OnyxUpdate<CopyPolicySettingsOnyxKeys>> = [];
const failureData: Array<OnyxUpdate<CopyPolicySettingsOnyxKeys>> = [];

const policyFieldPatch = buildPolicyFieldPatch(sourcePolicy, parts);
const pendingFields = buildExpandedPendingFields(parts);
const clearedPendingFields = buildClearedPendingFields(parts);

const isCategoriesSelected = parts.includes('categories');
const isTagsSelected = parts.includes('tags');
const isDistanceSelected = parts.includes('distanceRates');
const isPerDiemSelected = parts.includes('perDiem');

const sourceCategoriesKey = `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${sourcePolicy.id}` as const;
const sourceTagsKey = `${ONYXKEYS.COLLECTION.POLICY_TAGS}${sourcePolicy.id}` as const;
const sourceCategories = allPolicyCategories?.[sourceCategoriesKey] ?? {};
const sourceTags = allPolicyTags?.[sourceTagsKey] ?? {};

for (const targetPolicy of targetPolicies) {
const policyKey = `${ONYXKEYS.COLLECTION.POLICY}${targetPolicy.id}` as const;
const customUnitsPatch = buildCustomUnitsPatch(sourcePolicy, targetPolicy, isDistanceSelected, isPerDiemSelected);

// Step 1+2: SET the full policy with patched fields overlaid.
// We use SET (not MERGE) because Onyx.merge deep-merges nested objects — source
// values would be merged into target's, leaving stale nested keys behind.
optimisticData.push({
onyxMethod: Onyx.METHOD.SET,
key: policyKey,
value: {
...targetPolicy,
...policyFieldPatch,
...(customUnitsPatch ? {customUnits: {...targetPolicy.customUnits, ...customUnitsPatch.customUnits}} : {}),
pendingFields: {...targetPolicy.pendingFields, ...pendingFields},
},
});
Comment thread
fedirjh marked this conversation as resolved.

// Success: clear pending markers and any leftover errors from a prior failure
successData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: policyKey,
value: {
pendingFields: clearedPendingFields,
errors: null,
},
});
Comment thread
fedirjh marked this conversation as resolved.

// Failure: restore the original target policy in full, surface RBR
failureData.push({
onyxMethod: Onyx.METHOD.SET,
key: policyKey,
value: {
...targetPolicy,
errors: getMicroSecondOnyxErrorWithTranslationKey('workspace.copyPolicySettings.error'),
},
});

// Step 3: collection keys (categories / tags) — SET-level overwrite with snapshot rollback
if (isCategoriesSelected) {
const targetCategoriesKey = `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${targetPolicy.id}` as const;
const previousCategories = allPolicyCategories?.[targetCategoriesKey] ?? {};
optimisticData.push({
onyxMethod: Onyx.METHOD.SET,
key: targetCategoriesKey,
value: sourceCategories,
});
failureData.push({
onyxMethod: Onyx.METHOD.SET,
key: targetCategoriesKey,
value: previousCategories,
});
}

if (isTagsSelected) {
const targetTagsKey = `${ONYXKEYS.COLLECTION.POLICY_TAGS}${targetPolicy.id}` as const;
const previousTags = allPolicyTags?.[targetTagsKey] ?? {};
optimisticData.push({
onyxMethod: Onyx.METHOD.SET,
key: targetTagsKey,
value: sourceTags,
});
failureData.push({
onyxMethod: Onyx.METHOD.SET,
key: targetTagsKey,
value: previousTags,
});
}
}

// Surface an RBR on the source policy row so the admin knows the bulk copy failed
failureData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${sourcePolicy.id}` as const,
value: {
errors: getMicroSecondOnyxErrorWithTranslationKey('workspace.copyPolicySettings.error'),
},
});

// Clear source policy errors on success (in case this is a retry after failure)
successData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${sourcePolicy.id}` as const,
value: {
errors: null,
},
});

// Step 4: drive currentStep on the COPY_POLICY_SETTINGS key itself.
// Success intentionally omits this key — the backend transitions currentStep
// to 'complete' via the bulkCopySettings NVP push.
optimisticData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.COPY_POLICY_SETTINGS,
value: {currentStep: 'loading'},
});

failureData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.COPY_POLICY_SETTINGS,
value: {currentStep: null},
});

return {optimisticData, successData, failureData};
}

function copyPolicySettings(
sourcePolicy: Policy,
targetPolicies: Policy[],
parts: Part[],
allPolicyCategories: OnyxCollection<PolicyCategories>,
allPolicyTags: OnyxCollection<PolicyTagLists>,
): void {
const {optimisticData, successData, failureData} = buildCopyPolicySettingsData(sourcePolicy, targetPolicies, parts, allPolicyCategories, allPolicyTags);

const params: CopyPolicySettingsParams = {
sourcePolicyID: sourcePolicy.id,
policyIDList: targetPolicies.map((policy) => policy.id).join(','),
parts: parts.join(','),
};

write(WRITE_COMMANDS.COPY_POLICY_SETTINGS, params, {optimisticData, successData, failureData});
}

export {PARTS_TO_POLICY_FIELDS, setCopyPolicySettingsData, clearCopyPolicySettings, requestCopyPolicySettingsNotification, buildCopyPolicySettingsData, copyPolicySettings};
export type {Part};
Loading
Loading