Skip to content

Commit

Permalink
Merge pull request #54201 from callstack-internal/JKobrynski/feat/540…
Browse files Browse the repository at this point in the history
…06-create-expense-cleanup
  • Loading branch information
grgia authored Jan 16, 2025
2 parents 3cb915f + e6b5552 commit 3e2f03f
Show file tree
Hide file tree
Showing 17 changed files with 213 additions and 215 deletions.
4 changes: 2 additions & 2 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,9 @@ const onboardingPersonalSpendMessage: OnboardingMessage = {
'Here’s how to track an expense:\n' +
'\n' +
'1. Click the green *+* button.\n' +
'2. Choose *Track expense*.\n' +
'2. Choose *Create expense*.\n' +
'3. Enter an amount or scan a receipt.\n' +
'4. Click *Track*.\n' +
'4. Click *Create*.\n' +
'\n' +
'And you’re done! Yep, it’s that easy.',
},
Expand Down
154 changes: 89 additions & 65 deletions src/components/MoneyRequestConfirmationList.tsx

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions src/components/ReportWelcomeText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
import usePermissions from '@hooks/usePermissions';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import {getPersonalDetailsForAccountIDs} from '@libs/OptionsListUtils';
Expand Down Expand Up @@ -60,7 +59,7 @@ function ReportWelcomeText({report, policy}: ReportWelcomeTextProps) {
const displayNamesWithTooltips = getDisplayNamesWithTooltips(getPersonalDetailsForAccountIDs(participantAccountIDs, personalDetails), isMultipleParticipant);
const welcomeMessage = SidebarUtils.getWelcomeMessage(report, policy);
const moneyRequestOptions = temporary_getMoneyRequestOptions(report, policy, participantAccountIDs);
const {canUseCombinedTrackSubmit} = usePermissions();

const filteredOptions = moneyRequestOptions.filter(
(item): item is Exclude<IOUType, typeof CONST.IOU.TYPE.REQUEST | typeof CONST.IOU.TYPE.SEND | typeof CONST.IOU.TYPE.CREATE | typeof CONST.IOU.TYPE.INVOICE> =>
item !== CONST.IOU.TYPE.INVOICE,
Expand All @@ -69,7 +68,7 @@ function ReportWelcomeText({report, policy}: ReportWelcomeTextProps) {
.map(
(item, index) =>
`${index === filteredOptions.length - 1 && index > 0 ? `${translate('common.or')} ` : ''}${translate(
canUseCombinedTrackSubmit && item === 'submit' ? `reportActionsView.create` : `reportActionsView.iouTypes.${item}`,
item === 'submit' ? `reportActionsView.create` : `reportActionsView.iouTypes.${item}`,
)}`,
)
.join(', ');
Expand Down
9 changes: 4 additions & 5 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -839,15 +839,15 @@ const translations = {
quickAction: {
scanReceipt: 'Scan receipt',
recordDistance: 'Record distance',
requestMoney: 'Submit expense',
requestMoney: 'Create expense',
splitBill: 'Split expense',
splitScan: 'Split receipt',
splitDistance: 'Split distance',
paySomeone: ({name}: PaySomeoneParams = {}) => `Pay ${name ?? 'someone'}`,
assignTask: 'Assign task',
header: 'Quick action',
trackManual: 'Track expense',
trackScan: 'Track receipt',
trackManual: 'Create expense',
trackScan: 'Create expense for receipt',
trackDistance: 'Track distance',
noLongerHaveReportAccess: 'You no longer have access to your previous quick action destination. Pick a new one below.',
updateDestination: 'Update destination',
Expand All @@ -868,10 +868,9 @@ const translations = {
categorize: 'Categorize',
share: 'Share',
participants: 'Participants',
submitExpense: 'Submit expense',
createExpense: 'Create expense',
trackExpense: 'Track expense',
chooseRecipient: 'Choose recipient',
createExpenseWithAmount: ({amount}: {amount: string}) => `Create ${amount} expense`,
confirmDetails: 'Confirm details',
pay: 'Pay',
cancelPayment: 'Cancel payment',
Expand Down
5 changes: 2 additions & 3 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -834,7 +834,7 @@ const translations = {
quickAction: {
scanReceipt: 'Escanear recibo',
recordDistance: 'Grabar distancia',
requestMoney: 'Presentar gasto',
requestMoney: 'Crear gasto',
splitBill: 'Dividir gasto',
splitScan: 'Dividir recibo',
splitDistance: 'Dividir distancia',
Expand Down Expand Up @@ -862,11 +862,10 @@ const translations = {
categorize: 'Categorizar',
share: 'Compartir',
participants: 'Participantes',
submitExpense: 'Presentar gasto',
createExpense: 'Crear gasto',
paySomeone: ({name}: PaySomeoneParams = {}) => `Pagar a ${name ?? 'alguien'}`,
trackExpense: 'Seguimiento de gastos',
chooseRecipient: 'Elige destinatario',
createExpenseWithAmount: ({amount}: {amount: string}) => `Crear un gasto de ${amount}`,
confirmDetails: 'Confirma los detalles',
pay: 'Pagar',
cancelPayment: 'Cancelar el pago',
Expand Down
2 changes: 1 addition & 1 deletion src/libs/API/parameters/UpdateMoneyRequestParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type {TransactionDetails} from '@libs/ReportUtils';

type UpdateMoneyRequestParams = Partial<TransactionDetails> & {
reportID?: string;
transactionID: string;
transactionID?: string;
reportActionID?: string;
};

Expand Down
10 changes: 0 additions & 10 deletions src/libs/Permissions.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import type {OnyxEntry} from 'react-native-onyx';
import CONST from '@src/CONST';
import type Beta from '@src/types/onyx/Beta';
import * as SessionUtils from './SessionUtils';

const isAccountIDEven = (accountID: number) => accountID % 2 === 0;

function canUseAllBetas(betas: OnyxEntry<Beta[]>): boolean {
return !!betas?.includes(CONST.BETAS.ALL);
Expand All @@ -25,12 +22,6 @@ function canUseCategoryAndTagApprovers(betas: OnyxEntry<Beta[]>): boolean {
return !!betas?.includes(CONST.BETAS.CATEGORY_AND_TAG_APPROVERS) || canUseAllBetas(betas);
}

function canUseCombinedTrackSubmit(): boolean {
// We don't need to show this to all betas since this will be used for developing a feature for A/B testing.
const session = SessionUtils.getSession();
return isAccountIDEven(session?.accountID ?? CONST.DEFAULT_NUMBER_ID);
}

function canUsePerDiem(betas: OnyxEntry<Beta[]>): boolean {
return !!betas?.includes(CONST.BETAS.PER_DIEM) || canUseAllBetas(betas);
}
Expand All @@ -55,7 +46,6 @@ export default {
canUseLinkPreviews,
canUseSpotnanaTravel,
canUseNetSuiteUSATax,
canUseCombinedTrackSubmit,
canUseCategoryAndTagApprovers,
canUsePerDiem,
canUseMergeAccounts,
Expand Down
6 changes: 2 additions & 4 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3662,7 +3662,7 @@ function getTransactionReportName(reportAction: OnyxEntry<ReportAction | Optimis

if (isEmptyObject(transaction)) {
// Transaction data might be empty on app's first load, if so we fallback to Expense/Track Expense
return isTrackExpenseAction(reportAction) ? translateLocal('iou.trackExpense') : translateLocal('iou.expense');
return isTrackExpenseAction(reportAction) ? translateLocal('iou.createExpense') : translateLocal('iou.expense');
}

if (hasReceiptTransactionUtils(transaction) && isReceiptBeingScanned(transaction)) {
Expand Down Expand Up @@ -7567,9 +7567,7 @@ function canCreateRequest(report: OnyxEntry<Report>, policy: OnyxEntry<Policy>,
}

const requestOptions = getMoneyRequestOptions(report, policy, participantAccountIDs);
if (Permissions.canUseCombinedTrackSubmit()) {
requestOptions.push(CONST.IOU.TYPE.CREATE);
}
requestOptions.push(CONST.IOU.TYPE.CREATE);

return requestOptions.includes(iouType);
}
Expand Down
12 changes: 6 additions & 6 deletions src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2917,8 +2917,8 @@ function calculateDiffAmount(
* @param policyCategories
*/
function getUpdateMoneyRequestParams(
transactionID: string,
transactionThreadReportID: string,
transactionID: string | undefined,
transactionThreadReportID: string | undefined,
transactionChanges: TransactionChanges,
policy: OnyxEntry<OnyxTypes.Policy>,
policyTagList: OnyxTypes.OnyxInputOrEntry<OnyxTypes.PolicyTagLists>,
Expand Down Expand Up @@ -3254,8 +3254,8 @@ function getUpdateMoneyRequestParams(
* @param policy May be undefined, an empty object, or an object matching the Policy type (src/types/onyx/Policy.ts)
*/
function getUpdateTrackExpenseParams(
transactionID: string,
transactionThreadReportID: string,
transactionID: string | undefined,
transactionThreadReportID: string | undefined,
transactionChanges: TransactionChanges,
policy: OnyxEntry<OnyxTypes.Policy>,
): UpdateMoneyRequestData {
Expand Down Expand Up @@ -3560,8 +3560,8 @@ function updateMoneyRequestTaxRate({transactionID, optimisticReportActionID, tax
}

type UpdateMoneyRequestDistanceParams = {
transactionID: string;
transactionThreadReportID: string;
transactionID: string | undefined;
transactionThreadReportID: string | undefined;
waypoints: WaypointCollection;
routes?: Routes;
policy?: OnyxEntry<OnyxTypes.Policy>;
Expand Down
17 changes: 7 additions & 10 deletions src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ import enhanceParameters from '@libs/Network/enhanceParameters';
import type {NetworkStatus} from '@libs/NetworkConnection';
import LocalNotification from '@libs/Notification/LocalNotification';
import Parser from '@libs/Parser';
import Permissions from '@libs/Permissions';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as PhoneNumber from '@libs/PhoneNumber';
import getPolicyEmployeeAccountIDs from '@libs/PolicyEmployeeListUtils';
Expand Down Expand Up @@ -3602,16 +3601,14 @@ function prepareOnboardingOptimisticData(
wasInvited?: boolean,
) {
// If the user has the "combinedTrackSubmit" beta enabled we'll show different tasks for track and submit expense.
if (Permissions.canUseCombinedTrackSubmit()) {
if (engagementChoice === CONST.ONBOARDING_CHOICES.PERSONAL_SPEND) {
// eslint-disable-next-line no-param-reassign
data = CONST.COMBINED_TRACK_SUBMIT_ONBOARDING_MESSAGES[CONST.ONBOARDING_CHOICES.PERSONAL_SPEND];
}
if (engagementChoice === CONST.ONBOARDING_CHOICES.PERSONAL_SPEND) {
// eslint-disable-next-line no-param-reassign
data = CONST.COMBINED_TRACK_SUBMIT_ONBOARDING_MESSAGES[CONST.ONBOARDING_CHOICES.PERSONAL_SPEND];
}

if (engagementChoice === CONST.ONBOARDING_CHOICES.EMPLOYER || engagementChoice === CONST.ONBOARDING_CHOICES.SUBMIT) {
// eslint-disable-next-line no-param-reassign
data = CONST.COMBINED_TRACK_SUBMIT_ONBOARDING_MESSAGES[CONST.ONBOARDING_CHOICES.SUBMIT];
}
if (engagementChoice === CONST.ONBOARDING_CHOICES.EMPLOYER || engagementChoice === CONST.ONBOARDING_CHOICES.SUBMIT) {
// eslint-disable-next-line no-param-reassign
data = CONST.COMBINED_TRACK_SUBMIT_ONBOARDING_MESSAGES[CONST.ONBOARDING_CHOICES.SUBMIT];
}

// Guides are assigned and tasks are posted in the #admins room for the MANAGE_TEAM onboarding action, except for emails that have a '+'.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,20 @@ import PopoverMenu from '@components/PopoverMenu';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import Tooltip from '@components/Tooltip/PopoverAnchorTooltip';
import useLocalize from '@hooks/useLocalize';
import usePermissions from '@hooks/usePermissions';
import usePrevious from '@hooks/usePrevious';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as Browser from '@libs/Browser';
import {isSafari} from '@libs/Browser';
import getIconForAction from '@libs/getIconForAction';
import Navigation from '@libs/Navigation/Navigation';
import * as ReportUtils from '@libs/ReportUtils';
import * as SubscriptionUtils from '@libs/SubscriptionUtils';
import * as IOU from '@userActions/IOU';
import * as Modal from '@userActions/Modal';
import * as Report from '@userActions/Report';
import * as Task from '@userActions/Task';
import {canCreateTaskInReport, getPayeeName, temporary_getMoneyRequestOptions} from '@libs/ReportUtils';
import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils';
import {startMoneyRequest} from '@userActions/IOU';
import {close} from '@userActions/Modal';
import {setIsComposerFullSize} from '@userActions/Report';
import {clearOutTaskInfoAndNavigate} from '@userActions/Task';
import DelegateNoAccessModal from '@src/components/DelegateNoAccessModal';
import type {IOUType} from '@src/CONST';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -121,14 +120,13 @@ function AttachmentPickerWithMenuItems({
const {isDelegateAccessRestricted} = useDelegateUserDetails();
const [isNoDelegateAccessMenuVisible, setIsNoDelegateAccessMenuVisible] = useState(false);
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`);
const {canUseCombinedTrackSubmit} = usePermissions();

/**
* Returns the list of IOU Options
*/
const moneyRequestOptions = useMemo(() => {
const selectOption = (onSelected: () => void, shouldRestrictAction: boolean) => {
if (shouldRestrictAction && policy && SubscriptionUtils.shouldRestrictUserBillableActions(policy.id)) {
if (shouldRestrictAction && policy && shouldRestrictUserBillableActions(policy.id)) {
Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(policy.id));
return;
}
Expand All @@ -140,59 +138,56 @@ function AttachmentPickerWithMenuItems({
[CONST.IOU.TYPE.SPLIT]: {
icon: Expensicons.Transfer,
text: translate('iou.splitExpense'),
onSelected: () => selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.SPLIT, report?.reportID ?? '-1'), true),
onSelected: () => selectOption(() => startMoneyRequest(CONST.IOU.TYPE.SPLIT, report?.reportID ?? String(CONST.DEFAULT_NUMBER_ID)), true),
},
[CONST.IOU.TYPE.SUBMIT]: {
icon: canUseCombinedTrackSubmit ? getIconForAction(CONST.IOU.TYPE.CREATE) : getIconForAction(CONST.IOU.TYPE.REQUEST),
text: canUseCombinedTrackSubmit ? translate('iou.createExpense') : translate('iou.submitExpense'),
onSelected: () => selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.SUBMIT, report?.reportID ?? '-1'), true),
icon: getIconForAction(CONST.IOU.TYPE.CREATE),
text: translate('iou.createExpense'),
onSelected: () => selectOption(() => startMoneyRequest(CONST.IOU.TYPE.SUBMIT, report?.reportID ?? String(CONST.DEFAULT_NUMBER_ID)), true),
},
[CONST.IOU.TYPE.PAY]: {
icon: getIconForAction(CONST.IOU.TYPE.SEND),
text: translate('iou.paySomeone', {name: ReportUtils.getPayeeName(report)}),
text: translate('iou.paySomeone', {name: getPayeeName(report)}),
onSelected: () => {
if (isDelegateAccessRestricted) {
setIsNoDelegateAccessMenuVisible(true);
return;
}
selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.PAY, report?.reportID ?? '-1'), false);
selectOption(() => startMoneyRequest(CONST.IOU.TYPE.PAY, report?.reportID ?? String(CONST.DEFAULT_NUMBER_ID)), false);
},
},
[CONST.IOU.TYPE.TRACK]: {
icon: canUseCombinedTrackSubmit ? getIconForAction(CONST.IOU.TYPE.CREATE) : getIconForAction(CONST.IOU.TYPE.TRACK),
text: canUseCombinedTrackSubmit ? translate('iou.createExpense') : translate('iou.trackExpense'),
onSelected: () => selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.TRACK, report?.reportID ?? '-1'), true),
icon: getIconForAction(CONST.IOU.TYPE.CREATE),
text: translate('iou.createExpense'),
onSelected: () => selectOption(() => startMoneyRequest(CONST.IOU.TYPE.TRACK, report?.reportID ?? String(CONST.DEFAULT_NUMBER_ID)), true),
},
[CONST.IOU.TYPE.INVOICE]: {
icon: Expensicons.InvoiceGeneric,
text: translate('workspace.invoices.sendInvoice'),
onSelected: () => selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.INVOICE, report?.reportID ?? '-1'), false),
onSelected: () => selectOption(() => startMoneyRequest(CONST.IOU.TYPE.INVOICE, report?.reportID ?? String(CONST.DEFAULT_NUMBER_ID)), false),
},
};

const moneyRequestOptionsList = ReportUtils.temporary_getMoneyRequestOptions(report, policy, reportParticipantIDs ?? []).map((option) => ({
const moneyRequestOptionsList = temporary_getMoneyRequestOptions(report, policy, reportParticipantIDs ?? []).map((option) => ({
...options[option],
}));

return canUseCombinedTrackSubmit
? // Removes track option for the workspace with the canUseCombinedTrackSubmit enabled
moneyRequestOptionsList.filter((item, index, self) => index === self.findIndex((t) => t.text === item.text))
: moneyRequestOptionsList;
}, [translate, canUseCombinedTrackSubmit, report, policy, reportParticipantIDs, isDelegateAccessRestricted]);
return moneyRequestOptionsList.filter((item, index, self) => index === self.findIndex((t) => t.text === item.text));
}, [translate, report, policy, reportParticipantIDs, isDelegateAccessRestricted]);

/**
* Determines if we can show the task option
*/
const taskOption: PopoverMenuItem[] = useMemo(() => {
if (!ReportUtils.canCreateTaskInReport(report)) {
if (!canCreateTaskInReport(report)) {
return [];
}

return [
{
icon: Expensicons.Task,
text: translate('newTaskPage.assignTask'),
onSelected: () => Task.clearOutTaskInfoAndNavigate(reportID, report),
onSelected: () => clearOutTaskInfoAndNavigate(reportID, report),
},
];
}, [report, reportID, translate]);
Expand Down Expand Up @@ -300,7 +295,7 @@ function AttachmentPickerWithMenuItems({
onPress={(e) => {
e?.preventDefault();
raiseIsScrollLikelyLayoutTriggered();
Report.setIsComposerFullSize(reportID, false);
setIsComposerFullSize(reportID, false);
}}
// Keep focus on the composer when Collapse button is clicked.
onMouseDown={(e) => e.preventDefault()}
Expand All @@ -321,7 +316,7 @@ function AttachmentPickerWithMenuItems({
onPress={(e) => {
e?.preventDefault();
raiseIsScrollLikelyLayoutTriggered();
Report.setIsComposerFullSize(reportID, true);
setIsComposerFullSize(reportID, true);
}}
// Keep focus on the composer when Expand button is clicked.
onMouseDown={(e) => e.preventDefault()}
Expand Down Expand Up @@ -352,11 +347,11 @@ function AttachmentPickerWithMenuItems({
// function must be called from within a event handler that was initiated
// by the user on Safari.
if (index === menuItems.length - 1) {
if (Browser.isSafari()) {
if (isSafari()) {
triggerAttachmentPicker();
return;
}
Modal.close(() => {
close(() => {
triggerAttachmentPicker();
});
}
Expand Down
Loading

0 comments on commit 3e2f03f

Please sign in to comment.