From c655ff0a193f46b95a12ed0a701c5fd4c4835127 Mon Sep 17 00:00:00 2001 From: Hans Date: Sun, 2 Feb 2025 00:54:53 +0700 Subject: [PATCH 01/16] migrate to use new private_lastPaymentMethod --- src/CONST.ts | 6 ++++++ src/components/SettlementButton/index.tsx | 10 +++++++++- src/libs/actions/IOU.ts | 4 ++-- src/libs/actions/Search.ts | 10 ++++++++-- src/types/onyx/LastPaymentMethod.ts | 18 ++++++++++++++++-- src/types/onyx/index.ts | 3 ++- 6 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 1907af4c61095..f20d8ac89af73 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -6549,6 +6549,12 @@ const CONST = { ERROR_PERMISSION_DENIED: 'permissionDenied', }, }, + LAST_PAYMENT_METHOD: { + DEFAULT: 'DEFAULT', + IOU: 'IOU', + EXPENSE: 'EXPENSE', + INVOICE: 'INVOICE', + }, } as const; type Country = keyof typeof CONST.ALL_COUNTRIES; diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index 9c4294419c5e2..7b0208e1a2f43 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -16,6 +16,7 @@ import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {LastPaymentMethodType} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; @@ -70,7 +71,14 @@ function SettlementButton({ const policyEmployeeAccountIDs = policyID ? getPolicyEmployeeAccountIDs(policyID) : []; const reportBelongsToWorkspace = policyID ? ReportUtils.doesReportBelongToWorkspace(chatReport, policyEmployeeAccountIDs, policyID) : false; const policyIDKey = reportBelongsToWorkspace ? policyID : CONST.POLICY.ID_FAKE; - const [lastPaymentMethod = '-1', lastPaymentMethodResult] = useOnyx(ONYXKEYS.NVP_LAST_PAYMENT_METHOD, {selector: (paymentMethod) => paymentMethod?.[policyIDKey]}); + const [lastPaymentMethod, lastPaymentMethodResult] = useOnyx(ONYXKEYS.NVP_LAST_PAYMENT_METHOD, { + selector: (paymentMethod) => { + if (typeof paymentMethod?.[policyIDKey] === 'string') { + return paymentMethod?.[policyIDKey]; + } + return (paymentMethod?.[policyIDKey] as LastPaymentMethodType)?.DEFAULT; + }, + }); const isLoadingLastPaymentMethod = isLoadingOnyxValue(lastPaymentMethodResult); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 0cd321851b243..57222664a326d 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -9230,8 +9230,8 @@ function navigateToStartStepIfScanFileCannotBeRead( } /** Save the preferred payment method for a policy */ -function savePreferredPaymentMethod(policyID: string, paymentMethod: PaymentMethodType) { - Onyx.merge(`${ONYXKEYS.NVP_LAST_PAYMENT_METHOD}`, {[policyID]: paymentMethod}); +function savePreferredPaymentMethod(policyID: string, paymentMethod: PaymentMethodType, type: ValueOf | undefined) { + Onyx.merge(`${ONYXKEYS.NVP_LAST_PAYMENT_METHOD}`, {[policyID]: type ? {[type]: paymentMethod} : paymentMethod}); } /** Get report policy id of IOU request */ diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index 39cb9bc63e34a..ff29f70c7c820 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -19,7 +19,7 @@ import playSound, {SOUNDS} from '@libs/Sound'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import FILTER_KEYS from '@src/types/form/SearchAdvancedFiltersForm'; -import type {LastPaymentMethod, SearchResults} from '@src/types/onyx'; +import type {LastPaymentMethod, LastPaymentMethodType, SearchResults} from '@src/types/onyx'; import type {SearchPolicy, SearchReport, SearchTransaction} from '@src/types/onyx/SearchResults'; import {openReport} from './Report'; @@ -78,7 +78,13 @@ function handleActionButtonPress(hash: number, item: TransactionListItemType | R } function getPayActionCallback(hash: number, item: TransactionListItemType | ReportListItemType, goToItem: () => void) { - const lastPolicyPaymentMethod = item.policyID ? (lastPaymentMethod?.[item.policyID] as ValueOf) : null; + let lastPolicyPaymentMethod = null; + if (item.policyID) { + if (typeof lastPaymentMethod?.[item.policyID] === 'string') { + lastPolicyPaymentMethod = lastPaymentMethod?.[item.policyID]; + } + lastPolicyPaymentMethod = (lastPaymentMethod?.[item.policyID] as LastPaymentMethodType).DEFAULT; + } if (!lastPolicyPaymentMethod) { goToItem(); diff --git a/src/types/onyx/LastPaymentMethod.ts b/src/types/onyx/LastPaymentMethod.ts index ea0c644fc7304..9b2a8dc414482 100644 --- a/src/types/onyx/LastPaymentMethod.ts +++ b/src/types/onyx/LastPaymentMethod.ts @@ -1,4 +1,18 @@ +/** + * The new lastPaymentMethod object + */ +type LastPaymentMethodType = { + /** The default last payment method, this one hold the existing data of the old lastPaymentMethod value which is string */ + DEFAULT: string; + /** The lastPaymentMethod of an IOU */ + IOU: string; + /** The lastPaymentMethod of an Expense */ + EXPENSE: string; + /** The lastPaymentMethod of an Invoice */ + INVOICE: string; +}; + /** Record of last payment methods, indexed by policy id */ -type LastPaymentMethod = Record; +type LastPaymentMethod = Record; -export default LastPaymentMethod; +export type {LastPaymentMethodType, LastPaymentMethod}; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 569504437fb20..f5a9acb5f3ea4 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -35,7 +35,7 @@ import type InvitedEmailsToAccountIDs from './InvitedEmailsToAccountIDs'; import type IOU from './IOU'; import type JoinablePolicies from './JoinablePolicies'; import type LastExportMethod from './LastExportMethod'; -import type LastPaymentMethod from './LastPaymentMethod'; +import type {LastPaymentMethod, LastPaymentMethodType} from './LastPaymentMethod'; import type LastSelectedDistanceRates from './LastSelectedDistanceRates'; import type Locale from './Locale'; import type {LoginList} from './Login'; @@ -250,4 +250,5 @@ export type { JoinablePolicies, DismissedProductTraining, TravelProvisioning, + LastPaymentMethodType, }; From aa5fdaa164cc553b9796581439f47a8d217355b7 Mon Sep 17 00:00:00 2001 From: Hans Date: Sun, 2 Feb 2025 01:04:13 +0700 Subject: [PATCH 02/16] fix: lint --- src/components/SettlementButton/index.tsx | 2 +- src/libs/actions/Search.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index 7b0208e1a2f43..f270b0f8645af 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -225,7 +225,7 @@ function SettlementButton({ }; const savePreferredPaymentMethod = (id: string, value: PaymentMethodType) => { - IOU.savePreferredPaymentMethod(id, value); + IOU.savePreferredPaymentMethod(id, value, undefined); }; return ( diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index ff29f70c7c820..df2edceb32834 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -81,9 +81,9 @@ function getPayActionCallback(hash: number, item: TransactionListItemType | Repo let lastPolicyPaymentMethod = null; if (item.policyID) { if (typeof lastPaymentMethod?.[item.policyID] === 'string') { - lastPolicyPaymentMethod = lastPaymentMethod?.[item.policyID]; + lastPolicyPaymentMethod = lastPaymentMethod?.[item.policyID] as ValueOf; } - lastPolicyPaymentMethod = (lastPaymentMethod?.[item.policyID] as LastPaymentMethodType).DEFAULT; + lastPolicyPaymentMethod = (lastPaymentMethod?.[item.policyID] as LastPaymentMethodType)?.DEFAULT as ValueOf; } if (!lastPolicyPaymentMethod) { From 3362bf90b0c978b299629d3d4346611b10c01169 Mon Sep 17 00:00:00 2001 From: Hans Date: Sun, 2 Feb 2025 01:11:41 +0700 Subject: [PATCH 03/16] correct typo --- src/types/onyx/LastPaymentMethod.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/LastPaymentMethod.ts b/src/types/onyx/LastPaymentMethod.ts index 9b2a8dc414482..fb7038eaceb3e 100644 --- a/src/types/onyx/LastPaymentMethod.ts +++ b/src/types/onyx/LastPaymentMethod.ts @@ -2,7 +2,7 @@ * The new lastPaymentMethod object */ type LastPaymentMethodType = { - /** The default last payment method, this one hold the existing data of the old lastPaymentMethod value which is string */ + /** The default last payment method, this one holds the existing data of the old lastPaymentMethod value which is string */ DEFAULT: string; /** The lastPaymentMethod of an IOU */ IOU: string; From 3a2d2fb38f8c4afd5ee90f9a349388f53ffcd843 Mon Sep 17 00:00:00 2001 From: Hans Date: Mon, 3 Feb 2025 22:45:20 +0700 Subject: [PATCH 04/16] Address review --- src/components/Search/SearchPageHeader.tsx | 13 +++++++++++-- src/libs/actions/Search.ts | 3 ++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index 70398194cc840..9146ab263aee5 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -34,6 +34,7 @@ import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {LastPaymentMethodType} from '@src/types/onyx'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; import {useSearchContext} from './SearchContext'; import SearchPageHeaderInput from './SearchPageHeaderInput'; @@ -143,9 +144,17 @@ function SearchPageHeader({queryJSON}: SearchPageHeaderProps) { !isOffline && !isAnyTransactionOnHold && (selectedReports.length - ? selectedReports.every((report) => report.action === CONST.SEARCH.ACTION_TYPES.PAY && report.policyID && lastPaymentMethods[report.policyID]) + ? selectedReports.every( + (report) => + report.action === CONST.SEARCH.ACTION_TYPES.PAY && + report.policyID && + (!!lastPaymentMethods[report.policyID] || !!(lastPaymentMethods[report.policyID] as LastPaymentMethodType)?.DEFAULT), + ) : selectedTransactionsKeys.every( - (id) => selectedTransactions[id].action === CONST.SEARCH.ACTION_TYPES.PAY && selectedTransactions[id].policyID && lastPaymentMethods[selectedTransactions[id].policyID], + (id) => + selectedTransactions[id].action === CONST.SEARCH.ACTION_TYPES.PAY && + selectedTransactions[id].policyID && + (!!lastPaymentMethods[selectedTransactions[id].policyID] || !!(lastPaymentMethods[selectedTransactions[id].policyID] as LastPaymentMethodType).DEFAULT), )); if (shouldShowPayOption) { diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index df2edceb32834..601acd454e2a1 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -82,8 +82,9 @@ function getPayActionCallback(hash: number, item: TransactionListItemType | Repo if (item.policyID) { if (typeof lastPaymentMethod?.[item.policyID] === 'string') { lastPolicyPaymentMethod = lastPaymentMethod?.[item.policyID] as ValueOf; + } else { + lastPolicyPaymentMethod = (lastPaymentMethod?.[item.policyID] as LastPaymentMethodType)?.DEFAULT as ValueOf; } - lastPolicyPaymentMethod = (lastPaymentMethod?.[item.policyID] as LastPaymentMethodType)?.DEFAULT as ValueOf; } if (!lastPolicyPaymentMethod) { From 85e0796ef3e8f24b63c7cce450254c9ee4d5d739 Mon Sep 17 00:00:00 2001 From: Hans Date: Tue, 4 Feb 2025 23:58:58 +0700 Subject: [PATCH 05/16] add typeof condition --- src/components/Search/SearchPageHeader.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index 9146ab263aee5..840bdc50348ad 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -148,13 +148,13 @@ function SearchPageHeader({queryJSON}: SearchPageHeaderProps) { (report) => report.action === CONST.SEARCH.ACTION_TYPES.PAY && report.policyID && - (!!lastPaymentMethods[report.policyID] || !!(lastPaymentMethods[report.policyID] as LastPaymentMethodType)?.DEFAULT), + ((!!lastPaymentMethods[report.policyID] && typeof lastPaymentMethods[report.policyID] === 'string') || !!(lastPaymentMethods[report.policyID] as LastPaymentMethodType)?.DEFAULT), ) : selectedTransactionsKeys.every( (id) => selectedTransactions[id].action === CONST.SEARCH.ACTION_TYPES.PAY && selectedTransactions[id].policyID && - (!!lastPaymentMethods[selectedTransactions[id].policyID] || !!(lastPaymentMethods[selectedTransactions[id].policyID] as LastPaymentMethodType).DEFAULT), + ((!!lastPaymentMethods[selectedTransactions[id].policyID] && typeof lastPaymentMethods[selectedTransactions[id].policyID] === 'string') || !!(lastPaymentMethods[selectedTransactions[id].policyID] as LastPaymentMethodType).DEFAULT), )); if (shouldShowPayOption) { From bb7e6eee9fad287c63a0d9235cb87d2eb1fc3382 Mon Sep 17 00:00:00 2001 From: Hans Date: Wed, 5 Feb 2025 00:28:14 +0700 Subject: [PATCH 06/16] address linting --- src/components/Search/SearchPageHeader.tsx | 54 +++++++++++----------- src/libs/actions/Search.ts | 15 ++++++ 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index 840bdc50348ad..1663d743402cc 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -1,15 +1,15 @@ -import {useFocusEffect} from '@react-navigation/native'; -import React, {useCallback, useMemo, useState} from 'react'; -import {InteractionManager, View} from 'react-native'; -import {useOnyx} from 'react-native-onyx'; +import { useFocusEffect } from '@react-navigation/native'; +import React, { useCallback, useMemo, useState } from 'react'; +import { InteractionManager, View } from 'react-native'; +import { useOnyx } from 'react-native-onyx'; import Button from '@components/Button'; import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu'; -import type {DropdownOption} from '@components/ButtonWithDropdownMenu/types'; +import type { DropdownOption } from '@components/ButtonWithDropdownMenu/types'; import ConfirmModal from '@components/ConfirmModal'; import DecisionModal from '@components/DecisionModal'; import * as Expensicons from '@components/Icon/Expensicons'; -import {usePersonalDetails} from '@components/OnyxProvider'; -import {useProductTrainingContext} from '@components/ProductTrainingContext'; +import { usePersonalDetails } from '@components/OnyxProvider'; +import { useProductTrainingContext } from '@components/ProductTrainingContext'; import EducationalTooltip from '@components/Tooltip/EducationalTooltip'; import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useLocalize from '@hooks/useLocalize'; @@ -17,28 +17,22 @@ import useNetwork from '@hooks/useNetwork'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import { - approveMoneyRequestOnSearch, - deleteMoneyRequestOnSearch, - exportSearchItemsToCSV, - payMoneyRequestOnSearch, - unholdMoneyRequestOnSearch, - updateAdvancedFilters, -} from '@libs/actions/Search'; -import {mergeCardListWithWorkspaceFeeds} from '@libs/CardUtils'; +import { approveMoneyRequestOnSearch, deleteMoneyRequestOnSearch, exportSearchItemsToCSV, getLastPolicyPaymentMethod, payMoneyRequestOnSearch, unholdMoneyRequestOnSearch, updateAdvancedFilters } from '@libs/actions/Search'; +import { mergeCardListWithWorkspaceFeeds } from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; -import {getAllTaxRates, hasVBBA} from '@libs/PolicyUtils'; -import {buildFilterFormValuesFromQuery, isCannedSearchQuery} from '@libs/SearchQueryUtils'; +import { getAllTaxRates, hasVBBA } from '@libs/PolicyUtils'; +import { buildFilterFormValuesFromQuery, isCannedSearchQuery } from '@libs/SearchQueryUtils'; import SearchSelectedNarrow from '@pages/Search/SearchSelectedNarrow'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {LastPaymentMethodType} from '@src/types/onyx'; +import type { LastPaymentMethodType } from '@src/types/onyx'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; -import {useSearchContext} from './SearchContext'; +import { useSearchContext } from './SearchContext'; import SearchPageHeaderInput from './SearchPageHeaderInput'; -import type {PaymentData, SearchQueryJSON} from './types'; +import type { PaymentData, SearchQueryJSON } from './types'; + type SearchPageHeaderProps = {queryJSON: SearchQueryJSON}; @@ -148,13 +142,15 @@ function SearchPageHeader({queryJSON}: SearchPageHeaderProps) { (report) => report.action === CONST.SEARCH.ACTION_TYPES.PAY && report.policyID && - ((!!lastPaymentMethods[report.policyID] && typeof lastPaymentMethods[report.policyID] === 'string') || !!(lastPaymentMethods[report.policyID] as LastPaymentMethodType)?.DEFAULT), + ((!!lastPaymentMethods[report.policyID] && typeof lastPaymentMethods[report.policyID] === 'string') || + !!(lastPaymentMethods[report.policyID] as LastPaymentMethodType)?.DEFAULT), ) : selectedTransactionsKeys.every( (id) => selectedTransactions[id].action === CONST.SEARCH.ACTION_TYPES.PAY && selectedTransactions[id].policyID && - ((!!lastPaymentMethods[selectedTransactions[id].policyID] && typeof lastPaymentMethods[selectedTransactions[id].policyID] === 'string') || !!(lastPaymentMethods[selectedTransactions[id].policyID] as LastPaymentMethodType).DEFAULT), + ((!!lastPaymentMethods[selectedTransactions[id].policyID] && typeof lastPaymentMethods[selectedTransactions[id].policyID] === 'string') || + !!(lastPaymentMethods[selectedTransactions[id].policyID] as LastPaymentMethodType).DEFAULT), )); if (shouldShowPayOption) { @@ -175,7 +171,7 @@ function SearchPageHeader({queryJSON}: SearchPageHeaderProps) { for (const item of items) { const policyID = item.policyID; - const lastPolicyPaymentMethod = policyID ? lastPaymentMethods?.[policyID] : null; + const lastPolicyPaymentMethod = getLastPolicyPaymentMethod(policyID, lastPaymentMethods); if (!lastPolicyPaymentMethod) { Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: item.reportID, backTo: activeRoute})); @@ -192,11 +188,15 @@ function SearchPageHeader({queryJSON}: SearchPageHeaderProps) { const paymentData = ( selectedReports.length - ? selectedReports.map((report) => ({reportID: report.reportID, amount: report.total, paymentType: lastPaymentMethods[report.policyID]})) + ? selectedReports.map((report) => ({ + reportID: report.reportID, + amount: report.total, + paymentType: getLastPolicyPaymentMethod(report.policyID, lastPaymentMethods), + })) : Object.values(selectedTransactions).map((transaction) => ({ reportID: transaction.reportID, amount: transaction.amount, - paymentType: lastPaymentMethods[transaction.policyID], + paymentType: getLastPolicyPaymentMethod(transaction.policyID, lastPaymentMethods), })) ) as PaymentData[]; @@ -449,4 +449,4 @@ function SearchPageHeader({queryJSON}: SearchPageHeaderProps) { SearchPageHeader.displayName = 'SearchPageHeader'; export type {SearchHeaderOptionValue}; -export default SearchPageHeader; +export default SearchPageHeader; \ No newline at end of file diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index 601acd454e2a1..550fd525f5843 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -77,6 +77,20 @@ function handleActionButtonPress(hash: number, item: TransactionListItemType | R } } +function getLastPolicyPaymentMethod(policyID: string | undefined, lastPaymentMethods: OnyxEntry) { + if (!policyID) { + return null; + } + let lastPolicyPaymentMethod = null; + if (typeof lastPaymentMethods?.[policyID] === 'string') { + lastPolicyPaymentMethod = lastPaymentMethods?.[policyID] as ValueOf; + } else { + lastPolicyPaymentMethod = (lastPaymentMethods?.[policyID] as LastPaymentMethodType)?.DEFAULT as ValueOf; + } + + return lastPolicyPaymentMethod +} + function getPayActionCallback(hash: number, item: TransactionListItemType | ReportListItemType, goToItem: () => void) { let lastPolicyPaymentMethod = null; if (item.policyID) { @@ -428,4 +442,5 @@ export { handleActionButtonPress, submitMoneyRequestOnSearch, openSearchFiltersCardPage, + getLastPolicyPaymentMethod, }; From 42dc21bfaa314bd76906c9e8d7a1586e8fd3b672 Mon Sep 17 00:00:00 2001 From: Hans Date: Wed, 5 Feb 2025 00:34:27 +0700 Subject: [PATCH 07/16] address linting --- src/components/Search/SearchPageHeader.tsx | 39 +++++++++++++--------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index 1663d743402cc..2cfcbc6f00cda 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -1,15 +1,15 @@ -import { useFocusEffect } from '@react-navigation/native'; -import React, { useCallback, useMemo, useState } from 'react'; -import { InteractionManager, View } from 'react-native'; -import { useOnyx } from 'react-native-onyx'; +import {useFocusEffect} from '@react-navigation/native'; +import React, {useCallback, useMemo, useState} from 'react'; +import {InteractionManager, View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu'; -import type { DropdownOption } from '@components/ButtonWithDropdownMenu/types'; +import type {DropdownOption} from '@components/ButtonWithDropdownMenu/types'; import ConfirmModal from '@components/ConfirmModal'; import DecisionModal from '@components/DecisionModal'; import * as Expensicons from '@components/Icon/Expensicons'; -import { usePersonalDetails } from '@components/OnyxProvider'; -import { useProductTrainingContext } from '@components/ProductTrainingContext'; +import {usePersonalDetails} from '@components/OnyxProvider'; +import {useProductTrainingContext} from '@components/ProductTrainingContext'; import EducationalTooltip from '@components/Tooltip/EducationalTooltip'; import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useLocalize from '@hooks/useLocalize'; @@ -17,22 +17,29 @@ import useNetwork from '@hooks/useNetwork'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import { approveMoneyRequestOnSearch, deleteMoneyRequestOnSearch, exportSearchItemsToCSV, getLastPolicyPaymentMethod, payMoneyRequestOnSearch, unholdMoneyRequestOnSearch, updateAdvancedFilters } from '@libs/actions/Search'; -import { mergeCardListWithWorkspaceFeeds } from '@libs/CardUtils'; +import { + approveMoneyRequestOnSearch, + deleteMoneyRequestOnSearch, + exportSearchItemsToCSV, + getLastPolicyPaymentMethod, + payMoneyRequestOnSearch, + unholdMoneyRequestOnSearch, + updateAdvancedFilters, +} from '@libs/actions/Search'; +import {mergeCardListWithWorkspaceFeeds} from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; -import { getAllTaxRates, hasVBBA } from '@libs/PolicyUtils'; -import { buildFilterFormValuesFromQuery, isCannedSearchQuery } from '@libs/SearchQueryUtils'; +import {getAllTaxRates, hasVBBA} from '@libs/PolicyUtils'; +import {buildFilterFormValuesFromQuery, isCannedSearchQuery} from '@libs/SearchQueryUtils'; import SearchSelectedNarrow from '@pages/Search/SearchSelectedNarrow'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type { LastPaymentMethodType } from '@src/types/onyx'; +import type {LastPaymentMethodType} from '@src/types/onyx'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; -import { useSearchContext } from './SearchContext'; +import {useSearchContext} from './SearchContext'; import SearchPageHeaderInput from './SearchPageHeaderInput'; -import type { PaymentData, SearchQueryJSON } from './types'; - +import type {PaymentData, SearchQueryJSON} from './types'; type SearchPageHeaderProps = {queryJSON: SearchQueryJSON}; @@ -449,4 +456,4 @@ function SearchPageHeader({queryJSON}: SearchPageHeaderProps) { SearchPageHeader.displayName = 'SearchPageHeader'; export type {SearchHeaderOptionValue}; -export default SearchPageHeader; \ No newline at end of file +export default SearchPageHeader; From 6896ab5b2668c26c4f36b63ae3c552cda3bc2cfd Mon Sep 17 00:00:00 2001 From: Hans Date: Wed, 5 Feb 2025 00:45:18 +0700 Subject: [PATCH 08/16] prettier --- src/libs/actions/Search.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index 550fd525f5843..062aab2eedb35 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -88,7 +88,7 @@ function getLastPolicyPaymentMethod(policyID: string | undefined, lastPaymentMet lastPolicyPaymentMethod = (lastPaymentMethods?.[policyID] as LastPaymentMethodType)?.DEFAULT as ValueOf; } - return lastPolicyPaymentMethod + return lastPolicyPaymentMethod; } function getPayActionCallback(hash: number, item: TransactionListItemType | ReportListItemType, goToItem: () => void) { From fdc97e1ae8d2a192589a2b74d99fc5a351a7329f Mon Sep 17 00:00:00 2001 From: Hans Date: Wed, 5 Feb 2025 09:43:43 +0700 Subject: [PATCH 09/16] commi without submodule --- .github/workflows/androidBump.yml | 9 +- .github/workflows/buildAndroid.yml | 9 +- .github/workflows/compareNDandODbuilds.yml | 15 +- .github/workflows/deploy.yml | 72 +-- .github/workflows/failureNotifier.yml | 3 +- .github/workflows/testBuild.yml | 24 +- .github/workflows/testBuildHybrid.yml | 45 +- .../app/android-fastlane-json-key.json.gpg | Bin 1730 -> 0 bytes android/app/build.gradle | 4 +- android/app/my-upload-key.keystore.gpg | Bin 2244 -> 0 bytes assets/images/customEmoji/global-create.svg | 14 - .../netsuite-quickstart-icon-square.svg | 35 ++ .../Plan-types-and-pricing.md | 2 +- .../company-cards/Commercial-feeds.md | 158 +++--- fastlane/Fastfile | 18 +- ios/Certificates.p12.gpg | Bin 3323 -> 0 bytes ios/NewApp_AdHoc.mobileprovision.gpg | Bin 12018 -> 0 bytes ...c_Notification_Service.mobileprovision.gpg | Bin 11617 -> 0 bytes ..._AdHoc_Share_Extension.mobileprovision.gpg | Bin 11124 -> 0 bytes ios/NewApp_AppStore.mobileprovision.gpg | Bin 8310 -> 0 bytes ...e_Notification_Service.mobileprovision.gpg | Bin 7931 -> 0 bytes ios/NewApp_Development.mobileprovision.gpg | Bin 26075 -> 0 bytes ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- ios/ios-fastlane-json-key.json.gpg | 2 - package-lock.json | 12 +- package.json | 4 +- src/CONST.ts | 71 ++- src/ONYXKEYS.ts | 3 + src/ROUTES.ts | 66 +++ src/SCREENS.ts | 12 + src/components/ArchivedReportFooter.tsx | 2 +- src/components/AvatarWithDisplayName.tsx | 2 +- .../BlockingViews/FullPageNotFoundView.tsx | 11 + .../BrokenConnectionDescription.tsx | 2 +- src/components/ConnectToNSQSFlow/index.tsx | 15 + src/components/ConnectToNSQSFlow/types.ts | 10 + .../ConnectToNetSuiteFlow/index.tsx | 8 +- src/components/ConnectionLayout.tsx | 4 +- .../BaseHTMLEngineProvider.tsx | 1 - .../CustomEmojiWithDefaultPressableAction.tsx | 21 - .../HTMLRenderers/CustomEmojiRenderer.tsx | 42 -- .../HTMLEngineProvider/HTMLRenderers/index.ts | 2 - src/components/Icon/Expensicons.ts | 2 + .../LHNOptionsList/LHNOptionsList.tsx | 4 +- src/components/MenuItem.tsx | 6 +- src/components/MenuItemList.tsx | 51 +- .../MoneyRequestConfirmationList.tsx | 2 +- .../Reactions/ReactionTooltipContent.tsx | 5 +- .../ReportActionItem/IssueCardMessage.tsx | 4 +- .../MoneyRequestPreviewContent.tsx | 8 +- .../ReportActionItem/MoneyRequestView.tsx | 4 +- .../ReportActionItem/ReportPreview.tsx | 8 +- src/components/ReportActionItem/TaskView.tsx | 2 +- src/components/ReportWelcomeText.tsx | 8 +- .../Search/SearchAutocompleteInput.tsx | 9 +- .../SearchFiltersParticipantsSelector.tsx | 6 +- .../SelectionList/BaseSelectionList.tsx | 21 +- src/components/SelectionList/ChatListItem.tsx | 88 ++-- src/components/SelectionScreen.tsx | 13 +- src/hooks/useArrowKeyFocusManager.ts | 9 +- src/hooks/useBasePopoverReactionList/index.ts | 2 +- src/hooks/useSyncFocus/index.ts | 29 +- .../useSyncFocusImplementation.ts | 28 + src/languages/en.ts | 99 +++- src/languages/es.ts | 96 +++- .../parameters/ConnectPolicyToNSQSParams.ts | 6 + .../API/parameters/OpenWorkspaceViewParams.ts | 2 +- .../API/parameters/SyncPolicyToNSQSParams.ts | 6 + .../API/parameters/UpdateChatNameParams.ts | 6 + .../parameters/UpdateGroupChatNameParams.ts | 5 - .../UpdateNSQSApprovalAccountParams.ts | 6 + .../parameters/UpdateNSQSAutoSyncParams.ts | 6 + .../UpdateNSQSCustomersMappingParams.ts | 9 + .../parameters/UpdateNSQSExportDateParams.ts | 9 + .../parameters/UpdateNSQSExporterParams.ts | 6 + .../UpdateNSQSProjectsMappingParams.ts | 9 + src/libs/API/parameters/index.ts | 10 +- src/libs/API/types.ts | 20 +- src/libs/AccountingUtils.ts | 2 + src/libs/ApiUtils.ts | 10 +- src/libs/Fullstory/index.ts | 8 +- src/libs/ModifiedExpenseMessage.ts | 23 +- .../ModalStackNavigators/index.tsx | 16 + src/libs/Navigation/Navigation.ts | 24 +- .../FULL_SCREEN_TO_RHP_MAPPING.ts | 12 + src/libs/Navigation/linkingConfig/config.ts | 34 ++ src/libs/Navigation/types.ts | 39 ++ src/libs/NextStepUtils.ts | 6 +- .../LocalNotification/BrowserNotifications.ts | 2 +- src/libs/OptionsListUtils.ts | 23 +- src/libs/PaymentUtils.ts | 2 +- src/libs/PersonalDetailsUtils.ts | 16 +- src/libs/PolicyUtils.ts | 5 +- src/libs/ReportActionsUtils.ts | 42 +- src/libs/ReportUtils.ts | 486 ++++++++++++------ src/libs/SearchUIUtils.ts | 35 +- src/libs/SidebarUtils.ts | 12 +- src/libs/TransactionUtils/index.ts | 4 +- src/libs/actions/BankAccounts.ts | 2 +- src/libs/actions/IOU.ts | 9 +- src/libs/actions/Policy/Policy.ts | 7 +- src/libs/actions/Report.ts | 17 +- src/libs/actions/connections/NSQS.ts | 187 +++++++ src/libs/actions/connections/index.ts | 3 + .../getCompanyCardBankConnection/index.tsx | 14 +- src/libs/navigateAfterOnboarding.ts | 19 +- src/pages/GroupChatNameEditPage.tsx | 19 +- src/pages/NewChatConfirmPage.tsx | 58 +-- .../BaseOnboardingAccounting.tsx | 2 +- src/pages/ReportAvatar.tsx | 15 +- src/pages/ReportDetailsPage.tsx | 10 +- src/pages/ReportParticipantsPage.tsx | 2 +- src/pages/RoomMembersPage.tsx | 2 +- src/pages/ShareCodePage.tsx | 8 +- src/pages/TransactionDuplicate/Review.tsx | 6 +- src/pages/TripChatNameEditPage.tsx | 102 ++++ src/pages/home/HeaderView.tsx | 2 +- src/pages/home/ReportScreen.tsx | 2 + .../report/ContextMenu/ContextMenuActions.tsx | 6 +- .../ReportActionCompose/SuggestionMention.tsx | 2 +- src/pages/home/report/ReportActionItem.tsx | 2 +- .../home/report/ReportActionItemSingle.tsx | 8 +- .../home/report/ReportTypingIndicator.tsx | 4 +- .../FloatingActionButtonAndPopover.tsx | 230 ++++----- .../request/step/IOURequestStepSubrate.tsx | 1 + src/pages/settings/AboutPage/AboutPage.tsx | 18 +- src/pages/settings/Report/NamePage.tsx | 10 +- src/pages/workspace/WorkspaceInitialPage.tsx | 13 +- src/pages/workspace/WorkspaceMembersPage.tsx | 6 +- .../workspace/WorkspacePageWithSections.tsx | 21 +- src/pages/workspace/WorkspaceProfilePage.tsx | 3 +- src/pages/workspace/WorkspacesListRow.tsx | 2 +- .../MultiConnectionSelectorPage.tsx | 126 +++++ .../accounting/PolicyAccountingPage.tsx | 78 ++- .../NetSuiteTokenInputPage.tsx | 16 +- .../accounting/nsqs/NSQSSetupPage.tsx | 111 ++++ .../nsqs/advanced/NSQSAdvancedPage.tsx | 89 ++++ .../nsqs/advanced/NSQSApprovalAccountPage.tsx | 87 ++++ .../accounting/nsqs/export/NSQSDatePage.tsx | 77 +++ .../accounting/nsqs/export/NSQSExportPage.tsx | 75 +++ .../nsqs/export/NSQSPreferredExporterPage.tsx | 102 ++++ .../import/NSQSCustomersDisplayedAsPage.tsx | 73 +++ .../nsqs/import/NSQSCustomersPage.tsx | 81 +++ .../accounting/nsqs/import/NSQSImportPage.tsx | 76 +++ .../import/NSQSProjectsDisplayedAsPage.tsx | 73 +++ .../nsqs/import/NSQSProjectsPage.tsx | 81 +++ src/pages/workspace/accounting/utils.tsx | 25 +- .../WorkspaceCategoriesSettingsPage.tsx | 20 +- .../invoices/WorkspaceInvoiceVBASection.tsx | 23 +- src/pages/workspace/taxes/NamePage.tsx | 9 +- src/setup/index.ts | 5 +- src/styles/index.ts | 17 +- src/styles/utils/sizing.ts | 4 + src/types/form/NSQSOAuth2Form.ts | 18 + src/types/form/index.ts | 1 + src/types/onyx/Policy.ts | 94 ++++ src/types/onyx/SearchResults.ts | 18 + src/types/onyx/Transaction.ts | 2 +- tests/actions/IOUTest.ts | 55 +- .../ModifiedExpenseMessage.perf-test.ts | 2 +- tests/unit/ModifiedExpenseMessageTest.ts | 44 +- tests/unit/Search/SearchUIUtilsTest.ts | 1 + tests/unit/SidebarFilterTest.ts | 87 ++-- tests/unit/navigateAfterOnboardingTest.ts | 92 ++++ tests/unit/useSyncFocusTest.ts | 39 ++ web/thirdPartyScripts.js | 143 ------ 168 files changed, 3564 insertions(+), 1201 deletions(-) delete mode 100644 android/app/android-fastlane-json-key.json.gpg delete mode 100644 android/app/my-upload-key.keystore.gpg delete mode 100644 assets/images/customEmoji/global-create.svg create mode 100644 assets/images/integrationicons/netsuite-quickstart-icon-square.svg delete mode 100644 ios/Certificates.p12.gpg delete mode 100644 ios/NewApp_AdHoc.mobileprovision.gpg delete mode 100644 ios/NewApp_AdHoc_Notification_Service.mobileprovision.gpg delete mode 100644 ios/NewApp_AdHoc_Share_Extension.mobileprovision.gpg delete mode 100644 ios/NewApp_AppStore.mobileprovision.gpg delete mode 100644 ios/NewApp_AppStore_Notification_Service.mobileprovision.gpg delete mode 100644 ios/NewApp_Development.mobileprovision.gpg delete mode 100644 ios/ios-fastlane-json-key.json.gpg create mode 100644 src/components/ConnectToNSQSFlow/index.tsx create mode 100644 src/components/ConnectToNSQSFlow/types.ts delete mode 100644 src/components/HTMLEngineProvider/CustomEmojiWithDefaultPressableAction.tsx delete mode 100644 src/components/HTMLEngineProvider/HTMLRenderers/CustomEmojiRenderer.tsx create mode 100644 src/hooks/useSyncFocus/useSyncFocusImplementation.ts create mode 100644 src/libs/API/parameters/ConnectPolicyToNSQSParams.ts create mode 100644 src/libs/API/parameters/SyncPolicyToNSQSParams.ts create mode 100644 src/libs/API/parameters/UpdateChatNameParams.ts delete mode 100644 src/libs/API/parameters/UpdateGroupChatNameParams.ts create mode 100644 src/libs/API/parameters/UpdateNSQSApprovalAccountParams.ts create mode 100644 src/libs/API/parameters/UpdateNSQSAutoSyncParams.ts create mode 100644 src/libs/API/parameters/UpdateNSQSCustomersMappingParams.ts create mode 100644 src/libs/API/parameters/UpdateNSQSExportDateParams.ts create mode 100644 src/libs/API/parameters/UpdateNSQSExporterParams.ts create mode 100644 src/libs/API/parameters/UpdateNSQSProjectsMappingParams.ts create mode 100644 src/libs/actions/connections/NSQS.ts create mode 100644 src/pages/TripChatNameEditPage.tsx create mode 100644 src/pages/workspace/accounting/MultiConnectionSelectorPage.tsx create mode 100644 src/pages/workspace/accounting/nsqs/NSQSSetupPage.tsx create mode 100644 src/pages/workspace/accounting/nsqs/advanced/NSQSAdvancedPage.tsx create mode 100644 src/pages/workspace/accounting/nsqs/advanced/NSQSApprovalAccountPage.tsx create mode 100644 src/pages/workspace/accounting/nsqs/export/NSQSDatePage.tsx create mode 100644 src/pages/workspace/accounting/nsqs/export/NSQSExportPage.tsx create mode 100644 src/pages/workspace/accounting/nsqs/export/NSQSPreferredExporterPage.tsx create mode 100644 src/pages/workspace/accounting/nsqs/import/NSQSCustomersDisplayedAsPage.tsx create mode 100644 src/pages/workspace/accounting/nsqs/import/NSQSCustomersPage.tsx create mode 100644 src/pages/workspace/accounting/nsqs/import/NSQSImportPage.tsx create mode 100644 src/pages/workspace/accounting/nsqs/import/NSQSProjectsDisplayedAsPage.tsx create mode 100644 src/pages/workspace/accounting/nsqs/import/NSQSProjectsPage.tsx create mode 100644 src/types/form/NSQSOAuth2Form.ts create mode 100644 tests/unit/navigateAfterOnboardingTest.ts create mode 100644 tests/unit/useSyncFocusTest.ts diff --git a/.github/workflows/androidBump.yml b/.github/workflows/androidBump.yml index e10304d1d922f..5ea71c028e15a 100644 --- a/.github/workflows/androidBump.yml +++ b/.github/workflows/androidBump.yml @@ -21,9 +21,14 @@ jobs: with: bundler-cache: true - - name: Decrypt json Google Play credentials - run: gpg --batch --yes --decrypt --passphrase="${{ secrets.LARGE_SECRET_PASSPHRASE }}" --output android-fastlane-json-key.json android-fastlane-json-key.json.gpg + - name: Install 1Password CLI + uses: 1password/install-cli-action@v1 + + - name: Load files from 1Password working-directory: android/app + env: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + run: op read "op://Mobile-Deploy-CI/android-fastlane-json-key.json/android-fastlane-json-key.json" --force --out-file ./android-fastlane-json-key.json - name: Get status from Google Play and generate next rollout percentage id: checkAndroidStatus diff --git a/.github/workflows/buildAndroid.yml b/.github/workflows/buildAndroid.yml index d7784e2f610bf..c4d33b00bef4b 100644 --- a/.github/workflows/buildAndroid.yml +++ b/.github/workflows/buildAndroid.yml @@ -85,9 +85,14 @@ jobs: with: bundler-cache: true - - name: Decrypt keystore to sign the APK/AAB - run: gpg --batch --yes --decrypt --passphrase="${{ secrets.LARGE_SECRET_PASSPHRASE }}" --output my-upload-key.keystore my-upload-key.keystore.gpg + - name: Install 1Password CLI + uses: 1password/install-cli-action@v1 + + - name: Load files from 1Password working-directory: android/app + env: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + run: op read "op://Mobile-Deploy-CI/New Expensify my-upload-key.keystore/my-upload-key.keystore" --force --out-file ./my-upload-key.keystore - name: Get package version id: getPackageVersion diff --git a/.github/workflows/compareNDandODbuilds.yml b/.github/workflows/compareNDandODbuilds.yml index 51ba44a192e9d..99a5de8965010 100644 --- a/.github/workflows/compareNDandODbuilds.yml +++ b/.github/workflows/compareNDandODbuilds.yml @@ -53,11 +53,13 @@ jobs: uses: 1password/install-cli-action@v1 - name: Load files from 1Password + working-directory: android/app env: OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} run: | - op document get --output ./upload-key.keystore upload-key.keystore - op document get --output ./android-fastlane-json-key.json android-fastlane-json-key.json + op read "op://Mobile-Deploy-CI/android-fastlane-json-key.json/android-fastlane-json-key.json" --force --out-file ./android-fastlane-json-key.json + op read "op://Mobile-Deploy-CI/New Expensify my-upload-key.keystore/my-upload-key.keystore" --force --out-file ./my-upload-key.keystore + # Copy the keystore to the Android directory for Fullstory cp ./upload-key.keystore Mobile-Expensify/Android @@ -104,9 +106,14 @@ jobs: with: IS_HYBRID_BUILD: 'false' - - name: Decrypt keystore to sign the APK/AAB - run: gpg --batch --yes --decrypt --passphrase="${{ secrets.LARGE_SECRET_PASSPHRASE }}" --output my-upload-key.keystore my-upload-key.keystore.gpg + - name: Install 1Password CLI + uses: 1password/install-cli-action@v1 + + - name: Load files from 1Password working-directory: android/app + env: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + run: op read "op://Mobile-Deploy-CI/New Expensify my-upload-key.keystore/my-upload-key.keystore" --force --out-file ./my-upload-key.keystore - name: Build Android Release working-directory: android diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6ca2f0f8a698a..3b84fbc6a6e4d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -97,12 +97,14 @@ jobs: pattern: android-*-artifact merge-multiple: true - - name: Log downloaded artifact paths - run: ls -R /tmp/artifacts + - name: Install 1Password CLI + uses: 1password/install-cli-action@v1 - - name: Decrypt json w/ Google Play credentials - run: gpg --batch --yes --decrypt --passphrase="${{ secrets.LARGE_SECRET_PASSPHRASE }}" --output android-fastlane-json-key.json android-fastlane-json-key.json.gpg + - name: Load files from 1Password working-directory: android/app + env: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + run: op read "op://Mobile-Deploy-CI/android-fastlane-json-key.json/android-fastlane-json-key.json" --force --out-file ./android-fastlane-json-key.json - name: Upload Android app to Google Play run: bundle exec fastlane android upload_google_play_internal @@ -166,9 +168,10 @@ jobs: env: OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} run: | - op read op://Mobile-Deploy-CI/firebase.json/firebase.json --force --out-file ./firebase.json - op read op://Mobile-Deploy-CI/upload-key.keystore/upload-key.keystore --force --out-file ./upload-key.keystore - op read op://Mobile-Deploy-CI/android-fastlane-json-key.json/android-fastlane-json-key.json --force --out-file ./android-fastlane-json-key.json + op read "op://Mobile-Deploy-CI/firebase.json/firebase.json" --force --out-file ./firebase.json + op read "op://Mobile-Deploy-CI/upload-key.keystore/upload-key.keystore" --force --out-file ./upload-key.keystore + op read "op://Mobile-Deploy-CI/android-fastlane-json-key.json/android-fastlane-json-key.json" --force --out-file ./android-fastlane-json-key.json + # Copy the keystore to the Android directory for Fullstory cp ./upload-key.keystore Mobile-Expensify/Android @@ -373,25 +376,17 @@ jobs: max_attempts: 5 command: scripts/pod-install.sh - - name: Decrypt AppStore profile - run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output NewApp_AppStore.mobileprovision NewApp_AppStore.mobileprovision.gpg - env: - LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - - - name: Decrypt AppStore Notification Service profile - run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output NewApp_AppStore_Notification_Service.mobileprovision NewApp_AppStore_Notification_Service.mobileprovision.gpg - env: - LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - - - name: Decrypt certificate - run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output Certificates.p12 Certificates.p12.gpg - env: - LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} + - name: Install 1Password CLI + uses: 1password/install-cli-action@v1 - - name: Decrypt App Store Connect API key - run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output ios-fastlane-json-key.json ios-fastlane-json-key.json.gpg + - name: Load files from 1Password env: - LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + run: | + op read "op://Mobile-Deploy-CI/NewApp_AppStore/NewApp_AppStore.mobileprovision" --force --out-file ./NewApp_AppStore.mobileprovision + op read "op://Mobile-Deploy-CI/NewApp_AppStore_Notification_Service/NewApp_AppStore_Notification_Service.mobileprovision" --force --out-file ./NewApp_AppStore_Notification_Service.mobileprovision + op read "op://Mobile-Deploy-CI/New Expensify Distribution Certificate/Certificates.p12" --force --out-file ./Certificates.p12 + op read "op://Mobile-Deploy-CI/ios-fastlane-json-key.json/ios-fastlane-json-key.json" --force --out-file ./ios-fastlane-json-key.json - name: Get iOS native version id: getIOSVersion @@ -511,30 +506,11 @@ jobs: env: OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} run: | - op read op://Mobile-Deploy-CI/firebase.json/firebase.json --force --out-file ./firebase.json - op read op://Mobile-Deploy-CI/OldApp_AppStore/OldApp_AppStore.mobileprovision --force --out-file ./OldApp_AppStore.mobileprovision - op read op://Mobile-Deploy-CI/OldApp_AppStore_Share_Extension/OldApp_AppStore_Share_Extension.mobileprovision --force --out-file ./OldApp_AppStore_Share_Extension.mobileprovision - op read op://Mobile-Deploy-CI/OldApp_AppStore_Notification_Service/OldApp_AppStore_Notification_Service.mobileprovision --force --out-file ./OldApp_AppStore_Notification_Service.mobileprovision - - - name: Decrypt AppStore profile - run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output NewApp_AppStore.mobileprovision NewApp_AppStore.mobileprovision.gpg - env: - LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - - - name: Decrypt AppStore Notification Service profile - run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output NewApp_AppStore_Notification_Service.mobileprovision NewApp_AppStore_Notification_Service.mobileprovision.gpg - env: - LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - - - name: Decrypt certificate - run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output Certificates.p12 Certificates.p12.gpg - env: - LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - - - name: Decrypt App Store Connect API key - run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output ios-fastlane-json-key.json ios-fastlane-json-key.json.gpg - env: - LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} + op read "op://Mobile-Deploy-CI/firebase.json/firebase.json" --force --out-file ./firebase.json + op read "op://Mobile-Deploy-CI/OldApp_AppStore/OldApp_AppStore.mobileprovision" --force --out-file ./OldApp_AppStore.mobileprovision + op read "op://Mobile-Deploy-CI/OldApp_AppStore_Share_Extension/OldApp_AppStore_Share_Extension.mobileprovision" --force --out-file ./OldApp_AppStore_Share_Extension.mobileprovision + op read "op://Mobile-Deploy-CI/OldApp_AppStore_Notification_Service/OldApp_AppStore_Notification_Service.mobileprovision" --force --out-file ./OldApp_AppStore_Notification_Service.mobileprovision + op read "op://Mobile-Deploy-CI/ios-fastlane-json-key.json/ios-fastlane-json-key.json" --force --out-file ./ios-fastlane-json-key.json - name: Set current App version in Env run: echo "VERSION=$(npm run print-version --silent)" >> "$GITHUB_ENV" diff --git a/.github/workflows/failureNotifier.yml b/.github/workflows/failureNotifier.yml index 39dfbe8e84a78..134ac0eff19f7 100644 --- a/.github/workflows/failureNotifier.yml +++ b/.github/workflows/failureNotifier.yml @@ -25,7 +25,8 @@ jobs: repo: context.repo.repo, run_id: runId, }); - return jobsData.data; + const jobNamesToIgnore = ['confirmPassingBuild']; + return jobsData.data.filter(job => !jobNamesToIgnore.includes(job.name)); - name: Fetch Previous Workflow Run id: previous-workflow-run diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml index 869db3d04be74..ea62bca794fe2 100644 --- a/.github/workflows/testBuild.yml +++ b/.github/workflows/testBuild.yml @@ -111,9 +111,6 @@ jobs: pattern: android-*-artifact merge-multiple: true - - name: Log downloaded artifact paths - run: ls -R /tmp/artifacts - - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: @@ -189,20 +186,17 @@ jobs: max_attempts: 5 command: scripts/pod-install.sh - - name: Decrypt AdHoc profile - run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output NewApp_AdHoc.mobileprovision NewApp_AdHoc.mobileprovision.gpg - env: - LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} + - name: Install 1Password CLI + uses: 1password/install-cli-action@v1 - - name: Decrypt AdHoc Notification Service profile - run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output NewApp_AdHoc_Notification_Service.mobileprovision NewApp_AdHoc_Notification_Service.mobileprovision.gpg + - name: Load files from 1Password env: - LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - - - name: Decrypt certificate - run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output Certificates.p12 Certificates.p12.gpg - env: - LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + run: | + op read "op://Mobile-Deploy-CI/NewApp_AdHoc/NewApp_AdHoc.mobileprovision" --force --out-file ./NewApp_AdHoc.mobileprovision + op read "op://Mobile-Deploy-CI/NewApp_AdHoc_Notification_Service/NewApp_AdHoc_Notification_Service.mobileprovision" --force --out-file ./NewApp_AdHoc_Notification_Service.mobileprovision + op read "op://Mobile-Deploy-CI/NewApp_AdHoc_Share_Extension.mobileprovision/NewApp_AdHoc_Share_Extension.mobileprovision" --force --out-file ./NewApp_AdHoc_Share_Extension.mobileprovision + op read "op://Mobile-Deploy-CI/New Expensify Distribution Certificate/Certificates.p12" --force --out-file ./Certificates.p12 - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/testBuildHybrid.yml b/.github/workflows/testBuildHybrid.yml index 6a8a0d5884bfb..9bd1b3b0f541b 100644 --- a/.github/workflows/testBuildHybrid.yml +++ b/.github/workflows/testBuildHybrid.yml @@ -59,7 +59,7 @@ jobs: echo "REF=$(gh pr view ${{ github.event.inputs.PULL_REQUEST_NUMBER }} --json headRefOid --jq '.headRefOid')" >> "$GITHUB_OUTPUT" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - + getOldDotPR: runs-on: ubuntu-latest needs: validateActor @@ -106,7 +106,7 @@ jobs: fi env: GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} - + postGitHubCommentBuildStarted: runs-on: ubuntu-latest @@ -153,16 +153,16 @@ jobs: cd Mobile-Expensify git fetch origin ${{ needs.getOldDotBranchRef.outputs.OLD_DOT_REF }} git checkout ${{ needs.getOldDotBranchRef.outputs.OLD_DOT_REF }} - + - name: Configure MapBox SDK run: ./scripts/setup-mapbox-sdk.sh ${{ secrets.MAPBOX_SDK_DOWNLOAD_TOKEN }} - name: Setup Node id: setup-node uses: ./.github/actions/composite/setupNode - with: + with: IS_HYBRID_BUILD: 'true' - + - name: Run grunt build run: | cd Mobile-Expensify @@ -192,10 +192,11 @@ jobs: env: OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} run: | - op document get --output ./upload-key.keystore upload-key.keystore - op document get --output ./android-fastlane-json-key.json android-fastlane-json-key.json + op read "op://Mobile-Deploy-CI/upload-key.keystore/upload-key.keystore" --force --out-file ./upload-key.keystore + op read "op://Mobile-Deploy-CI/android-fastlane-json-key.json/android-fastlane-json-key.json" --force --out-file ./android-fastlane-json-key.json + # Copy the keystore to the Android directory for Fullstory - cp ./upload-key.keystore Mobile-Expensify/Android + cp ./upload-key.keystore Mobile-Expensify/Android - name: Load Android upload keystore credentials from 1Password id: load-credentials @@ -215,28 +216,28 @@ jobs: ANDROID_UPLOAD_KEYSTORE_ALIAS: ${{ steps.load-credentials.outputs.ANDROID_UPLOAD_KEYSTORE_ALIAS }} ANDROID_UPLOAD_KEY_PASSWORD: ${{ steps.load-credentials.outputs.ANDROID_UPLOAD_KEY_PASSWORD }} run: bundle exec fastlane android build_adhoc_hybrid - + - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1 - + - name: Upload Android AdHoc build to S3 run: bundle exec fastlane android upload_s3 env: S3_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY_ID }} S3_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} S3_BUCKET: ad-hoc-expensify-cash - S3_REGION: us-east-1 + S3_REGION: us-east-1 - name: Export S3 path id: exportAndroidS3Path run: | # $s3APKPath is set from within the Fastfile, android upload_s3 lane echo "S3_APK_PATH=$s3APKPath" >> "$GITHUB_OUTPUT" - + iosHybrid: name: Build and deploy iOS for testing needs: [validateActor, getBranchRef, getOldDotBranchRef] @@ -271,9 +272,9 @@ jobs: - name: Setup Node id: setup-node uses: ./.github/actions/composite/setupNode - with: + with: IS_HYBRID_BUILD: 'true' - + - name: Create .env.adhoc file based on staging and add PULL_REQUEST_NUMBER env to it run: | cp .env.staging .env.adhoc @@ -284,7 +285,7 @@ jobs: uses: ruby/setup-ruby@v1.204.0 with: bundler-cache: true - + - name: Install New Expensify Gems run: bundle install @@ -314,14 +315,10 @@ jobs: env: OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} run: | - op read op://Mobile-Deploy-CI/OldApp_AdHoc/OldApp_AdHoc.mobileprovision --force --out-file ./OldApp_AdHoc.mobileprovision - op read op://Mobile-Deploy-CI/OldApp_AdHoc_Share_Extension/OldApp_AdHoc_Share_Extension.mobileprovision --force --out-file ./OldApp_AdHoc_Share_Extension.mobileprovision - op read op://Mobile-Deploy-CI/OldApp_AdHoc_Notification_Service/OldApp_AdHoc_Notification_Service.mobileprovision --force --out-file ./OldApp_AdHoc_Notification_Service.mobileprovision - - - name: Decrypt certificate - run: cd ios && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output Certificates.p12 Certificates.p12.gpg - env: - LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} + op read "op://Mobile-Deploy-CI/OldApp_AdHoc/OldApp_AdHoc.mobileprovision" --force --out-file ./OldApp_AdHoc.mobileprovision + op read "op://Mobile-Deploy-CI/OldApp_AdHoc_Share_Extension/OldApp_AdHoc_Share_Extension.mobileprovision" --force --out-file ./OldApp_AdHoc_Share_Extension.mobileprovision + op read "op://Mobile-Deploy-CI/OldApp_AdHoc_Notification_Service/OldApp_AdHoc_Notification_Service.mobileprovision" --force --out-file ./OldApp_AdHoc_Notification_Service.mobileprovision + op read "op://Mobile-Deploy-CI/New Expensify Distribution Certificate/Certificates.p12" --force --out-file ./Certificates.p12 - name: Build AdHoc app run: bundle exec fastlane ios build_adhoc_hybrid @@ -347,8 +344,6 @@ jobs: name: ios path: ./ios_paths.json - - postGithubComment: runs-on: ubuntu-latest name: Post a GitHub comment with app download links for testing diff --git a/android/app/android-fastlane-json-key.json.gpg b/android/app/android-fastlane-json-key.json.gpg deleted file mode 100644 index 386ee2b45f44d99b412cc2ec27e2f216c04f74a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1730 zcmV;z20i(V4Fm}T0w>eCkWp+0mbUZ`96ORCT?q^Opl9fz} zsmB9s;hl56{fJct71sAYO;-h=ul5uRj~E60q4eC%%BHCC-?I3L7M(43ErFBISzowqE#qH&Ef{I=Po)>*_-u7m$VX({{*u=btIX2+LnYLpT;G}(7l!(FglS2Q z2^JPMDmQi}dTurqbBpZ^La5Qh_J$Ky(8!(&@m|eQ# zdn|>d3R5yGXxW{r_CA(@WlCc|5=jv6s~#Z^=}s$wsLhZb;aN}A zR9pX2SF*}QV?_G>f~_?HqWVME_fFOql>McIn9&5D4b>WRZ@Xgo2GAN;FVzDg70Hl6 zwyik<2_#K?Z8PKa^-8z~{A!o}S}2d=@{!8X`X?0|1;8BrX4FYLhHN^xbbDD!fFfD> z7uvHRk=y$l-CCLB%vBd;E&v}v!$76TMO(XUICuYY+G?sG@p`I7o40m&-$K}jKE-@> z9h8rIpq2U#N?9JlIK++_g_g5)%}Zj%-U&yv5l{aZpn(lGWZIB`z7|fY#MpF(2wrHO z=O30smAj{xOQg?-^v%S}{G)D$dWU-pQ@|Y6k)g^b>0Var#i#d{jJT>Ek(K+qdl}JK z#mgyWcn>S`61++>AfWJ)>U17c9X=%F$excy2@oEi9QprFWsHmsO7;R4a=8e8&tAvq zONv@FUysH7>B39beAJ5QK_*#oVU@C#1ky(l{O8K^uL8T^!LPUT2RI=Aq4H?9^k(J3 zNNW{mF&JN+iX^Mg%xxbVBYO+5%U<5;_?*$4-Gjhzu4+}gNP01Ed10_B4mPDL+g4!( zu2c#)pSw@Io?X;Fo`Av!z6T(QthO-~^+P7aeV<4fldd1LAm@MCwpq4-fCR*Qz*PDU zhrqd+3I45`k*Qz3{2~+-{{2;{jYa--ooG27M9!MRxj3dzzAMSOG~&HbZY5WKKWlGZ z{!4!3&Y{4@gB(upL%)pIf)@(LwS2}zrHENCe7W|l+$OT+km!R}HC9qKt@7#5x+e)D_R*&42pxIiiL1Ig=%~4@=m2WK8W?N@Wk+%REuVs z1)`U0?-xyqOz~aUM`!a|=quii^!GhH79(nlyT%xue+u}6zN@eAO2ozO`cV!NAmzuHX?$Rl@%?G zbJbl|U&jH0W5DF_{RcH;h`=9$q_D1Vg2bkkA&dQhj*|+24OGe*YiqmAnqwW(Y#fpirk#`{GaLn^`>B5 zN3RUknRVMqIH3ckG;Ar}ysCa@{n{qTVfvU4AvI4#a%T{!8Tms~GBdZNj#KIK?9CR5 z!KnA3Vr2RO&c&8zL1#|t&Ju8v<2FtL`ze-q#aXq_21z4&^6&C2edn%-Hm@t1e&~Py zf5aM~bXw|hQBI#`F(#RaIMB7RF5b?KJ4P*fx64NNOHKga0AR7oaXSG9O<1u0`ddgP Y6n&Bt*huG-I1r*h$2*!|y+7AYHLJ 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 - - - - - - diff --git a/assets/images/integrationicons/netsuite-quickstart-icon-square.svg b/assets/images/integrationicons/netsuite-quickstart-icon-square.svg new file mode 100644 index 0000000000000..5b8ddb542cf77 --- /dev/null +++ b/assets/images/integrationicons/netsuite-quickstart-icon-square.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/articles/new-expensify/billing-and-subscriptions/Plan-types-and-pricing.md b/docs/articles/new-expensify/billing-and-subscriptions/Plan-types-and-pricing.md index ee181706d70df..59314be96584b 100644 --- a/docs/articles/new-expensify/billing-and-subscriptions/Plan-types-and-pricing.md +++ b/docs/articles/new-expensify/billing-and-subscriptions/Plan-types-and-pricing.md @@ -39,7 +39,7 @@ Yes! Customers can pay in AUD, GBP, or NZD in addition to USD. - **Control Plan:** A$30, £14, or NZ$32 per user/month (Annual subscription + Expensify Cards) ## Is Expensify free for individuals? -Yes! Individuals can use Expensify for free to track expenses. +Yes! Individuals can use Expensify for free to track expenses. The steps in this [help article](https://help.expensify.com/articles/expensify-classic/getting-started/Create-a-workspace-for-yourself) will walk you through creating a personal workspace to track your expenses. ## How do I get more info about pricing? For customized information or help choosing the right plan, reach out to Expensify Concierge or email **concierge@expensify.com**. diff --git a/docs/articles/new-expensify/connect-credit-cards/company-cards/Commercial-feeds.md b/docs/articles/new-expensify/connect-credit-cards/company-cards/Commercial-feeds.md index 2dbe47d3b1781..bd94e2ccff54b 100644 --- a/docs/articles/new-expensify/connect-credit-cards/company-cards/Commercial-feeds.md +++ b/docs/articles/new-expensify/connect-credit-cards/company-cards/Commercial-feeds.md @@ -2,115 +2,111 @@ title: Commercial-feeds.md description: Commercial feeds --- + # Overview Commercial feeds are the most reliable way to import company card expenses. They remain unaffected by changes to bank login credentials or UI updates, making them highly recommended for those eligible. + The easiest way to confirm your eligibility for a commercial feed is to ask your bank directly. -# Prerequisites for enabling a commercial feed -If you haven't already, you need to create a workspace before setting up a commercial feed. Go to Settings > Workspaces > New workspace to create one. -Additionally, you’ll need to enable company cards on your workspace by navigating to Settings > Workspaces > [your workspace] > More features, and toggling on Company cards. Note that upgrading to the Control plan is required to access this feature. -# How to set up a Mastercard commercial feed + +# Prerequisites for Enabling a Commercial Feed +If you haven't already, you need to create a workspace before setting up a commercial feed. Go to **Settings > Workspaces > New workspace** to create one. + +Additionally, you’ll need to enable company cards on your workspace by navigating to **Settings > Workspaces > [your workspace] > More features**, and toggling on **Company cards**. Note that upgrading to the Control plan is required to access this feature. + +# How to Set Up a Mastercard Commercial Feed Your bank must access Mastercard's SmartData portal to complete the process. Expensify is a registered vendor in the portal, so no additional Mastercard forms are required. Your bank may, however, have its own forms. -## Steps to add a Mastercard commercial feed: -Contact your banking relationship manager and request that your CDF (Common Data File) feed be sent directly to Expensify in the Mastercard SmartData Portal (file type: CDF version 3 Release 11.01). Specify the earliest transaction date you need in the feed. -The bank will initiate feed delivery by selecting Expensify in Mastercard's portal and will email you the distribution ID. -While waiting for your bank, ensure your Control plan workspace in Expensify is set up. -Submit the distribution ID in Expensify by navigating to Settings > Workspaces > [your workspace] > Company cards > Add cards, selecting your bank (choose "Other" if not listed), and then selecting Mastercard Commercial Cards. -Once submitted, Expensify will connect the feed and notify you when it’s enabled. -# How to set up a Visa commercial feed -## Steps to add a Visa commercial feed: -Contact your banking relationship manager and request that your VCF (Variant Call Format) feed be sent directly to Expensify. Share this with your bank: "There’s a checkbox in your Visa Subscription Management portal that can be selected to enable the feed, eliminating the need for a test file." -Request the feed filename or raw file information, including the Processor ID, Financial Institution (bank) ID, and Company ID. -While waiting for your bank, ensure your Control plan workspace in Expensify is set up. -Submit the required IDs in Expensify by navigating to Settings > Workspaces > [your workspace] > Company cards > Add cards, selecting your bank (choose "Other" if not listed), and then selecting Visa Commercial Cards. -Once submitted, Expensify will connect the feed and notify you when it’s enabled. - -# How to set up an American Express corporate feed + +## Steps to Add a Mastercard Commercial Feed: +1. Contact your banking relationship manager and request that your CDF (Common Data File) feed be sent directly to Expensify in the Mastercard SmartData Portal (file type: CDF version 3 Release 11.01). Specify the earliest transaction date you need in the feed. +2. The bank will initiate feed delivery by selecting Expensify in Mastercard's portal and will email you the distribution ID. +3. While waiting for your bank, ensure your Control workspace in Expensify is set up. +4. Submit the distribution ID in Expensify by navigating to **Settings > Workspaces > [your workspace] > Company cards > Add cards**, selecting your bank (choose "Other" if not listed), and then selecting **Mastercard Commercial Cards**. +5. Once submitted, Expensify will connect the feed and notify you when it’s enabled. + +# How to Set Up a Visa Commercial Feed +## Steps to Add a Visa Commercial Feed: +1. Contact your banking relationship manager and request that your VCF (Variant Call Format) feed be sent directly to Expensify. Share this with your bank: "There’s a checkbox in your Visa Subscription Management portal that can be selected to enable the feed, eliminating the need for a test file." +2. Request the feed filename or raw file information, including the Processor ID, Financial Institution (bank) ID, and Company ID. +3. While waiting for your bank, ensure your Control workspace in Expensify is set up. +4. Submit the required IDs in Expensify by navigating to **Settings > Workspaces > [your workspace] > Company cards > Add cards**, selecting your bank (choose "Other" if not listed), and then selecting **Visa Commercial Cards**. +5. Once submitted, Expensify will connect the feed and notify you when it’s enabled. + +# How to Set Up an American Express Corporate Feed To begin, fill out Amex's required forms and send them to Amex for processing. Download the forms [here](https://drive.google.com/file/d/1zqDA_MCk06jk_fWjzx2y0r4gOyAMqKJe/view?usp=sharing). -## Instructions for filling out the Amex forms: -PAGE 1 -Corporation Name: The legal name of your company on file with American Express -Corporation Address: The legal address of your company -Requested Feed Start Date: The earliest transaction date you want in Expensify (use international date format: DD/MM/YY or spelled out, e.g., January 1, 1900). -Requestor Contact: Name of the person completing the request -Email Address: Email of the person completing the request -Control Account Number: The master or basic control account number for the cards you’d like to add (not a credit card number). Contact Amex if you need assistance identifying the correct number. -PAGE 2 +## Instructions for Filling Out the Amex Forms: +**PAGE 1** +- **Corporation Name:** The legal name of your company on file with American Express +- **Corporation Address:** The legal address of your company +- **Requested Feed Start Date:** The earliest transaction date you want in Expensify (use international date format: DD/MM/YY or spelled out, e.g., January 1, 1900). +- **Requestor Contact:** Name of the person completing the request +- **Email Address:** Email of the person completing the request +- **Control Account Number:** The master or basic control account number for the cards you’d like to add (not a credit card number). Contact Amex if you need assistance identifying the correct number. + +**PAGE 2** No information required -PAGE 3 -Client Registered Name: The legal name of your company on file with American Express -Master Control Account or Basic Control Account: Same as the control account number on page 1 -PAGE 4 -Country List: The country where the account originates -Client Authorization: Complete your full name, job title, and date (use international date format i.e., DD/MM/YY). Sign where indicated. +**PAGE 3** +- **Client Registered Name:** The legal name of your company on file with American Express +- **Master Control Account or Basic Control Account:** Same as the control account number on page 1 + +**PAGE 4** +- **Country List:** The country where the account originates +- **Client Authorization:** Complete your full name, job title, and date (use international date format i.e., DD/MM/YY). Sign where indicated. -## Steps to add an American Express corporate feed: -Send the completed forms to electronictransmissionsteam@aexp.com and request they send your corporate card feed to Expensify. You should receive a confirmation email within a few days. -While waiting, ensure your Control plan workspace in Expensify is set up. -Amex will send a Production Letter with delivery file name information (e.g., R123456_B123456789_GL1025_001_$DATE$$TIME$_$SEQ$). -Submit the delivery file name in Expensify by navigating to Settings > Workspaces > [your workspace] > Company cards > Add cards > American Express > American Express Corporate Cards. -Once submitted, Expensify will connect the feed and notify you when it’s enabled. +## Steps to Add an American Express Corporate Feed: +1. Send the completed forms to **electronictransmissionsteam@aexp.com** and request they send your corporate card feed to Expensify. You should receive a confirmation email within a few days. +2. While waiting, ensure your Control workspace in Expensify is set up. +3. Amex will send a Production Letter with delivery file name information (e.g., `R123456_B123456789_GL1025_001_$DATE$$TIME$_$SEQ$`). +4. Submit the delivery file name in Expensify by navigating to **Settings > Workspaces > [your workspace] > Company cards > Add cards > American Express > American Express Corporate Cards**. +5. Once submitted, Expensify will connect the feed and notify you when it’s enabled. -# How to assign company cards -Once your feed is connected, you can assign cards to employees. To do this, navigate to Settings > Workspaces > [your workspace] > Company cards. +# How to Assign Company Cards +Once your feed is connected, you can assign cards to employees. To do this, navigate to **Settings > Workspaces > [your workspace] > Company cards**. -![Click the feed name to view the feed selector]({{site.url}}/assets/images/commfeed/commfeed-01.png){:width="100%"} +![Click the feed name to view the feed selector]({{site.url}}/assets/images/commfeed/commfeed-01-updated.png){:width="100%"} If you have multiple feeds, click the feed name at the top left to select the appropriate one. -![Select a feed from the feed selector to view it]({{site.url}}/assets/images/commfeed/commfeed-02.png){:width="100%"} +![Select a feed from the feed selector to view it]({{site.url}}/assets/images/commfeed/commfeed-02-updated.png){:width="100%"} -Click Assign card to select an employee. All workspace members appear in the list. +Click **Assign card** to select an employee. All workspace members appear in the list. -![Click assign card and select an employee from the list]({{site.url}}/assets/images/commfeed-03.png){:width="100%"} +![Click assign card and select an employee from the list]({{site.url}}/assets/images/commfeed/commfeed-03-updated.png){:width="100%"} Select the card you want to assign. Cards only appear if they have recent transactions. -![Select a card from the list]({{site.url}}/assets/images/commfeed/commfeed-04.png){:width="100%"} +![Select a card from the list]({{site.url}}/assets/images/commfeed/commfeed-04-updated.png){:width="100%"} Choose a start date: -From the beginning: Imports all available transactions (typically 30-90 days). -Custom start date: Allows you to specify a date. -![Select your transaction start date]({{site.url}}/assets/images/commfeed/commfeed-05.png){:width="100%"} -Review the details and click Assign card. Transactions will import immediately. -![Double check the selections and assign the card]({{site.url}}/assets/images/commfeed/commfeed-06.png){:width="100%"} - -# Managing cards -Clicking an assigned card opens the Card details page, where you can: -Change the card name. -Select a card-specific export account (if connected to accounting software like QuickBooks, NetSuite, Xero, etc.). -Update the card to pull recent transactions. -Unassign the card (note: unassigning deletes unsubmitted expenses on draft reports in the cardholder’s account). -![Manage the card on the card details page]({{site.url}}/assets/images/commfeed/commfeed-07.png){:width="100%"} - -{% include faq-begin.md %} +- **From the beginning:** Imports all available transactions (typically 30-90 days). +- **Custom start date:** Allows you to specify a date. + +![Select your transaction start date]({{site.url}}/assets/images/commfeed/commfeed-05-updated.png){:width="100%"} -## My commercial feed is connected. Why is a specific card not appearing for assignment? -Cards appear for assignment if they’re active and have at least one recent transaction. If a card meeting these criteria doesn’t appear, contact your account manager or message concierge@expensify.com. +Review the details and click **Assign card**. Transactions will import immediately. -## Is there an extra fee for using commercial feeds? -No, commercial feed setup is included in the Control plan. +![Double check the selections and assign the card]({{site.url}}/assets/images/commfeed/commfeed-06-updated.png){:width="100%"} -## What’s the difference between a direct feed and commercial feed? -Direct feeds use login credentials for quick setup, but can require re-authenticating from time to time. Commercial feeds require bank involvement for setup but offer the most reliable connection. +# Managing Cards +Once a card is assigned, you can manage its settings by navigating to **Settings > Workspaces > [your workspace] > Company cards** and selecting the assigned card. -## I have a Small Business Amex account. Am I eligible to set up a commercial feed? -Small Business or Triumph Amex accounts may not be eligible for a commercial feed and might need to use an Amex direct feed. +## Available Card Management Actions: +- **Rename the Card**: Change the card name for easier identification. +- **Set a Specific Export Account**: If connected to accounting software like QuickBooks, NetSuite, or Xero, you can assign a unique export account for this card. +- **Update Transactions**: Manually refresh the card feed to pull in the latest transactions. +- **Unassign the Card**: Removing a card unassigns it from the employee and deletes unsubmitted expenses from draft reports in their account. -## Are commercial feeds the best option if my bank isn’t one where Expensify supports direct feeds? -Yes. If direct feeds are not available for your bank, commercial feeds are the best option for importing company card transactions. Currently, Expensify supports direct feeds for: -American Express -Bank of America -Brex -Capital One -Chase -Citibank -Stripe -Wells Fargo +![Manage the card on the card details page]({{site.url}}/assets/images/commfeed/commfeed-07-updated.png){:width="100%"} +# FAQ -{% include faq-end.md %} +## My commercial feed is connected. Why is a specific card not appearing for assignment? +Cards appear for assignment if they’re active and have at least one recent transaction. If a card meeting these criteria doesn’t appear, contact your account manager or message concierge@expensify.com. +## Is there an extra fee for using commercial feeds? +No, commercial feed setup is included in the Control plan. +## What’s the difference between a direct feed and commercial feed? +Direct feeds use login credentials for quick setup, but can require re-authenticating from time to time. Commercial feeds require bank involvement for setup but offer the most reliable connection. diff --git a/fastlane/Fastfile b/fastlane/Fastfile index be90ce55ffaa0..b9930ca92324e 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -332,7 +332,7 @@ def setupIOSSigningCertificate() ) import_certificate( - certificate_path: "./ios/Certificates.p12", + certificate_path: "./Certificates.p12", keychain_name: "ios-build.keychain", keychain_password: keychain_password ) @@ -346,11 +346,11 @@ platform :ios do setupIOSSigningCertificate() install_provisioning_profile( - path: "./ios/NewApp_AppStore.mobileprovision" + path: "./NewApp_AppStore.mobileprovision" ) install_provisioning_profile( - path: "./ios/NewApp_AppStore_Notification_Service.mobileprovision" + path: "./NewApp_AppStore_Notification_Service.mobileprovision" ) build_app( @@ -478,11 +478,11 @@ platform :ios do setupIOSSigningCertificate() install_provisioning_profile( - path: "./ios/NewApp_AdHoc.mobileprovision" + path: "./NewApp_AdHoc.mobileprovision" ) install_provisioning_profile( - path: "./ios/NewApp_AdHoc_Notification_Service.mobileprovision" + path: "./NewApp_AdHoc_Notification_Service.mobileprovision" ) build_app( @@ -520,7 +520,7 @@ platform :ios do lane :upload_testflight do upload_to_testflight( app_identifier: "com.chat.expensify.chat", - api_key_path: "./ios/ios-fastlane-json-key.json", + api_key_path: "./ios-fastlane-json-key.json", distribute_external: true, notify_external_testers: true, changelog: "Thank you for beta testing New Expensify, this version includes bug fixes and improvements.", @@ -554,7 +554,7 @@ platform :ios do lane :upload_testflight_hybrid do upload_to_testflight( app_identifier: "com.expensify.expensifylite", - api_key_path: "./ios/ios-fastlane-json-key.json", + api_key_path: "./ios-fastlane-json-key.json", distribute_external: true, notify_external_testers: true, reject_build_waiting_for_review: true, @@ -590,7 +590,7 @@ platform :ios do lane :submit_for_review do deliver( app_identifier: "com.chat.expensify.chat", - api_key_path: "./ios/ios-fastlane-json-key.json", + api_key_path: "./ios-fastlane-json-key.json", # Skip HTMl report verification force: true, @@ -674,7 +674,7 @@ platform :ios do lane :submit_hybrid_for_rollout do deliver( app_identifier: "com.expensify.expensifylite", - api_key_path: "./ios/ios-fastlane-json-key.json", + api_key_path: "./ios-fastlane-json-key.json", # Skip HTML report verification force: true, diff --git a/ios/Certificates.p12.gpg b/ios/Certificates.p12.gpg deleted file mode 100644 index 91f827416367d1f9fb62b0ff724541e68fb7e9a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3323 zcmV1#dp#^Rp~6TY1SVYx5u zorL*jw3R~P0BmY8FAv+@s{MZr=BR8QN|TI#p!V*LiXPxaenR0!Wd%!edV6tTseewb zD$t0_^!Z0tYgVINV{VG2jD+@$NkDF4M;nvFdUUqp>S97f;SA#tv9-J6*$6zk!eWZTfv+_UMF!gTm9kyQf3_vNL*H zo?C2#ri61fFf^_3jvECEod=}PsIz!r_2IET|23rUGg8@6DY^UL`-23eZxCy&NJd71 zXNk6k$VS36nvF3(BX47`zBU&9=UvkNk!))l;+hs%fbarU%?A%cj=glKDS?aaH~5E= z0@}%D!}y9Wig8EpQ&}uw^c87^CIPtL)DL*lUcH2aJ^iT0a}#L=@BPUmj~8!WbM-8h zT@bGighG*b8PwG%A$euDQz=6A3`_%{%{#LyKU3z>+Dx7hKu#zTLw!>A)WzZ-B7xKWA8 zBi0v6=j@aFWT-*SA;(jVZ~a@$!o4oJ!~XV^B2Fs{3pLUt;_T+A%dF8OW1)+4a^2XL zR_$4je08h&U+-d~JJe2gGY3#B5SF>`?9rPbFlP1t(t^26M_U>=@e6cso%iCcqz&~P zDHM4-TQEZ;+x94|_!sd=w(A+AAUja4C~S69WrIB@wtFvbFoxdFxR*#5;u_T5tYV+N zUq)VVF0}omAoLyU_WM4_F@ejZM5239ms>Rrb&`mz#mfw{7oUu)y1aH>J&paT%>d(E zkqe<02-UDqslpf2%=%>oh%&T<&TvbV(&>Wk#73r0mGoj7$^=Yj7WsTRsoO@J*uzVK5? zIe!Pd))e6XRlGaTHW&$J>%t7l^RELtDLLW3!d&4nzrS#Bl(P3%e$DVCult^tQ+u5d zGZE)^QlV%B28qjk4}uTZm(t$M>Xg*zdnJCIy| z+`WC8Ug&X{&@4-qJ)p0FYC;UsQS)Yg9hgb}RuMT^NOPwZL9FgV?EI_rNRIVrKSgIQtPMgYQ^Y zYr-1!Ztgl%doW#aZDwxkzNqUiY-woAPBa+GJl02`{nuw;Lj&j8pIUNkySKWiAN$rs zxeN6J70D5f)+vf~An)Su#ZZnug{=NZOq;5f-NO3O(sI>1fimPf z8UB$<)t~F!ywk4BMNBzj3CGVjW87;IYAB0xz{kCEG#&5`-eUwT>~JT{>I@#hFdUjt zbs(PsLAY04mgdO1rH&9~`hl8BSWr0rn({L=U3CV}i8zj?@&abo`Vord(g0;MPI)Ct zRICv}0EV&*QW`HpW@Z(udI!>laf?S=^&~JQ%Z7&zx$2lJQbs%!_il#dsK8|7*A&8S zFDQaR50N^e(zAtK4T1KDn#MX1Z6+*n~NA$$@+ z%dsy=oppNesmcl+;ak=C8&thmlUGV5dnP=WUWizmJc~11`aEnDa`_)_$n>7D8>e{* zc2@Q9|GTXnD1#L$h*q)zY~c?WQgqY8^l73wW;)>F)5Jn6|3XBJ55U4&oa)XXNt&Z= ze!-Jj70hOVCG})CLwmvi-$)ViOYwlt>1K!)d{2750Ni%p$By{{+nHe*9LZ<>b=WUi zx_!L2;bwKLqO{$@$Pwr#At_<%v$93H^;Fk5F3Va~x{BNWy}oYRFO8S!4Ou7|yZr*z z3g%KCrhft@!z}W;a)q=Ji@@ta=619$=%*$mtJc)P*VrMsxVy!tL{8lziL#< z0gjRbcA_0yNLmsVeIrBVIov)6_zjn*d0!<-!Oc81{Y$?Lv?;zpfx`6&@>PC&~>}>o?WH0$hyJC0UM#MvPnS(A&Z4j`W zf8lnqghmOIpglKTNioq0t~QyjOCoYhh%V@8$-zS2i#zh0E$1HvX`0*X-p-v&iIxp| zn22o`wL}gBJ?NtQNEPNA`FOc?OKq&2M{ypTMN4s4rSdf7JA*F53@L*$F*ZNh#0_F? zbMt(E<(e4?7s*mRsn602M*p+2%Nv-foV+4eb;j~R+t*||^dfiM=RKE$iE5#!T@u28 z+*W#e4UhZ@8VURld(^WHm|L%TOZe-Eb;SbQz>GTmE8nvv#zL(9P4$Whzly7>o)w7_ z<7KrJ9nJKK}CA)8KUDy4RV0C?;7Y)We(=H_-J*m+j4TgkY|d14LitSNk- zTxzbPQXwzA<;mLjmh06)Tqt5yh8dXe5qUnvwW6)#y+$wD9=vFamYoe2MCUbf*7JAO zoN;@4>}wB)3FX+0k0}u3Gg)nvaL5D6kzBMc7EWZKwOC18KgaH1{ja&-mGw9_+mu4z z+pLEH^0($?Odg=E50g2k2tGLn3b2io$pAnZhy(^|#+oxMOvEZWAF$I`vf9!d^U0Gv zW(wop3{|2+666-xch2-MAov)^!vkX|(C)@{w1^Ba?BFOmE~_juzN=z z+gcure@!HB4hD;>DB<^2f7KgRel6+U8fjY8RH+Kh|^ZwUe$IAeXEyG@6T6IrCNnWe1gCxe{w1d+Pn$oxxRJI85kG%^1YRa z&xZh+k+muExx&lDyCkvV>U0VPxu^1I{APC`^Pb*mf;aS^B$(4I{7_#C&V)zM-|x&L zw86C0KBc3ep5)^oSw=1vh?&H<1wKyjnK|yT87;X4L=!Au_6zSs`SiIJRx-%)NM}{S zSXZLtJ1pJ~>2t6b$0Qz0s;=4k9x>!lNSmrS8DU2jjmWlogHbfie6JhpgfbX=T}4Hk FZh$)`ewP3M diff --git a/ios/NewApp_AdHoc.mobileprovision.gpg b/ios/NewApp_AdHoc.mobileprovision.gpg deleted file mode 100644 index 567a867981e614a4bb9aa8027d75c3356e71a5f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12018 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 diff --git a/ios/NewApp_AdHoc_Share_Extension.mobileprovision.gpg b/ios/NewApp_AdHoc_Share_Extension.mobileprovision.gpg deleted file mode 100644 index c9b3eb213f797c03d25f69b8f97a700b5d0ed71f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11124 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 GduK@g=(H^W diff --git a/ios/NewApp_AppStore.mobileprovision.gpg b/ios/NewApp_AppStore.mobileprovision.gpg deleted file mode 100644 index 22624c6f41d65aa1c8e3d95780df57fe3c09bbe0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8310 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 AA^-pY diff --git a/ios/NewApp_AppStore_Notification_Service.mobileprovision.gpg b/ios/NewApp_AppStore_Notification_Service.mobileprovision.gpg deleted file mode 100644 index 503a096f1726bbc0ff3c947a663260e710255091..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7931 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 diff --git a/ios/NewApp_Development.mobileprovision.gpg b/ios/NewApp_Development.mobileprovision.gpg deleted file mode 100644 index 34f034752b7f0db11c35d469692b50017de91c02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26075 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 diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 44b35c2d63fe5..55087defb4c0b 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -44,7 +44,7 @@ CFBundleVersion - 9.0.94.1 + 9.0.94.7 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index acfce5c2e6758..7fa6784a30e64 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.94.1 + 9.0.94.7 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 2e661cccef7fc..d51f8fd912c0e 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.94 CFBundleVersion - 9.0.94.1 + 9.0.94.7 NSExtension NSExtensionPointIdentifier diff --git a/ios/ios-fastlane-json-key.json.gpg b/ios/ios-fastlane-json-key.json.gpg deleted file mode 100644 index 06d2109da0802..0000000000000 --- a/ios/ios-fastlane-json-key.json.gpg +++ /dev/null @@ -1,2 +0,0 @@ -Œ  H46 )‰ÕëÒÀ¯€E$é¨R `L¤IuÌ0Ø„ç¹<;ä\ôÏÕ F•I™ßιíú¾{[›·‡ 3¢üñEįL?ʼëö-VŒÿ¢6vÉW¶6Œ}뾆Àc¢kýž¶ö†)×â>Oå¥#ôƒý#E:AÖAîÝx|ÄßüFQeŽäõ"6õQ Єâ 7ØôY¿Q”• µöö´Ó+Ãp³cöáÃ8~÷’‰ó—麜Ǔb‚D‡Y›·žRA¶ŒœÀ!ˆ\T åí`¢Èñàá¶-ÊØyGþy>I»G‰ÞêÿñÕU ¹Ö e·¶¡jYC¤<¿î H}„—ÿ?µ9J`Î2 ½T -‹ýè,ža¸½á> "J(,«}ž€v;ÝF©>ià„9›Ó£ó©êŠüìÑ aQŸÒÛr˜Nð¨;m¼M­ÝùMŠ_ýD3LÍ–êsVDa1nä™h½£®Æ9¡ÉXœ†º9 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 9087c78c68b97..db7a86b0b7bad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.94-1", + "version": "9.0.94-7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.94-1", + "version": "9.0.94-7", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -99,7 +99,7 @@ "react-native-launch-arguments": "^4.0.2", "react-native-localize": "^2.2.6", "react-native-modal": "^13.0.0", - "react-native-onyx": "2.0.89", + "react-native-onyx": "2.0.92", "react-native-pager-view": "6.5.1", "react-native-pdf": "6.7.3", "react-native-performance": "^5.1.0", @@ -32336,9 +32336,9 @@ } }, "node_modules/react-native-onyx": { - "version": "2.0.89", - "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-2.0.89.tgz", - "integrity": "sha512-JzXjas0UNnYqTH4XD2Qfs64kBJBvHQ7HIIglieL1+Gto7eANyFRUpr0uRM3BlONinSPD/1xWZIurYAJtHuM5dg==", + "version": "2.0.92", + "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-2.0.92.tgz", + "integrity": "sha512-6StFOp3j4DC3gsY5Cl1qcbZ8mXL1RUMyzDf4l4im/4QlF6+bSpOHdYDZZjrUddbO/i1PA5ktUnAK4NM/JQ+BZg==", "license": "MIT", "dependencies": { "ascii-table": "0.0.9", diff --git a/package.json b/package.json index e250de3bce5a5..b5dccb4c03d11 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.94-1", + "version": "9.0.94-7", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", @@ -166,7 +166,7 @@ "react-native-launch-arguments": "^4.0.2", "react-native-localize": "^2.2.6", "react-native-modal": "^13.0.0", - "react-native-onyx": "2.0.89", + "react-native-onyx": "2.0.92", "react-native-pager-view": "6.5.1", "react-native-pdf": "6.7.3", "react-native-performance": "^5.1.0", diff --git a/src/CONST.ts b/src/CONST.ts index d642c4586d19b..c77166e6cbd70 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1,6 +1,9 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {add as dateAdd} from 'date-fns'; import {sub as dateSubtract} from 'date-fns/sub'; +// eslint-disable-next-line lodash/import-scope +import type {Dictionary} from 'lodash'; +import invertBy from 'lodash/invertBy'; import Config from 'react-native-config'; import * as KeyCommand from 'react-native-key-command'; import type {ValueOf} from 'type-fest'; @@ -16,6 +19,8 @@ import type PlaidBankAccount from './types/onyx/PlaidBankAccount'; const EMPTY_ARRAY = Object.freeze([]); const EMPTY_OBJECT = Object.freeze({}); +const DEFAULT_NUMBER_ID = 0; + const CLOUDFRONT_DOMAIN = 'cloudfront.net'; const CLOUDFRONT_URL = `https://d2k5nsl2zxldvw.${CLOUDFRONT_DOMAIN}`; const ACTIVE_EXPENSIFY_URL = addTrailingForwardSlash(Config?.NEW_EXPENSIFY_URL ?? 'https://new.expensify.com'); @@ -159,7 +164,7 @@ const onboardingEmployerOrSubmitMessage: OnboardingMessage = { '\n' + 'Here’s how to submit an expense:\n' + '\n' + - '1. Press the button.\n' + + '1. Click the green *+* button.\n' + '2. Choose *Create expense*.\n' + '3. Enter an amount or scan a receipt.\n' + '4. Add your reimburser to the request.\n' + @@ -182,7 +187,7 @@ const combinedTrackSubmitOnboardingEmployerOrSubmitMessage: OnboardingMessage = '\n' + 'Here’s how to submit an expense:\n' + '\n' + - '1. Press the button\n' + + '1. Click the green *+* button.\n' + '2. Choose *Create expense*.\n' + '3. Enter an amount or scan a receipt.\n' + '4. Add your reimburser to the request.\n' + @@ -206,7 +211,7 @@ const onboardingPersonalSpendMessage: OnboardingMessage = { '\n' + 'Here’s how to track an expense:\n' + '\n' + - '1. Press the button.\n' + + '1. Click the green *+* button.\n' + '2. Choose *Create expense*.\n' + '3. Enter an amount or scan a receipt.\n' + '4. Click "Just track it (don\'t submit it)".\n' + @@ -229,7 +234,7 @@ const combinedTrackSubmitOnboardingPersonalSpendMessage: OnboardingMessage = { '\n' + 'Here’s how to track an expense:\n' + '\n' + - '1. Press the button.\n' + + '1. Click the green *+* button.\n' + '2. Choose *Create expense*.\n' + '3. Enter an amount or scan a receipt.\n' + '4. Click "Just track it (don\'t submit it)".\n' + @@ -943,7 +948,7 @@ const CONST = { CLOUDFRONT_URL, EMPTY_ARRAY, EMPTY_OBJECT, - DEFAULT_NUMBER_ID: 0, + DEFAULT_NUMBER_ID, USE_EXPENSIFY_URL, EXPENSIFY_URL, GOOGLE_MEET_URL_ANDROID: 'https://meet.google.com', @@ -2171,6 +2176,31 @@ const CONST = { '_vietNam', ] as string[], + NSQS_EXPORT_DATE: { + LAST_EXPENSE: 'LAST_EXPENSE', + EXPORTED: 'EXPORTED', + SUBMITTED: 'SUBMITTED', + }, + + NSQS_INTEGRATION_ENTITY_MAP_TYPES: { + NETSUITE_DEFAULT: 'NETSUITE_DEFAULT', + REPORT_FIELD: 'REPORT_FIELD', + TAG: 'TAG', + }, + + NSQS_CONFIG: { + AUTO_SYNC: 'autoSync', + SYNC_OPTIONS: { + MAPPING: { + CUSTOMERS: 'syncOptions.mapping.customers', + PROJECTS: 'syncOptions.mapping.projects', + }, + }, + EXPORTER: 'exporter', + EXPORT_DATE: 'exportDate', + APPROVAL_ACCOUNT: 'approvalAccount', + }, + QUICKBOOKS_EXPORT_DATE: { LAST_EXPENSE: 'LAST_EXPENSE', REPORT_EXPORTED: 'REPORT_EXPORTED', @@ -2657,17 +2687,20 @@ const CONST = { QBD: 'quickbooksDesktop', XERO: 'xero', NETSUITE: 'netsuite', + NSQS: 'netsuiteQuickStart', SAGE_INTACCT: 'intacct', }, ROUTE: { QBO: 'quickbooks-online', XERO: 'xero', NETSUITE: 'netsuite', + NSQS: 'nsqs', SAGE_INTACCT: 'sage-intacct', QBD: 'quickbooks-desktop', }, NAME_USER_FRIENDLY: { netsuite: 'NetSuite', + netsuiteQuickStart: 'NSQS', quickbooksOnline: 'QuickBooks Online', quickbooksDesktop: 'QuickBooks Desktop', xero: 'Xero', @@ -2745,6 +2778,12 @@ const CONST = { NETSUITE_SYNC_EXPENSIFY_REIMBURSED_REPORTS: 'netSuiteSyncExpensifyReimbursedReports', NETSUITE_SYNC_IMPORT_VENDORS_TITLE: 'netSuiteImportVendorsTitle', NETSUITE_SYNC_IMPORT_CUSTOM_LISTS_TITLE: 'netSuiteImportCustomListsTitle', + NSQS_SYNC_CONNECTION: 'nsqsSyncConnection', + NSQS_SYNC_ACCOUNTS: 'nsqsSyncAccounts', + NSQS_SYNC_EMPLOYEES: 'nsqsSyncEmployees', + NSQS_SYNC_CUSTOMERS: 'nsqsSyncCustomers', + NSQS_SYNC_PROJECTS: 'nsqsSyncProjects', + NSQS_SYNC_CURRENCY: 'nsqsSyncCurrency', SAGE_INTACCT_SYNC_CHECK_CONNECTION: 'intacctCheckConnection', SAGE_INTACCT_SYNC_IMPORT_TITLE: 'intacctImportTitle', SAGE_INTACCT_SYNC_IMPORT_DATA: 'intacctImportData', @@ -2753,6 +2792,19 @@ const CONST = { SAGE_INTACCT_SYNC_IMPORT_SYNC_REIMBURSED_REPORTS: 'intacctImportSyncBillPayments', }, SYNC_STAGE_TIMEOUT_MINUTES: 20, + + // Map each connection to its designated display connection + get MULTI_CONNECTIONS_MAPPING() { + return { + [this.NAME.NETSUITE]: this.NAME.NETSUITE, + [this.NAME.NSQS]: this.NAME.NETSUITE, + } as Record, ValueOf | undefined>; + }, + + // Get linked connections by the designated display connection + get MULTI_CONNECTIONS_MAPPING_INVERTED() { + return invertBy(this.MULTI_CONNECTIONS_MAPPING) as Dictionary> | undefined>; + }, }, ACCESS_VARIANTS: { PAID: 'paid', @@ -5043,6 +5095,7 @@ const CONST = { quickbooksOnline: 'QuickBooks Online', xero: 'Xero', netsuite: 'NetSuite', + netsuiteQuickStart: 'NSQS', intacct: 'Sage Intacct', quickbooksDesktop: 'QuickBooks Desktop', }, @@ -5192,7 +5245,7 @@ const CONST = { '\n' + 'Here’s how to start a chat:\n' + '\n' + - '1. Press the button.\n' + + '1. Click the green *+* button.\n' + '2. Choose *Start chat*.\n' + '3. Enter emails or phone numbers.\n' + '\n' + @@ -5209,7 +5262,7 @@ const CONST = { '\n' + 'Here’s how to request money:\n' + '\n' + - '1. Press the button\n' + + '1. Click the green *+* button.\n' + '2. Choose *Start chat*.\n' + '3. Enter any email, SMS, or name of who you want to split with.\n' + '4. From within the chat, click the *+* button on the message bar, and click *Split expense*.\n' + @@ -5244,7 +5297,7 @@ const CONST = { '\n' + 'Here’s how to submit an expense:\n' + '\n' + - '1. Press the button.\n' + + '1. Click the green *+* button.\n' + '2. Choose *Create expense*.\n' + '3. Enter an amount or scan a receipt.\n' + '4. Add your reimburser to the request.\n' + @@ -6573,6 +6626,8 @@ const CONST = { EXPENSE: 'EXPENSE', INVOICE: 'INVOICE', }, + SKIPPABLE_COLLECTION_MEMBER_IDS: [String(DEFAULT_NUMBER_ID), '-1', 'undefined', 'null', 'NaN'] as string[], + SETUP_SPECIALIST_LOGIN: 'Setup Specialist', } as const; type Country = keyof typeof CONST.ALL_COUNTRIES; diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 54b7da704cd1e..1fb84c3dd9cf4 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -727,6 +727,8 @@ const ONYXKEYS = { NETSUITE_TOKEN_INPUT_FORM_DRAFT: 'netsuiteTokenInputFormDraft', NETSUITE_CUSTOM_FORM_ID_FORM: 'netsuiteCustomFormIDForm', NETSUITE_CUSTOM_FORM_ID_FORM_DRAFT: 'netsuiteCustomFormIDFormDraft', + NSQS_OAUTH2_FORM: 'nsqsOAuth2Form', + NSQS_OAUTH2_FORM_DRAFT: 'nsqsOAuth2FormDraft', SAGE_INTACCT_DIMENSION_TYPE_FORM: 'sageIntacctDimensionTypeForm', SAGE_INTACCT_DIMENSION_TYPE_FORM_DRAFT: 'sageIntacctDimensionTypeFormDraft', SEARCH_ADVANCED_FILTERS_FORM: 'searchAdvancedFiltersForm', @@ -837,6 +839,7 @@ type OnyxFormValuesMapping = { [ONYXKEYS.FORMS.NETSUITE_CUSTOM_SEGMENT_ADD_FORM]: FormTypes.NetSuiteCustomFieldForm; [ONYXKEYS.FORMS.NETSUITE_TOKEN_INPUT_FORM]: FormTypes.NetSuiteTokenInputForm; [ONYXKEYS.FORMS.NETSUITE_CUSTOM_FORM_ID_FORM]: FormTypes.NetSuiteCustomFormIDForm; + [ONYXKEYS.FORMS.NSQS_OAUTH2_FORM]: FormTypes.NSQSOAuth2Form; [ONYXKEYS.FORMS.SAGE_INTACCT_DIMENSION_TYPE_FORM]: FormTypes.SageIntacctDimensionForm; [ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM]: FormTypes.SearchAdvancedFiltersForm; [ONYXKEYS.FORMS.TEXT_PICKER_MODAL_FORM]: FormTypes.TextPickerModalForm; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 393085ab43848..87664b718974d 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1114,6 +1114,28 @@ const ROUTES = { getRoute: (policyID: string, connection?: ValueOf) => `settings/workspaces/${policyID}/accounting/${connection as string}/card-reconciliation/account` as const, }, + WORKSPACE_ACCOUNTING_MULTI_CONNECTION_SELECTOR: { + route: 'settings/workspaces/:policyID/accounting/:connection/connection-selector', + getRoute: ( + policyID: string, + connection: ValueOf, + integrationToDisconnect?: ConnectionName, + shouldDisconnectIntegrationBeforeConnecting?: boolean, + ) => { + const searchParams = new URLSearchParams(); + + if (integrationToDisconnect) { + searchParams.append('integrationToDisconnect', integrationToDisconnect); + } + if (shouldDisconnectIntegrationBeforeConnecting !== undefined) { + searchParams.append('shouldDisconnectIntegrationBeforeConnecting', shouldDisconnectIntegrationBeforeConnecting.toString()); + } + + const queryParams = searchParams.size ? `?${searchParams.toString()}` : ''; + + return `settings/workspaces/${policyID}/accounting/${connection}/connection-selector${queryParams}` as const; + }, + }, WORKSPACE_CATEGORIES: { route: 'settings/workspaces/:policyID/categories', getRoute: (policyID: string | undefined) => { @@ -1942,6 +1964,50 @@ const ROUTES = { route: 'settings/workspaces/:policyID/connections/netsuite/advanced/autosync/accounting-method', getRoute: (policyID: string) => `settings/workspaces/${policyID}/connections/netsuite/advanced/autosync/accounting-method` as const, }, + POLICY_ACCOUNTING_NSQS_SETUP: { + route: 'settings/workspaces/:policyID/accounting/nsqs/setup', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/nsqs/setup` as const, + }, + POLICY_ACCOUNTING_NSQS_IMPORT: { + route: 'settings/workspaces/:policyID/accounting/nsqs/import', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/nsqs/import` as const, + }, + POLICY_ACCOUNTING_NSQS_IMPORT_CUSTOMERS: { + route: 'settings/workspaces/:policyID/accounting/nsqs/import/customers', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/nsqs/import/customers` as const, + }, + POLICY_ACCOUNTING_NSQS_IMPORT_CUSTOMERS_DISPLAYED_AS: { + route: 'settings/workspaces/:policyID/accounting/nsqs/import/customers/displayed-as', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/nsqs/import/customers/displayed-as` as const, + }, + POLICY_ACCOUNTING_NSQS_IMPORT_PROJECTS: { + route: 'settings/workspaces/:policyID/accounting/nsqs/import/projects', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/nsqs/import/projects` as const, + }, + POLICY_ACCOUNTING_NSQS_IMPORT_PROJECTS_DISPLAYED_AS: { + route: 'settings/workspaces/:policyID/accounting/nsqs/import/projects/displayed-as', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/nsqs/import/projects/displayed-as` as const, + }, + POLICY_ACCOUNTING_NSQS_EXPORT: { + route: 'settings/workspaces/:policyID/accounting/nsqs/export', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/nsqs/export` as const, + }, + POLICY_ACCOUNTING_NSQS_EXPORT_PREFERRED_EXPORTER: { + route: 'settings/workspaces/:policyID/accounting/nsqs/export/preferred-exporter', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/nsqs/export/preferred-exporter` as const, + }, + POLICY_ACCOUNTING_NSQS_EXPORT_DATE: { + route: 'settings/workspaces/:policyID/accounting/nsqs/export/date', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/nsqs/export/date` as const, + }, + POLICY_ACCOUNTING_NSQS_ADVANCED: { + route: 'settings/workspaces/:policyID/accounting/nsqs/advanced', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/nsqs/advanced` as const, + }, + POLICY_ACCOUNTING_NSQS_ADVANCED_APPROVAL_ACCOUNT: { + route: 'settings/workspaces/:policyID/accounting/nsqs/advanced/approval-account', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/nsqs/advanced/approval-account` as const, + }, POLICY_ACCOUNTING_SAGE_INTACCT_PREREQUISITES: { route: 'settings/workspaces/:policyID/accounting/sage-intacct/prerequisites', getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/prerequisites` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 04bb3c6297ba9..4ee20f34cf166 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -431,6 +431,17 @@ const SCREENS = { NETSUITE_CUSTOM_FORM_ID: 'Policy_Accounting_NetSuite_Custom_Form_ID', NETSUITE_AUTO_SYNC: 'Policy_Accounting_NetSuite_Auto_Sync', NETSUITE_ACCOUNTING_METHOD: 'Policy_Accounting_NetSuite_Accounting_Method', + NSQS_SETUP: 'Policy_Accounting_NSQS_Setup', + NSQS_IMPORT: 'Policy_Accounting_NSQS_Import', + NSQS_IMPORT_CUSTOMERS: 'Policy_Accounting_NSQS_Import_Customers', + NSQS_IMPORT_CUSTOMERS_DISPLAYED_AS: 'Policy_Accounting_NSQS_Import_Customers_Displayed_As', + NSQS_IMPORT_PROJECTS: 'Policy_Accounting_NSQS_Import_Projects', + NSQS_IMPORT_PROJECTS_DISPLAYED_AS: 'Policy_Accounting_NSQS_Import_Projects_Displayed_As', + NSQS_EXPORT: 'Policy_Accounting_NSQS_Export', + NSQS_EXPORT_PREFERRED_EXPORTER: 'Policy_Accounting_NSQS_Export_Preferred_Exporter', + NSQS_EXPORT_DATE: 'Policy_Accounting_NSQS_Export_Date', + NSQS_ADVANCED: 'Policy_Accounting_NSQS_Advanced', + NSQS_ADVANCED_APPROVAL_ACCOUNT: 'Policy_Accounting_NSQS_Advanced_Approval_Account', SAGE_INTACCT_PREREQUISITES: 'Policy_Accounting_Sage_Intacct_Prerequisites', ENTER_SAGE_INTACCT_CREDENTIALS: 'Policy_Enter_Sage_Intacct_Credentials', EXISTING_SAGE_INTACCT_CONNECTIONS: 'Policy_Existing_Sage_Intacct_Connections', @@ -454,6 +465,7 @@ const SCREENS = { SAGE_INTACCT_PAYMENT_ACCOUNT: 'Policy_Accounting_Sage_Intacct_Payment_Account', CARD_RECONCILIATION: 'Policy_Accounting_Card_Reconciliation', RECONCILIATION_ACCOUNT_SETTINGS: 'Policy_Accounting_Reconciliation_Account_Settings', + MULTI_CONNECTION_SELECTOR: 'Policy_Accounting_Multi_Connection_Selector', }, INITIAL: 'Workspace_Initial', PROFILE: 'Workspace_Profile', diff --git a/src/components/ArchivedReportFooter.tsx b/src/components/ArchivedReportFooter.tsx index fc5c779586359..75f9ac9859661 100644 --- a/src/components/ArchivedReportFooter.tsx +++ b/src/components/ArchivedReportFooter.tsx @@ -38,7 +38,7 @@ function ArchivedReportFooter({report}: ArchivedReportFooterProps) { const shouldRenderHTML = archiveReason !== CONST.REPORT.ARCHIVE_REASON.DEFAULT && archiveReason !== CONST.REPORT.ARCHIVE_REASON.BOOKING_END_DATE_HAS_PASSED; - let policyName = ReportUtils.getPolicyName(report); + let policyName = ReportUtils.getPolicyName({report}); if (archiveReason === CONST.REPORT.ARCHIVE_REASON.INVOICE_RECEIVER_POLICY_DELETED) { policyName = originalMessage?.receiverPolicyName ?? ''; diff --git a/src/components/AvatarWithDisplayName.tsx b/src/components/AvatarWithDisplayName.tsx index 98e6dd6268839..fec3fa6fa6fd3 100644 --- a/src/components/AvatarWithDisplayName.tsx +++ b/src/components/AvatarWithDisplayName.tsx @@ -71,7 +71,7 @@ function AvatarWithDisplayName({ const [invoiceReceiverPolicy] = useOnyx( `${ONYXKEYS.COLLECTION.POLICY}${parentReport?.invoiceReceiver && 'policyID' in parentReport.invoiceReceiver ? parentReport.invoiceReceiver.policyID : -1}`, ); - const title = ReportUtils.getReportName(report, undefined, undefined, undefined, invoiceReceiverPolicy); + const title = ReportUtils.getReportName(report, invoiceReceiverPolicy); const subtitle = ReportUtils.getChatRoomSubtitle(report); const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(report); const isMoneyRequestOrReport = diff --git a/src/components/BlockingViews/FullPageNotFoundView.tsx b/src/components/BlockingViews/FullPageNotFoundView.tsx index ad1a659e6d9fa..7c4ce77c8a99f 100644 --- a/src/components/BlockingViews/FullPageNotFoundView.tsx +++ b/src/components/BlockingViews/FullPageNotFoundView.tsx @@ -1,5 +1,6 @@ import React from 'react'; import {View} from 'react-native'; +import type {StyleProp, TextStyle} from 'react-native'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Illustrations from '@components/Icon/Illustrations'; import useLocalize from '@hooks/useLocalize'; @@ -43,6 +44,12 @@ type FullPageNotFoundViewProps = { /** Whether we should force the full page view */ shouldForceFullScreen?: boolean; + + /** The style of the subtitle message */ + subtitleStyle?: StyleProp; + + /** Whether we should display the button that opens new SearchRouter */ + shouldDisplaySearchRouter?: boolean; }; // eslint-disable-next-line rulesdir/no-negated-variables @@ -58,6 +65,8 @@ function FullPageNotFoundView({ shouldShowBackButton = true, onLinkPress = () => Navigation.dismissModal(), shouldForceFullScreen = false, + subtitleStyle, + shouldDisplaySearchRouter, }: FullPageNotFoundViewProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -68,6 +77,7 @@ function FullPageNotFoundView({ diff --git a/src/components/BrokenConnectionDescription.tsx b/src/components/BrokenConnectionDescription.tsx index ed5ecf41078a4..bc628458c46c0 100644 --- a/src/components/BrokenConnectionDescription.tsx +++ b/src/components/BrokenConnectionDescription.tsx @@ -56,7 +56,7 @@ function BrokenConnectionDescription({transactionID, policy, report}: BrokenConn ); } - if (isReportApproved(report) || isReportManuallyReimbursed(report) || (isProcessingReport(report) && !isInstantSubmitEnabled(policy))) { + if (isReportApproved({report}) || isReportManuallyReimbursed(report) || (isProcessingReport(report) && !isInstantSubmitEnabled(policy))) { return translate('violations.memberBrokenConnectionError'); } diff --git a/src/components/ConnectToNSQSFlow/index.tsx b/src/components/ConnectToNSQSFlow/index.tsx new file mode 100644 index 0000000000000..87b32007f9f5e --- /dev/null +++ b/src/components/ConnectToNSQSFlow/index.tsx @@ -0,0 +1,15 @@ +import {useEffect} from 'react'; +import Navigation from '@libs/Navigation/Navigation'; +import ROUTES from '@src/ROUTES'; +import type {ConnectToNSQSFlowProps} from './types'; + +function ConnectToNSQSFlow({policyID}: ConnectToNSQSFlowProps) { + useEffect(() => { + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NSQS_SETUP.getRoute(policyID)); + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps + }, []); + + return null; +} + +export default ConnectToNSQSFlow; diff --git a/src/components/ConnectToNSQSFlow/types.ts b/src/components/ConnectToNSQSFlow/types.ts new file mode 100644 index 0000000000000..7a19bd321b994 --- /dev/null +++ b/src/components/ConnectToNSQSFlow/types.ts @@ -0,0 +1,10 @@ +import type {PolicyConnectionName} from '@src/types/onyx/Policy'; + +type ConnectToNSQSFlowProps = { + policyID: string; + shouldDisconnectIntegrationBeforeConnecting?: boolean; + integrationToDisconnect?: PolicyConnectionName; +}; + +// eslint-disable-next-line import/prefer-default-export +export type {ConnectToNSQSFlowProps}; diff --git a/src/components/ConnectToNetSuiteFlow/index.tsx b/src/components/ConnectToNetSuiteFlow/index.tsx index 7957896d4006e..1bf3712c0f018 100644 --- a/src/components/ConnectToNetSuiteFlow/index.tsx +++ b/src/components/ConnectToNetSuiteFlow/index.tsx @@ -18,7 +18,11 @@ function ConnectToNetSuiteFlow({policyID}: ConnectToNetSuiteFlowProps) { const {translate} = useLocalize(); const hasPoliciesConnectedToNetSuite = !!getAdminPoliciesConnectedToNetSuite()?.length; - const {shouldUseNarrowLayout} = useResponsiveLayout(); + + // We need to use isSmallScreenWidth instead of shouldUseNarrowLayout to use the correct modal type + // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth + const {isSmallScreenWidth} = useResponsiveLayout(); + const [isReuseConnectionsPopoverOpen, setIsReuseConnectionsPopoverOpen] = useState(false); const [reuseConnectionPopoverPosition, setReuseConnectionPopoverPosition] = useState({horizontal: 0, vertical: 0}); const {popoverAnchorRefs} = useAccountingContext(); @@ -57,7 +61,7 @@ function ConnectToNetSuiteFlow({policyID}: ConnectToNetSuiteFlowProps) { }, []); if (threeDotsMenuContainerRef) { - if (!shouldUseNarrowLayout) { + if (!isSmallScreenWidth) { threeDotsMenuContainerRef.current?.measureInWindow((x, y, width, height) => { const horizontal = x + width; const vertical = y + height; diff --git a/src/components/ConnectionLayout.tsx b/src/components/ConnectionLayout.tsx index 9a232e83fb976..c7bc37e38e3e2 100644 --- a/src/components/ConnectionLayout.tsx +++ b/src/components/ConnectionLayout.tsx @@ -5,7 +5,7 @@ import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; -import * as PolicyUtils from '@libs/PolicyUtils'; +import {getPolicy} from '@libs/PolicyUtils'; import type {AccessVariant} from '@pages/workspace/AccessOrNotFoundWrapper'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {TranslationPaths} from '@src/languages/types'; @@ -106,7 +106,7 @@ function ConnectionLayout({ }: ConnectionLayoutProps) { const {translate} = useLocalize(); - const policy = PolicyUtils.getPolicy(policyID); + const policy = getPolicy(policyID); const isConnectionEmpty = isEmpty(policy?.connections?.[connectionName]); const renderSelectionContent = useMemo( diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx index 332255e539951..12b515194928b 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx @@ -80,7 +80,6 @@ function BaseHTMLEngineProvider({textSelectable = false, children, enableExperim 'mention-user': HTMLElementModel.fromCustomModel({tagName: 'mention-user', contentModel: HTMLContentModel.textual}), 'mention-report': HTMLElementModel.fromCustomModel({tagName: 'mention-report', contentModel: HTMLContentModel.textual}), 'mention-here': HTMLElementModel.fromCustomModel({tagName: 'mention-here', contentModel: HTMLContentModel.textual}), - 'custom-emoji': HTMLElementModel.fromCustomModel({tagName: 'custom-emoji', contentModel: HTMLContentModel.textual}), 'next-step': HTMLElementModel.fromCustomModel({ tagName: 'next-step', mixedUAStyles: {...styles.textLabelSupporting, ...styles.lh16}, diff --git a/src/components/HTMLEngineProvider/CustomEmojiWithDefaultPressableAction.tsx b/src/components/HTMLEngineProvider/CustomEmojiWithDefaultPressableAction.tsx deleted file mode 100644 index 8cd33eab6c90a..0000000000000 --- a/src/components/HTMLEngineProvider/CustomEmojiWithDefaultPressableAction.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import type {ReactNode} from 'react'; -import React from 'react'; -import FloatingActionButtonAndPopover from '@pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover'; - -type CustomEmojiWithDefaultPressableActionProps = { - /* Key name identifying the emoji */ - emojiKey: string; - - /* Emoji content to render */ - children: ReactNode; -}; - -function CustomEmojiWithDefaultPressableAction({emojiKey, children}: CustomEmojiWithDefaultPressableActionProps) { - if (emojiKey === 'actionMenuIcon') { - return {children}; - } - - return children; -} - -export default CustomEmojiWithDefaultPressableAction; diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/CustomEmojiRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/CustomEmojiRenderer.tsx deleted file mode 100644 index dab8c89013dd4..0000000000000 --- a/src/components/HTMLEngineProvider/HTMLRenderers/CustomEmojiRenderer.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import type {FC} from 'react'; -import {View} from 'react-native'; -import type {CustomRendererProps, TPhrasing, TText} from 'react-native-render-html'; -import type {SvgProps} from 'react-native-svg'; -import GlobalCreateIcon from '@assets/images/customEmoji/global-create.svg'; -import CustomEmojiWithDefaultPressableAction from '@components/HTMLEngineProvider/CustomEmojiWithDefaultPressableAction'; -import ImageSVG from '@components/ImageSVG'; -import useThemeStyles from '@hooks/useThemeStyles'; -import variables from '@styles/variables'; - -const emojiMap: Record> = { - actionMenuIcon: GlobalCreateIcon, -}; - -function CustomEmojiRenderer({tnode}: CustomRendererProps) { - const styles = useThemeStyles(); - const emojiKey = tnode.attributes.emoji; - - if (emojiMap[emojiKey]) { - const image = ( - - - - ); - - if ('pressablewithdefaultaction' in tnode.attributes) { - return {image}; - } - - return image; - } - - return null; -} - -export default CustomEmojiRenderer; -export {emojiMap}; diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/index.ts b/src/components/HTMLEngineProvider/HTMLRenderers/index.ts index bcf3d4dfaf94d..91ed66f8b9311 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/index.ts +++ b/src/components/HTMLEngineProvider/HTMLRenderers/index.ts @@ -1,7 +1,6 @@ import type {CustomTagRendererRecord} from 'react-native-render-html'; import AnchorRenderer from './AnchorRenderer'; import CodeRenderer from './CodeRenderer'; -import CustomEmojiRenderer from './CustomEmojiRenderer'; import DeletedActionRenderer from './DeletedActionRenderer'; import EditedRenderer from './EditedRenderer'; import EmojiRenderer from './EmojiRenderer'; @@ -30,7 +29,6 @@ const HTMLEngineProviderComponentList: CustomTagRendererRecord = { 'mention-user': MentionUserRenderer, 'mention-report': MentionReportRenderer, 'mention-here': MentionHereRenderer, - 'custom-emoji': CustomEmojiRenderer, emoji: EmojiRenderer, 'next-step-email': NextStepEmailRenderer, 'deleted-action': DeletedActionRenderer, diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index 5cfa87d472da0..da402f612a2c2 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -113,6 +113,7 @@ import ImageCropSquareMask from '@assets/images/image-crop-square-mask.svg'; import Inbox from '@assets/images/inbox.svg'; import Info from '@assets/images/info.svg'; import NetSuiteSquare from '@assets/images/integrationicons/netsuite-icon-square.svg'; +import NSQSSquare from '@assets/images/integrationicons/netsuite-quickstart-icon-square.svg'; import QBDSquare from '@assets/images/integrationicons/qbd-icon-square.svg'; import QBOCircle from '@assets/images/integrationicons/qbo-icon-circle.svg'; import QBOSquare from '@assets/images/integrationicons/qbo-icon-square.svg'; @@ -406,6 +407,7 @@ export { CheckCircle, CheckmarkCircle, NetSuiteSquare, + NSQSSquare, XeroCircle, QBOCircle, Filters, diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index e48646204f345..841fb55380e2a 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -14,7 +14,6 @@ import TextBlock from '@components/TextBlock'; import useLHNEstimatedListSize from '@hooks/useLHNEstimatedListSize'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; -import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {isValidDraftComment} from '@libs/DraftCommentUtils'; @@ -48,9 +47,8 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio const theme = useTheme(); const styles = useThemeStyles(); const {translate, preferredLocale} = useLocalize(); - const {shouldUseNarrowLayout} = useResponsiveLayout(); const estimatedListSize = useLHNEstimatedListSize(); - const shouldShowEmptyLHN = shouldUseNarrowLayout && data.length === 0; + const shouldShowEmptyLHN = data.length === 0; // When the first item renders we want to call the onFirstItemRendered callback. // At this point in time we know that the list is actually displaying items. diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index 40ec431ca8935..32f9f7d5a8272 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -1,5 +1,5 @@ import type {ImageContentFit} from 'expo-image'; -import type {ReactElement, ReactNode} from 'react'; +import type {ReactElement, ReactNode, Ref} from 'react'; import React, {forwardRef, useContext, useMemo} from 'react'; import type {GestureResponderEvent, StyleProp, TextStyle, ViewStyle} from 'react-native'; import {ActivityIndicator, View} from 'react-native'; @@ -60,6 +60,10 @@ type NoIcon = { }; type MenuItemBaseProps = { + /* View ref */ + /* eslint-disable-next-line react/no-unused-prop-types */ + ref?: Ref; + /** Function to fire when component is pressed */ onPress?: (event: GestureResponderEvent | KeyboardEvent) => void | Promise; diff --git a/src/components/MenuItemList.tsx b/src/components/MenuItemList.tsx index b2d79b6243acf..21fd73e7353dd 100644 --- a/src/components/MenuItemList.tsx +++ b/src/components/MenuItemList.tsx @@ -1,6 +1,7 @@ import React, {useRef} from 'react'; import type {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native'; import useSingleExecution from '@hooks/useSingleExecution'; +import mergeRefs from '@libs/mergeRefs'; import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import CONST from '@src/CONST'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; @@ -70,32 +71,32 @@ function MenuItemList({menuItems = [], shouldUseSingleExecution = false, wrapper }; return ( - <> - {menuItems.map(({key, ...menuItemProps}) => ( - ( + + - secondaryInteraction(menuItemProps.link, e) : undefined} - ref={popoverAnchor} - shouldBlockSelection={!!menuItemProps.link} - icon={icon} - iconWidth={iconWidth} - iconHeight={iconHeight} - // eslint-disable-next-line react/jsx-props-no-spreading - {...menuItemProps} - disabled={!!menuItemProps.disabled || isExecuting} - onPress={shouldUseSingleExecution ? singleExecution(menuItemProps.onPress) : menuItemProps.onPress} - /> - - ))} - + wrapperStyle={wrapperStyle} + onSecondaryInteraction={menuItemProps.link !== undefined ? (e) => secondaryInteraction(menuItemProps.link, e) : undefined} + ref={mergeRefs(ref, popoverAnchor)} + shouldBlockSelection={!!menuItemProps.link} + icon={icon} + iconWidth={iconWidth} + iconHeight={iconHeight} + // eslint-disable-next-line react/jsx-props-no-spreading + {...menuItemProps} + disabled={!!menuItemProps.disabled || isExecuting} + onPress={shouldUseSingleExecution ? singleExecution(menuItemProps.onPress) : menuItemProps.onPress} + /> + + )) ); } diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 4536b18217a2c..5b3877050e7ab 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -264,7 +264,7 @@ function MoneyRequestConfirmationList({ const policyTagLists = useMemo(() => getTagLists(policyTags), [policyTags]); - const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest) && !isPerDiemRequest; + const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest, isPerDiemRequest); const previousTransactionAmount = usePrevious(transaction?.amount); const previousTransactionCurrency = usePrevious(transaction?.currency); diff --git a/src/components/Reactions/ReactionTooltipContent.tsx b/src/components/Reactions/ReactionTooltipContent.tsx index 8f469b01272cb..59197fa7984d0 100644 --- a/src/components/Reactions/ReactionTooltipContent.tsx +++ b/src/components/Reactions/ReactionTooltipContent.tsx @@ -26,7 +26,10 @@ type ReactionTooltipContentProps = Pick PersonalDetailsUtils.getPersonalDetailsByIDs(accountIDs, currentUserPersonalDetails.accountID, true), [currentUserPersonalDetails.accountID, accountIDs]); + const users = useMemo( + () => PersonalDetailsUtils.getPersonalDetailsByIDs({accountIDs, currentUserAccountID: currentUserPersonalDetails.accountID, shouldChangeUserDisplayName: true}), + [currentUserPersonalDetails.accountID, accountIDs], + ); const namesString = users .map((user) => user?.displayName) diff --git a/src/components/ReportActionItem/IssueCardMessage.tsx b/src/components/ReportActionItem/IssueCardMessage.tsx index dcf657137d582..f0e22c9a731b2 100644 --- a/src/components/ReportActionItem/IssueCardMessage.tsx +++ b/src/components/ReportActionItem/IssueCardMessage.tsx @@ -56,7 +56,9 @@ function IssueCardMessage({action, policyID}: IssueCardMessageProps) { return ( <> - ${ReportActionsUtils.getCardIssuedMessage(action, true, policyID, !!card)}`} /> + ${ReportActionsUtils.getCardIssuedMessage({reportAction: action, shouldRenderHTML: true, policyID, shouldDisplayLinkToCard: !!card})}`} + /> {shouldShowAddMissingDetailsButton && (