Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QBO Export] Implement offline pattern B for manual report exports #44733

Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7ad0af4
feat: add offline pattern B for manual report exports
kosmydel Jul 2, 2024
8d1f659
Merge branch '@kosmydel/qbo-export/manual-exports' into @kosmydel/qbo…
kosmydel Jul 3, 2024
deba812
Merge branch '@kosmydel/qbo-export/manual-exports' into @kosmydel/qbo…
kosmydel Jul 4, 2024
533097a
cleanup
kosmydel Jul 4, 2024
5f448b8
Merge branch '@kosmydel/qbo-export/manual-exports' into @kosmydel/qbo…
kosmydel Jul 4, 2024
1425235
fix: rbr
kosmydel Jul 4, 2024
652ce36
feat: add in progress status
kosmydel Jul 5, 2024
5f85e77
Merge branch '@kosmydel/qbo-export/manual-exports' into @kosmydel/qbo…
kosmydel Jul 5, 2024
a2a1c41
Merge branch '@kosmydel/qbo-export/manual-exports' into @kosmydel/qbo…
kosmydel Jul 5, 2024
afcee83
Merge branch '@kosmydel/qbo-export/manual-exports' into @kosmydel/qbo…
kosmydel Jul 5, 2024
42b923c
fix lang
kosmydel Jul 5, 2024
eefd928
Merge branch '@kosmydel/qbo-export/manual-exports' into @kosmydel/qbo…
kosmydel Jul 9, 2024
98c7443
fixes
kosmydel Jul 9, 2024
1d2e00c
Merge branch '@kosmydel/qbo-export/manual-exports' into @kosmydel/qbo…
kosmydel Jul 16, 2024
972f910
cleanup
kosmydel Jul 16, 2024
37a7735
add DeepL translations
kosmydel Jul 16, 2024
8cbf964
Merge branch 'main' into @kosmydel/qbo-export/manual-exports-offline-…
kosmydel Jul 16, 2024
d214d75
Merge branch 'main' into @kosmydel/qbo-export/manual-exports-offline-…
kosmydel Jul 17, 2024
fdcd827
fix: displaying friendly name
kosmydel Jul 17, 2024
a029907
address review
kosmydel Jul 19, 2024
59d7c77
Merge branch 'main' into @kosmydel/qbo-export/manual-exports-offline-…
kosmydel Jul 19, 2024
9af61d1
address review
kosmydel Jul 19, 2024
fce269d
Merge branch 'refs/heads/main' into @kosmydel/qbo-export/manual-expor…
war-in Jul 25, 2024
d024caf
adjust API calls parameters
war-in Jul 25, 2024
dd8d4bd
fix params
war-in Jul 25, 2024
805ae17
fix wrong export integration button conditions
war-in Jul 25, 2024
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
4 changes: 2 additions & 2 deletions src/components/ReportActionItem/ExportWithDropdownMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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]);

