Skip to content

Commit 9bd2810

Browse files
authored
Merge pull request Expensify#63202 from Expensify/cmartins-createChangeReportPolicyAndInviteSubmitter
Create change report policy and invite submitter
2 parents 5f4cfaf + 31094da commit 9bd2810

7 files changed

Lines changed: 288 additions & 210 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
type ChangeReportPolicyParams = {
2+
reportID: string;
3+
policyID: string;
4+
reportPreviewReportActionID: string;
5+
changePolicyReportActionID: string;
6+
policyExpenseChatReportID: string;
7+
policyExpenseCreatedReportActionID: string;
8+
};
9+
export default ChangeReportPolicyParams;

src/libs/API/parameters/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ export type {default as SaveCorpayOnboardingDirectorInformationParams} from './S
392392
export type {default as MoveIOUReportToPolicyAndInviteSubmitterParams} from './MoveIOUReportToPolicyAndInviteSubmitterParams';
393393
export type {default as MoveIOUReportToExistingPolicyParams} from './MoveIOUReportToExistingPolicyParams';
394394
export type {default as ChangeReportPolicyParams} from './ChangeReportPolicyParams';
395+
export type {default as ChangeReportPolicyAndInviteSubmitterParams} from './ChangeReportPolicyAndInviteSubmitterParams';
395396
export type {ChangeTransactionsReportParams, TransactionThreadInfo} from './ChangeTransactionsReportParams';
396397
export type {default as ResetBankAccountSetupParams} from './ResetBankAccountSetupParams';
397398
export type {default as SendRecapInAdminsRoomParams} from './SendRecapInAdminsRoomParams';

