Skip to content
4 changes: 4 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,9 @@ const ONYXKEYS = {
/** Details on whether an account is locked or not */
NVP_PRIVATE_LOCK_ACCOUNT_DETAILS: 'nvp_private_lockAccountDetails',

/** The NVP containing the user's custom IS templates */
NVP_INTEGRATION_SERVER_EXPORT_TEMPLATES: 'nvp_expensify_integrationServerExportTemplates',

/** Plaid data (access tokens, bank accounts ...) */
PLAID_DATA: 'plaidData',

Expand Down Expand Up @@ -1213,6 +1216,7 @@ type OnyxValuesMapping = {
[ONYXKEYS.NVP_LAST_IPHONE_LOGIN]: string;
[ONYXKEYS.NVP_LAST_ANDROID_LOGIN]: string;
[ONYXKEYS.TRANSACTION_THREAD_NAVIGATION_REPORT_IDS]: string[];
[ONYXKEYS.NVP_INTEGRATION_SERVER_EXPORT_TEMPLATES]: OnyxTypes.IntegrationServerExportTemplate[];
[ONYXKEYS.ONBOARDING_USER_REPORTED_INTEGRATION]: OnboardingAccounting;
[ONYXKEYS.HYBRID_APP]: OnyxTypes.HybridApp;
};
Expand Down
30 changes: 22 additions & 8 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ function MoneyReportHeader({
const [download] = useOnyx(`${ONYXKEYS.COLLECTION.DOWNLOAD}${reportPDFFilename}`, {canBeMissing: true});
const isDownloadingPDF = download?.isDownloading ?? false;
const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false});
const [integrationsExportTemplates] = useOnyx(ONYXKEYS.NVP_INTEGRATION_SERVER_EXPORT_TEMPLATES, {canBeMissing: true});
const requestParentReportAction = useMemo(() => {
if (!reportActions || !transactionThreadReport?.parentReportActionID) {
return null;
Expand Down Expand Up @@ -549,8 +550,8 @@ function MoneyReportHeader({

const [offlineModalVisible, setOfflineModalVisible] = useState(false);

const exportSubMenuOptions: Record<ValueOf<typeof CONST.REPORT.EXPORT_OPTIONS>, DropdownOption<ValueOf<typeof CONST.REPORT.EXPORT_OPTIONS>>> = useMemo(
() => ({
const exportSubmenuOptions: Record<string, DropdownOption<string>> = useMemo(() => {
const options: Record<string, DropdownOption<string>> = {
[CONST.REPORT.EXPORT_OPTIONS.DOWNLOAD_CSV]: {
text: translate('export.basicExport'),
icon: Expensicons.Table,
Expand Down Expand Up @@ -611,9 +612,22 @@ function MoneyReportHeader({
markAsManuallyExported(moneyRequestReport?.reportID, connectedIntegration);
},
},
}),
[translate, connectedIntegrationFallback, connectedIntegration, moneyRequestReport, isOffline, transactionIDs, isExported, beginExportWithTemplate],
);
};

// If the user has any custom integration export templates, add them as export options
if (integrationsExportTemplates && integrationsExportTemplates.length > 0) {
for (const template of integrationsExportTemplates) {
options[template.name] = {
text: template.name,
icon: Expensicons.Table,
value: template.name,
onSelected: () => beginExportWithTemplate(template.name, CONST.EXPORT_TEMPLATE_TYPES.INTEGRATIONS, transactionIDs),
};
}
}

return options;
}, [translate, connectedIntegrationFallback, connectedIntegration, moneyRequestReport, isOffline, transactionIDs, isExported, beginExportWithTemplate, integrationsExportTemplates]);

const primaryActionsImplementation = {
[CONST.REPORT.PRIMARY_ACTIONS.SUBMIT]: (
Expand Down Expand Up @@ -759,8 +773,8 @@ function MoneyReportHeader({
if (!moneyRequestReport) {
return [];
}
return getSecondaryExportReportActions(moneyRequestReport, policy, reportActions);
}, [moneyRequestReport, policy, reportActions]);
return getSecondaryExportReportActions(moneyRequestReport, policy, reportActions, integrationsExportTemplates ?? []);
}, [moneyRequestReport, policy, reportActions, integrationsExportTemplates]);

const secondaryActionsImplementation: Record<
ValueOf<typeof CONST.REPORT.SECONDARY_ACTIONS>,
Expand All @@ -779,7 +793,7 @@ function MoneyReportHeader({
text: translate('common.export'),
backButtonText: translate('common.export'),
icon: Expensicons.Export,
subMenuItems: secondaryExportActions.map((action) => exportSubMenuOptions[action]),
subMenuItems: secondaryExportActions.map((action) => exportSubmenuOptions[action as string]),
},
[CONST.REPORT.SECONDARY_ACTIONS.DOWNLOAD_PDF]: {
value: CONST.REPORT.SECONDARY_ACTIONS.DOWNLOAD_PDF,
Expand Down
13 changes: 13 additions & 0 deletions src/hooks/useSelectedTransactionsActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ function useSelectedTransactionsActions({
}) {
const {selectedTransactionIDs, clearSelectedTransactions} = useSearchContext();
const [allTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {canBeMissing: false});
const [integrationsExportTemplates] = useOnyx(ONYXKEYS.NVP_INTEGRATION_SERVER_EXPORT_TEMPLATES, {canBeMissing: true});
const {duplicateTransactions, duplicateTransactionViolations} = useDuplicateTransactionsAndViolations(selectedTransactionIDs);
const isReportArchived = useReportIsArchived(report?.reportID);
const selectedTransactions = useMemo(
Expand Down Expand Up @@ -210,6 +211,17 @@ function useSelectedTransactionsActions({
});
}

// If the user has any custom integration export templates, add them as export options
if (integrationsExportTemplates && integrationsExportTemplates.length > 0) {
for (const template of integrationsExportTemplates) {
exportOptions.push({
text: template.name,
icon: Expensicons.Table,
onSelected: () => beginExportWithTemplate(template.name, CONST.EXPORT_TEMPLATE_TYPES.INTEGRATIONS, selectedTransactionIDs),
});
}
}

return exportOptions;
};

Expand Down Expand Up @@ -279,6 +291,7 @@ function useSelectedTransactionsActions({
session?.accountID,
showDeleteModal,
beginExportWithTemplate,
integrationsExportTemplates,
]);

return {
Expand Down
18 changes: 14 additions & 4 deletions src/libs/ReportSecondaryActionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Policy, Report, ReportAction, ReportNameValuePairs, Transaction, TransactionViolation} from '@src/types/onyx';
import type {IntegrationServerExportTemplate, Policy, Report, ReportAction, ReportNameValuePairs, Transaction, TransactionViolation} from '@src/types/onyx';
import {isApprover as isApproverUtils} from './actions/Policy/Member';
import {getCurrentUserAccountID, getCurrentUserEmail} from './actions/Report';
import {
Expand Down Expand Up @@ -646,9 +646,13 @@ function getSecondaryReportActions({
return options;
}

function getSecondaryExportReportActions(report: Report, policy?: Policy, reportActions?: ReportAction[]): Array<ValueOf<typeof CONST.REPORT.EXPORT_OPTIONS>> {
const options: Array<ValueOf<typeof CONST.REPORT.EXPORT_OPTIONS>> = [];

function getSecondaryExportReportActions(
report: Report,
policy?: Policy,
reportActions?: ReportAction[],
integrationsExportTemplates?: IntegrationServerExportTemplate[],
): Array<ValueOf<string>> {
const options: Array<ValueOf<string>> = [];
if (isExportAction(report, policy, reportActions)) {
options.push(CONST.REPORT.EXPORT_OPTIONS.EXPORT_TO_INTEGRATION);
}
Expand All @@ -659,6 +663,12 @@ function getSecondaryExportReportActions(report: Report, policy?: Policy, report

options.push(CONST.REPORT.EXPORT_OPTIONS.DOWNLOAD_CSV, CONST.REPORT.EXPORT_OPTIONS.EXPENSE_LEVEL_EXPORT, CONST.REPORT.EXPORT_OPTIONS.REPORT_LEVEL_EXPORT);

if (integrationsExportTemplates && integrationsExportTemplates.length > 0) {
for (const template of integrationsExportTemplates) {
options.push(template.name);
}
}

return options;
}

Expand Down
17 changes: 16 additions & 1 deletion src/pages/Search/SearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ function SearchPage({route}: SearchPageProps) {
const [newParentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${newReport?.parentReportID}`, {canBeMissing: true});
const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: false});
const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, {canBeMissing: true});

const [integrationsExportTemplates] = useOnyx(ONYXKEYS.NVP_INTEGRATION_SERVER_EXPORT_TEMPLATES, {canBeMissing: true});
const [isOfflineModalVisible, setIsOfflineModalVisible] = useState(false);
const [isDownloadErrorModalVisible, setIsDownloadErrorModalVisible] = useState(false);
const [isDeleteExpensesConfirmModalVisible, setIsDeleteExpensesConfirmModalVisible] = useState(false);
Expand Down Expand Up @@ -198,6 +198,20 @@ function SearchPage({route}: SearchPageProps) {
});
}

// If the user has any custom integration export templates, add them as export options
if (integrationsExportTemplates && integrationsExportTemplates.length > 0) {
for (const template of integrationsExportTemplates) {
exportOptions.push({
text: template.name,
icon: Expensicons.Table,
onSelected: () => {
// Custom IS templates are not policy specific, so we don't need to pass a policyID
beginExportWithTemplate(template.name, CONST.EXPORT_TEMPLATE_TYPES.INTEGRATIONS, undefined);
},
});
}
}

return exportOptions;
};

Expand Down Expand Up @@ -433,6 +447,7 @@ function SearchPage({route}: SearchPageProps) {
styles.fontWeightNormal,
styles.textWrap,
beginExportWithTemplate,
integrationsExportTemplates,
]);

const handleDeleteExpenses = () => {
Expand Down
9 changes: 9 additions & 0 deletions src/types/onyx/IntegrationServerExportTemplate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type * as OnyxCommon from './OnyxCommon';

/** Information about integration server export templates */
type IntegrationServerExportTemplate = OnyxCommon.OnyxValueWithOfflineFeedback<{
/** Name of the template */
name: string;
}>;

export default IntegrationServerExportTemplate;
2 changes: 2 additions & 0 deletions src/types/onyx/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import type {FundList} from './Fund';
import type Fund from './Fund';
import type HybridApp from './HybridApp';
import type ImportedSpreadsheet from './ImportedSpreadsheet';
import type IntegrationServerExportTemplate from './IntegrationServerExportTemplate';
import type IntroSelected from './IntroSelected';
import type InvitedEmailsToAccountIDs from './InvitedEmailsToAccountIDs';
import type JoinablePolicies from './JoinablePolicies';
Expand Down Expand Up @@ -265,5 +266,6 @@ export type {
ValidateUserAndGetAccessiblePolicies,
VacationDelegate,
BillingReceiptDetails,
IntegrationServerExportTemplate,
HybridApp,
};
Loading