diff --git a/src/components/ReportActionItem/ExportWithDropdownMenu.tsx b/src/components/ReportActionItem/ExportWithDropdownMenu.tsx
index a13c0a266689..87ddb3b42bf0 100644
--- a/src/components/ReportActionItem/ExportWithDropdownMenu.tsx
+++ b/src/components/ReportActionItem/ExportWithDropdownMenu.tsx
@@ -74,7 +74,7 @@ function ExportWithDropdownMenu({policy, report, connectionName}: ExportWithDrop
if (modalStatus === CONST.REPORT.EXPORT_OPTIONS.EXPORT_TO_INTEGRATION) {
ReportActions.exportToIntegration(reportID, connectionName);
} else if (modalStatus === CONST.REPORT.EXPORT_OPTIONS.MARK_AS_EXPORTED) {
- ReportActions.markAsManuallyExported(reportID);
+ ReportActions.markAsManuallyExported(reportID, connectionName);
}
}, [connectionName, modalStatus, reportID]);
@@ -106,7 +106,7 @@ function ExportWithDropdownMenu({policy, report, connectionName}: ExportWithDrop
if (value === CONST.REPORT.EXPORT_OPTIONS.EXPORT_TO_INTEGRATION) {
ReportActions.exportToIntegration(reportID, connectionName);
} else if (value === CONST.REPORT.EXPORT_OPTIONS.MARK_AS_EXPORTED) {
- ReportActions.markAsManuallyExported(reportID);
+ ReportActions.markAsManuallyExported(reportID, connectionName);
}
}}
onOptionSelected={({value}) => savePreferredExportMethod(value)}
diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx
index 7a527610422b..68f5bca3d764 100644
--- a/src/components/ReportActionItem/ReportPreview.tsx
+++ b/src/components/ReportActionItem/ReportPreview.tsx
@@ -152,7 +152,7 @@ function ReportPreview({
const hasReceipts = transactionsWithReceipts.length > 0;
const isScanning = hasReceipts && areAllRequestsBeingSmartScanned;
const hasErrors =
- hasMissingSmartscanFields ||
+ (hasMissingSmartscanFields && !iouSettled) ||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
(canUseViolations && (ReportUtils.hasViolations(iouReportID, transactionViolations) || ReportUtils.hasWarningTypeViolations(iouReportID, transactionViolations))) ||
(ReportUtils.isReportOwner(iouReport) && ReportUtils.hasReportViolations(iouReportID)) ||
@@ -285,7 +285,7 @@ function ReportPreview({
const shouldShowSettlementButton = (shouldShowPayButton || shouldShowApproveButton) && !showRTERViolationMessage;
const shouldPromptUserToAddBankAccount = ReportUtils.hasMissingPaymentMethod(userWallet, iouReportID);
- const shouldShowRBR = !iouSettled && hasErrors;
+ const shouldShowRBR = hasErrors;
/*
Show subtitle if at least one of the expenses is not being smart scanned, and either:
@@ -425,7 +425,7 @@ function ReportPreview({
)}
- {shouldShowSettlementButton && !shouldShowExportIntegrationButton && (
+ {shouldShowSettlementButton && (
)}
- {shouldShowExportIntegrationButton && (
+ {shouldShowExportIntegrationButton && !shouldShowSettlementButton && (
+ */
+ data: string;
};
export default MarkAsExportedParams;
diff --git a/src/libs/API/parameters/ReportExportParams.ts b/src/libs/API/parameters/ReportExportParams.ts
index dc87ce2170c4..c6a4b7b58ee8 100644
--- a/src/libs/API/parameters/ReportExportParams.ts
+++ b/src/libs/API/parameters/ReportExportParams.ts
@@ -5,6 +5,13 @@ type ReportExportParams = {
reportIDList: string;
connectionName: ValueOf;
type: 'MANUAL';
+ /**
+ * Stringified JSON object with type of following structure:
+ * {
+ * [reportID]: optimisticReportActionID;
+ * }>
+ */
+ optimisticReportActions: string;
};
export default ReportExportParams;
diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index 9194ce15ef42..d56feca5ca0e 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -43,6 +43,7 @@ import type {
UserWallet,
} from '@src/types/onyx';
import type {Participant} from '@src/types/onyx/IOU';
+import type {OriginalMessageExportedToIntegration} from '@src/types/onyx/OldDotAction';
import type Onboarding from '@src/types/onyx/Onboarding';
import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon';
import type {OriginalMessageChangeLog, PaymentMethodType} from '@src/types/onyx/OriginalMessage';
@@ -287,6 +288,12 @@ type OptimisticChatReport = Pick<
isOptimisticReport: true;
};
+type OptimisticExportIntegrationAction = OriginalMessageExportedToIntegration &
+ Pick<
+ ReportAction,
+ 'reportActionID' | 'actorAccountID' | 'avatar' | 'created' | 'lastModified' | 'message' | 'person' | 'shouldShow' | 'pendingAction' | 'errors' | 'automatic'
+ >;
+
type OptimisticTaskReportAction = Pick<
ReportAction,
| 'reportActionID'
@@ -5184,6 +5191,40 @@ function buildOptimisticTaskReport(
};
}
+/**
+ * Builds an optimistic EXPORTED_TO_INTEGRATION report action
+ *
+ * @param integration - The connectionName of the integration
+ * @param markedManually - Whether the integration was marked as manually exported
+ */
+function buildOptimisticExportIntegrationAction(integration: ConnectionName, markedManually = false): OptimisticExportIntegrationAction {
+ const label = CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[integration];
+ return {
+ reportActionID: NumberUtils.rand64(),
+ actionName: CONST.REPORT.ACTIONS.TYPE.EXPORTED_TO_INTEGRATION,
+ pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
+ actorAccountID: currentUserAccountID,
+ message: [],
+ person: [
+ {
+ type: CONST.REPORT.MESSAGE.TYPE.TEXT,
+ style: 'strong',
+ text: getCurrentUserDisplayNameOrEmail(),
+ },
+ ],
+ automatic: false,
+ avatar: getCurrentUserAvatar(),
+ created: DateUtils.getDBTime(),
+ shouldShow: true,
+ originalMessage: {
+ label,
+ lastModified: DateUtils.getDBTime(),
+ markedManually,
+ inProgress: true,
+ },
+ };
+}
+
/**
* A helper method to create transaction thread
*
@@ -7528,6 +7569,7 @@ export {
isAdminOwnerApproverOrReportOwner,
createDraftWorkspaceAndNavigateToConfirmationScreen,
isChatUsedForOnboarding,
+ buildOptimisticExportIntegrationAction,
getChatUsedForOnboarding,
getFieldViolationTranslation,
getFieldViolation,
diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts
index b4d1360b2d94..9cb92ebd7c8a 100644
--- a/src/libs/actions/Report.ts
+++ b/src/libs/actions/Report.ts
@@ -25,6 +25,7 @@ import type {
InviteToGroupChatParams,
InviteToRoomParams,
LeaveRoomParams,
+ MarkAsExportedParams,
MarkAsUnreadParams,
OpenReportParams,
OpenRoomMembersPageParams,
@@ -32,6 +33,7 @@ import type {
RemoveEmojiReactionParams,
RemoveFromGroupChatParams,
RemoveFromRoomParams,
+ ReportExportParams,
ResolveActionableMentionWhisperParams,
ResolveActionableReportMentionWhisperParams,
SearchForReportsParams,
@@ -3835,18 +3837,94 @@ function setGroupDraft(newGroupDraft: Partial) {
}
function exportToIntegration(reportID: string, connectionName: ConnectionName) {
- API.write(WRITE_COMMANDS.REPORT_EXPORT, {
+ const action = ReportUtils.buildOptimisticExportIntegrationAction(connectionName);
+ const optimisticReportActionID = action.reportActionID;
+
+ const optimisticData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
+ value: {
+ [optimisticReportActionID]: action,
+ },
+ },
+ ];
+
+ const failureData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
+ value: {
+ [optimisticReportActionID]: {
+ errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
+ },
+ },
+ },
+ ];
+
+ const params = {
reportIDList: reportID,
connectionName,
type: 'MANUAL',
- });
+ optimisticReportActions: JSON.stringify({
+ [reportID]: optimisticReportActionID,
+ }),
+ } satisfies ReportExportParams;
+
+ API.write(WRITE_COMMANDS.REPORT_EXPORT, params, {optimisticData, failureData});
}
-function markAsManuallyExported(reportID: string) {
- API.write(WRITE_COMMANDS.MARK_AS_EXPORTED, {
- reportIDList: reportID,
+function markAsManuallyExported(reportID: string, connectionName: ConnectionName) {
+ const action = ReportUtils.buildOptimisticExportIntegrationAction(connectionName, true);
+ const label = CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[connectionName];
+ const optimisticReportActionID = action.reportActionID;
+
+ const optimisticData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
+ value: {
+ [optimisticReportActionID]: action,
+ },
+ },
+ ];
+
+ const successData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
+ value: {
+ [optimisticReportActionID]: {
+ pendingAction: null,
+ },
+ },
+ },
+ ];
+
+ const failureData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
+ value: {
+ [optimisticReportActionID]: {
+ errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
+ },
+ },
+ },
+ ];
+
+ const params = {
markedManually: true,
- });
+ data: JSON.stringify([
+ {
+ reportID,
+ label,
+ optimisticReportActionID,
+ },
+ ]),
+ } satisfies MarkAsExportedParams;
+
+ API.write(WRITE_COMMANDS.MARK_AS_EXPORTED, params, {optimisticData, successData, failureData});
}
export {
diff --git a/src/pages/home/report/ReportDetailsExportPage.tsx b/src/pages/home/report/ReportDetailsExportPage.tsx
index 99b7305cc7a9..6f16786682af 100644
--- a/src/pages/home/report/ReportDetailsExportPage.tsx
+++ b/src/pages/home/report/ReportDetailsExportPage.tsx
@@ -45,7 +45,7 @@ function ReportDetailsExportPage({route}: ReportDetailsExportPageProps) {
if (type === CONST.REPORT.EXPORT_OPTIONS.EXPORT_TO_INTEGRATION) {
ReportActions.exportToIntegration(reportID, connectionName);
} else if (type === CONST.REPORT.EXPORT_OPTIONS.MARK_AS_EXPORTED) {
- ReportActions.markAsManuallyExported(reportID);
+ ReportActions.markAsManuallyExported(reportID, connectionName);
}
setModalStatus(null);
Navigation.dismissModal();