src/libs/API/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,7 @@ const WRITE_COMMANDS = {
478478
ADD_WORK_EMAIL: 'AddWorkEmail',
479479
SAVE_CORPAY_ONBOARDING_DIRECTOR_INFORMATION: 'SaveCorpayOnboardingDirectorInformation',
480480
CHANGE_REPORT_POLICY: 'ChangeReportPolicy',
481+
CHANGE_REPORT_POLICY_AND_INVITE_SUBMITTER: 'ChangeReportPolicyAndInviteSubmitter',
481482
CHANGE_TRANSACTIONS_REPORT: 'ChangeTransactionsReport',
482483
SEND_RECAP_IN_ADMINS_ROOM: 'SendRecapInAdminsRoom',
483484
RETRACT_REPORT: 'RetractReport',
@@ -985,6 +986,7 @@ type WriteCommandParameters = {
985986
[WRITE_COMMANDS.MERGE_WITH_VALIDATE_CODE]: Parameters.MergeWithValidateCodeParams;
986987
// Change report policy
987988
[WRITE_COMMANDS.CHANGE_REPORT_POLICY]: Parameters.ChangeReportPolicyParams;
989+
[WRITE_COMMANDS.CHANGE_REPORT_POLICY_AND_INVITE_SUBMITTER]: Parameters.ChangeReportPolicyAndInviteSubmitterParams;
988990

989991
[WRITE_COMMANDS.PAY_AND_DOWNGRADE]: null;
990992

src/libs/ReportUtils.ts

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9573,6 +9573,215 @@ function createDraftTransactionAndNavigateToParticipantSelector(
95739573
return createDraftWorkspaceAndNavigateToConfirmationScreen(transactionID, actionName);
95749574
}
95759575

9576+
/**
9577+
* @private
9578+
* Builds a map of parentReportID to child report IDs for efficient traversal.
9579+
*/
9580+
function buildReportIDToThreadsReportIDsMap(): Record<string, string[]> {
9581+
const reportIDToThreadsReportIDsMap: Record<string, string[]> = {};
9582+
Object.values(allReports ?? {}).forEach((report) => {
9583+
if (!report?.parentReportID) {
9584+
return;
9585+
}
9586+
if (!reportIDToThreadsReportIDsMap[report.parentReportID]) {
9587+
reportIDToThreadsReportIDsMap[report.parentReportID] = [];
9588+
}
9589+
reportIDToThreadsReportIDsMap[report.parentReportID].push(report.reportID);
9590+
});
9591+
return reportIDToThreadsReportIDsMap;
9592+
}
9593+
9594+
/**
9595+
* @private
9596+
* Recursively updates the policyID for a report and all its child reports.
9597+
*/
9598+
function updatePolicyIdForReportAndThreads(
9599+
currentReportID: string,
9600+
policyID: string,
9601+
reportIDToThreadsReportIDsMap: Record<string, string[]>,
9602+
optimisticData: OnyxUpdate[],
9603+
failureData: OnyxUpdate[],
9604+
) {
9605+
const currentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${currentReportID}`];
9606+
const originalPolicyID = currentReport?.policyID;
9607+
9608+
if (originalPolicyID) {
9609+
optimisticData.push({
9610+
onyxMethod: Onyx.METHOD.MERGE,
9611+
key: `${ONYXKEYS.COLLECTION.REPORT}${currentReportID}`,
9612+
value: {policyID},
9613+
});
9614+
failureData.push({
9615+
onyxMethod: Onyx.METHOD.MERGE,
9616+
key: `${ONYXKEYS.COLLECTION.REPORT}${currentReportID}`,
9617+
value: {policyID: originalPolicyID},
9618+
});
9619+
}
9620+
9621+
// Recursively process child reports for the current report
9622+
const childReportIDs = reportIDToThreadsReportIDsMap[currentReportID] || [];
9623+
childReportIDs.forEach((childReportID) => {
9624+
updatePolicyIdForReportAndThreads(childReportID, policyID, reportIDToThreadsReportIDsMap, optimisticData, failureData);
9625+
});
9626+
}
9627+
9628+
function buildOptimisticChangePolicyData(report: Report, policyID: string, optimisticPolicyExpenseChatReport?: Report | undefined) {
9629+
const optimisticData: OnyxUpdate[] = [];
9630+
const successData: OnyxUpdate[] = [];
9631+
const failureData: OnyxUpdate[] = [];
9632+
9633+
// 1. Optimistically set the policyID on the report (and all its threads) by:
9634+
// 1.1 Preprocess reports to create a map of parentReportID to child reports list of reportIDs
9635+
// 1.2 Recursively update the policyID of the report and all its child reports
9636+
const reportID = report.reportID;
9637+
const reportIDToThreadsReportIDsMap = buildReportIDToThreadsReportIDsMap();
9638+
updatePolicyIdForReportAndThreads(reportID, policyID, reportIDToThreadsReportIDsMap, optimisticData, failureData);
9639+
9640+
// 2. If the old workspace had a expense chat, mark the report preview action as deleted
9641+
if (report.parentReportID && report.parentReportActionID) {
9642+
const oldWorkspaceChatReportID = report.parentReportID;
9643+
const oldReportPreviewActionID = report.parentReportActionID;
9644+
const oldReportPreviewAction = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldWorkspaceChatReportID}`]?.[oldReportPreviewActionID];
9645+
const deletedTime = DateUtils.getDBTime();
9646+
const firstMessage = Array.isArray(oldReportPreviewAction?.message) ? oldReportPreviewAction.message.at(0) : null;
9647+
const updatedReportPreviewAction = {
9648+
...oldReportPreviewAction,
9649+
originalMessage: {
9650+
deleted: deletedTime,
9651+
},
9652+
...(firstMessage && {
9653+
message: [
9654+
{
9655+
...firstMessage,
9656+
deleted: deletedTime,
9657+
},
9658+
...(Array.isArray(oldReportPreviewAction?.message) ? oldReportPreviewAction.message.slice(1) : []),
9659+
],
9660+
}),
9661+
...(!Array.isArray(oldReportPreviewAction?.message) && {
9662+
message: {
9663+
deleted: deletedTime,
9664+
},
9665+
}),
9666+
};
9667+
9668+
optimisticData.push({
9669+
onyxMethod: Onyx.METHOD.MERGE,
9670+
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldWorkspaceChatReportID}`,
9671+
value: {[oldReportPreviewActionID]: updatedReportPreviewAction},
9672+
});
9673+
failureData.push({
9674+
onyxMethod: Onyx.METHOD.MERGE,
9675+
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldWorkspaceChatReportID}`,
9676+
value: {[oldReportPreviewActionID]: oldReportPreviewAction},
9677+
});
9678+
9679+
// Update the expense chat report
9680+
const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oldWorkspaceChatReportID}`];
9681+
const lastMessageText = getLastVisibleMessage(oldWorkspaceChatReportID, {[oldReportPreviewActionID]: updatedReportPreviewAction as ReportAction})?.lastMessageText;
9682+
const lastVisibleActionCreated = getReportLastMessage(oldWorkspaceChatReportID, {[oldReportPreviewActionID]: updatedReportPreviewAction as ReportAction})?.lastVisibleActionCreated;
9683+
9684+
optimisticData.push({
9685+
onyxMethod: Onyx.METHOD.MERGE,
9686+
key: `${ONYXKEYS.COLLECTION.REPORT}${oldWorkspaceChatReportID}`,
9687+
value: {
9688+
hasOutstandingChildRequest: false,
9689+
iouReportID: null,
9690+
lastMessageText,
9691+
lastVisibleActionCreated,
9692+
},
9693+
});
9694+
failureData.push({
9695+
onyxMethod: Onyx.METHOD.MERGE,
9696+
key: `${ONYXKEYS.COLLECTION.REPORT}${oldWorkspaceChatReportID}`,
9697+
value: chatReport,
9698+
});
9699+
}
9700+
9701+
// 3. Optimistically create a new REPORT_PREVIEW reportAction with the newReportPreviewActionID
9702+
// and set it as a parent of the moved report
9703+
const newPolicyExpenseChatReport = optimisticPolicyExpenseChatReport ?? getPolicyExpenseChat(currentUserAccountID, policyID);
9704+
const optimisticReportPreviewAction = buildOptimisticReportPreview(newPolicyExpenseChatReport, report);
9705+
9706+
if (newPolicyExpenseChatReport) {
9707+
const newPolicyExpenseChatReportID = newPolicyExpenseChatReport.reportID;
9708+
9709+
optimisticData.push({
9710+
onyxMethod: Onyx.METHOD.MERGE,
9711+
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${newPolicyExpenseChatReportID}`,
9712+
value: {[optimisticReportPreviewAction.reportActionID]: optimisticReportPreviewAction},
9713+
});
9714+
successData.push({
9715+
onyxMethod: Onyx.METHOD.MERGE,
9716+
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${newPolicyExpenseChatReportID}`,
9717+
value: {
9718+
[optimisticReportPreviewAction.reportActionID]: {
9719+
pendingAction: null,
9720+
},
9721+
},
9722+
});
9723+
failureData.push({
9724+
onyxMethod: Onyx.METHOD.MERGE,
9725+
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${newPolicyExpenseChatReportID}`,
9726+
value: {[optimisticReportPreviewAction.reportActionID]: null},
9727+
});
9728+
9729+
// Set the new report preview action as a parent of the moved report,
9730+
// and set the parentReportID on the moved report as the expense chat reportID
9731+
optimisticData.push({
9732+
onyxMethod: Onyx.METHOD.MERGE,
9733+
key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
9734+
value: {parentReportActionID: optimisticReportPreviewAction.reportActionID, parentReportID: newPolicyExpenseChatReportID},
9735+
});
9736+
failureData.push({
9737+
onyxMethod: Onyx.METHOD.MERGE,
9738+
key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
9739+
value: {parentReportActionID: report.parentReportActionID, parentReportID: report.parentReportID},
9740+
});
9741+
9742+
// Set lastVisibleActionCreated
9743+
optimisticData.push({
9744+
onyxMethod: Onyx.METHOD.MERGE,
9745+
key: `${ONYXKEYS.COLLECTION.REPORT}${newPolicyExpenseChatReportID}`,
9746+
value: {lastVisibleActionCreated: optimisticReportPreviewAction?.created},
9747+
});
9748+
failureData.push({
9749+
onyxMethod: Onyx.METHOD.MERGE,
9750+
key: `${ONYXKEYS.COLLECTION.REPORT}${newPolicyExpenseChatReportID}`,
9751+
value: {lastVisibleActionCreated: newPolicyExpenseChatReport.lastVisibleActionCreated},
9752+
});
9753+
}
9754+
9755+
// 4. Optimistically create a CHANGE_POLICY reportAction on the report using the reportActionID
9756+
const optimisticMovedReportAction = buildOptimisticChangePolicyReportAction(report.policyID, policyID);
9757+
optimisticData.push({
9758+
onyxMethod: Onyx.METHOD.MERGE,
9759+
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
9760+
value: {[optimisticMovedReportAction.reportActionID]: optimisticMovedReportAction},
9761+
});
9762+
successData.push({
9763+
onyxMethod: Onyx.METHOD.MERGE,
9764+
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
9765+
value: {
9766+
[optimisticMovedReportAction.reportActionID]: {
9767+
pendingAction: null,
9768+
errors: null,
9769+
},
9770+
},
9771+
});
9772+
failureData.push({
9773+
onyxMethod: Onyx.METHOD.MERGE,
9774+
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
9775+
value: {
9776+
[optimisticMovedReportAction.reportActionID]: {
9777+
errors: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
9778+
},
9779+
},
9780+
});
9781+
9782+
return {optimisticData, successData, failureData, optimisticReportPreviewAction, optimisticMovedReportAction};
9783+
}
9784+
95769785
/**
95779786
* Check if a report has any forwarded actions
95789787
*/
@@ -10691,6 +10900,7 @@ export {
1069110900
buildOptimisticUnapprovedReportAction,
1069210901
buildOptimisticCancelPaymentReportAction,
1069310902
buildOptimisticChangedTaskAssigneeReportAction,
10903+
buildOptimisticChangePolicyData,
1069410904
buildOptimisticChatReport,
1069510905
buildOptimisticClosedReportAction,
1069610906
buildOptimisticCreatedReportAction,

src/libs/actions/Policy/Policy.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ import * as PhoneNumber from '@libs/PhoneNumber';
7777
import * as PolicyUtils from '@libs/PolicyUtils';
7878
import {goBackWhenEnableFeature, navigateToExpensifyCardPage} from '@libs/PolicyUtils';
7979
import * as ReportUtils from '@libs/ReportUtils';
80+
import type {OptimisticChatReport} from '@libs/ReportUtils';
8081
import type {PolicySelector} from '@pages/home/sidebar/FloatingActionButtonAndPopover';
8182
import * as PaymentMethods from '@userActions/PaymentMethods';
8283
import * as PersistedRequests from '@userActions/PersistedRequests';
@@ -111,6 +112,7 @@ type ReportCreationData = Record<
111112
{
112113
reportID: string;
113114
reportActionID?: string;
115+
report: OptimisticChatReport;
114116
}
115117
>;
116118

@@ -1070,6 +1072,7 @@ function createPolicyExpenseChats(policyID: string, invitedEmailsToAccountIDs: I
10701072
// If the chat already exists, we don't want to create a new one - just make sure it's not archived
10711073
if (oldChat) {
10721074
workspaceMembersChats.reportCreationData[login] = {
1075+
report: oldChat,
10731076
reportID: oldChat.reportID,
10741077
};
10751078
workspaceMembersChats.onyxOptimisticData.push({
@@ -1119,6 +1122,7 @@ function createPolicyExpenseChats(policyID: string, invitedEmailsToAccountIDs: I
11191122
const optimisticCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(login);
11201123

11211124
workspaceMembersChats.reportCreationData[login] = {
1125+
report: optimisticReport,
11221126
reportID: optimisticReport.reportID,
11231127
reportActionID: optimisticCreatedAction.reportActionID,
11241128
};

0 commit comments

Comments
 (0)