Expand Down Expand Up @@ -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)}
Expand Down
4 changes: 2 additions & 2 deletions src/components/ReportActionItem/ReportPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,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.hasActionsWithErrors(iouReportID);
Expand Down Expand Up @@ -286,7 +286,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:
Expand Down
8 changes: 7 additions & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3607,7 +3607,13 @@ export default {
changeType: ({oldType, newType}: ChangeTypeParams) => `changed type from ${oldType} to ${newType}`,
delegateSubmit: ({delegateUser, originalManager}: DelegateSubmitParams) => `sent this report to ${delegateUser} since ${originalManager} is on vacation`,
exportedToCSV: `exported this report to CSV`,
exportedToIntegration: ({label}: ExportedToIntegrationParams) => `exported this report to ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[label] ?? label}`,
exportedToIntegration: ({label, markedManually}: ExportedToIntegrationParams) => {
if (markedManually) {
return `You marked this report as manually exported to ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[label] ?? label}`;
}
return `exported this report to ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[label] ?? label}`;
},
exportInProgress: ({label}: ExportedToIntegrationParams) => `started exporting this report to ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[label] ?? label}...`,
forwarded: ({amount, currency}: ForwardedParams) => `approved ${currency}${amount}`,
integrationsMessage: (errorMessage: string, label: string) => `failed to export this report to ${label} ("${errorMessage}").`,
managerAttachReceipt: `added a receipt`,
Expand Down
8 changes: 7 additions & 1 deletion src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3662,7 +3662,13 @@ export default {
changeType: ({oldType, newType}: ChangeTypeParams) => `cambió type de ${oldType} a ${newType}`,
delegateSubmit: ({delegateUser, originalManager}: DelegateSubmitParams) => `envié este informe a ${delegateUser} ya que ${originalManager} está de vacaciones`,
exportedToCSV: `exportó este informe a CSV`,
exportedToIntegration: ({label}: ExportedToIntegrationParams) => `exportó este informe a ${label}`,
exportedToIntegration: ({label, markedManually}: ExportedToIntegrationParams) => {
if (markedManually) {
return `Ha marcado este informe como exportado manualmente a ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[label] ?? label}`;
}
return `exportó este informe a ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[label] ?? label}`;
},
exportInProgress: ({label}: ExportedToIntegrationParams) => `comenzado a exportar este informe a ${CONST.POLICY.CONNECTIONS.NAME_USER_FRIENDLY[label] ?? label}...`,
forwarded: ({amount, currency}: ForwardedParams) => `aprobado ${currency}${amount}`,
integrationsMessage: (errorMessage: string, label: string) => `no se pudo exportar este informe a ${label} ("${errorMessage}").`,
managerAttachReceipt: `agregó un recibo`,
Expand Down
2 changes: 1 addition & 1 deletion src/languages/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ type ChangeTypeParams = {oldType: string; newType: string};

type DelegateSubmitParams = {delegateUser: string; originalManager: string};

type ExportedToIntegrationParams = {label: ConnectionName};
type ExportedToIntegrationParams = {label: ConnectionName; markedManually?: boolean; inProgress?: boolean; lastModified?: string};

type ForwardedParams = {amount: string; currency: string};

Expand Down
1 change: 1 addition & 0 deletions src/libs/API/parameters/MarkAsExportedParams.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
type MarkAsExportedParams = {
reportIDList: string;
markedManually: true;
optimisticReportActionID: string;
};

export default MarkAsExportedParams;
1 change: 1 addition & 0 deletions src/libs/API/parameters/ReportExportParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type ReportExportParams = {
reportIDList: string;
connectionName: ValueOf<typeof CONST.POLICY.CONNECTIONS.NAME>;
type: 'MANUAL';
optimisticReportActionID: string;
};

export default ReportExportParams;
8 changes: 6 additions & 2 deletions src/libs/ReportActionsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1220,8 +1220,12 @@ function getMessageOfOldDotReportAction(oldDotAction: PartialReportAction | OldD
}
case CONST.REPORT.ACTIONS.TYPE.EXPORTED_TO_CSV:
return Localize.translateLocal('report.actions.type.exportedToCSV');
case CONST.REPORT.ACTIONS.TYPE.EXPORTED_TO_INTEGRATION:
return Localize.translateLocal('report.actions.type.exportedToIntegration', {label: originalMessage.label});
case CONST.REPORT.ACTIONS.TYPE.EXPORTED_TO_INTEGRATION: {
if (originalMessage.inProgress && !originalMessage.markedManually) {
return Localize.translateLocal('report.actions.type.exportInProgress', {label: originalMessage.label});
}
return Localize.translateLocal('report.actions.type.exportedToIntegration', {label: originalMessage.label, markedManually: originalMessage.markedManually});
}
case CONST.REPORT.ACTIONS.TYPE.INTEGRATIONS_MESSAGE: {
const {result, label} = originalMessage;
const errorMessage = result?.messages?.join(', ') ?? '';
Expand Down
35 changes: 35 additions & 0 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,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';
Expand Down Expand Up @@ -283,6 +284,12 @@ type OptimisticChatReport = Pick<
isOptimisticReport: true;
};

type OptimisticExportAction = OriginalMessageExportedToIntegration &
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
type OptimisticExportAction = OriginalMessageExportedToIntegration &
type OptimisticExportIntegrationAction = OriginalMessageExportedToIntegration &

Pick<
ReportAction<typeof CONST.REPORT.ACTIONS.TYPE.EXPORTED_TO_INTEGRATION>,
'reportActionID' | 'actorAccountID' | 'avatar' | 'created' | 'lastModified' | 'message' | 'person' | 'shouldShow' | 'pendingAction' | 'errors' | 'automatic'
>;

type OptimisticTaskReportAction = Pick<
ReportAction,
| 'reportActionID'
Expand Down Expand Up @@ -5131,6 +5138,33 @@ function buildOptimisticTaskReport(
};
}

/**
* Builds an optimistic EXPORTED_TO_INTEGRATION report action
*
* @param label - The connectionName of the integration
* @param markedManually - Whether the integration was marked as manually exported
*/
function buildOptimisticExportIntegrationAction(label: ConnectionName, markedManually = false): OptimisticExportAction {
return {
reportActionID: NumberUtils.rand64(),
actionName: CONST.REPORT.ACTIONS.TYPE.EXPORTED_TO_INTEGRATION,
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
actorAccountID: currentUserAccountID,
message: [],
person: [],
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
person: [],
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
*
Expand Down Expand Up @@ -7403,6 +7437,7 @@ export {
isAdminOwnerApproverOrReportOwner,
createDraftWorkspaceAndNavigateToConfirmationScreen,
isChatUsedForOnboarding,
buildOptimisticExportIntegrationAction,
getChatUsedForOnboarding,
findPolicyExpenseChatByPolicyID,
getIntegrationIcon,
Expand Down
87 changes: 82 additions & 5 deletions src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ import type {
InviteToGroupChatParams,
InviteToRoomParams,
LeaveRoomParams,
MarkAsExportedParams,
MarkAsUnreadParams,
OpenReportParams,
OpenRoomMembersPageParams,
ReadNewestActionParams,
RemoveEmojiReactionParams,
RemoveFromGroupChatParams,
RemoveFromRoomParams,
ReportExportParams,
ResolveActionableMentionWhisperParams,
ResolveActionableReportMentionWhisperParams,
SearchForReportsParams,
Expand Down Expand Up @@ -3779,18 +3781,93 @@ function setGroupDraft(newGroupDraft: Partial<NewGroupChatDraft>) {
}

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 successData: OnyxUpdate[] = [
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's not use any successData here. We'll let the backend clear the pendingAction when the export is finally done.

{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
value: {
[optimisticReportActionID]: null,
},
},
];
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',
});
optimisticReportActionID,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
optimisticReportActionID,
optimisticReportActions: JSON.stringify({
[reportID]: optimisticReportActionID
}),

We're refactoring this a bit. Instead of just passing optimisticReportActionID we'll pass optimisticReportActions that maps reportID => optimisticReportActionID

} satisfies ReportExportParams;

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

function markAsManuallyExported(reportID: string) {
API.write(WRITE_COMMANDS.MARK_AS_EXPORTED, {
function markAsManuallyExported(reportID: string, connectionName: ConnectionName) {
const action = ReportUtils.buildOptimisticExportIntegrationAction(connectionName, true);
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]: null,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
[optimisticReportActionID]: null,
[optimisticReportActionID]: {
pendingAction: null
},

Don't delete the optimistic action here. Just clear the pendingAction

},
},
];

const failureData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
value: {
[optimisticReportActionID]: {
errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
},
},
},
];

const params = {
reportIDList: reportID,
markedManually: true,
});
optimisticReportActionID,
arosiclair marked this conversation as resolved.
Show resolved Hide resolved
} satisfies MarkAsExportedParams;
Copy link
Contributor

Choose a reason for hiding this comment

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

We also need to change the shape of MarkAsExported params:

const params = {
    markedManually: true,
    data: JSON.stringify([
        {
            reportID: Number(reportID),
            label,
            optimisticReportActionID
        }
    ])
} satisfies MarkAsExportedParams;

data should be a stringified array of the export data we need


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

export {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/home/report/ReportDetailsExportPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Loading