diff --git a/src/CONST.ts b/src/CONST.ts index 285d3ed2e13d..d6fb28a7e0a3 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1052,6 +1052,7 @@ const CONST = { REQUEST_MANUAL: 'requestManual', REQUEST_SCAN: 'requestScan', REQUEST_DISTANCE: 'requestDistance', + PER_DIEM: 'perDiem', SPLIT_MANUAL: 'splitManual', SPLIT_SCAN: 'splitScan', SPLIT_DISTANCE: 'splitDistance', diff --git a/src/languages/en.ts b/src/languages/en.ts index 6118d0a7dd72..92b6f0b6ce60 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -842,17 +842,15 @@ const translations = { }, quickAction: { scanReceipt: 'Scan receipt', - recordDistance: 'Record distance', + recordDistance: 'Track distance', requestMoney: 'Create expense', + perDiem: 'Create per diem', splitBill: 'Split expense', splitScan: 'Split receipt', splitDistance: 'Split distance', paySomeone: ({name}: PaySomeoneParams = {}) => `Pay ${name ?? 'someone'}`, assignTask: 'Assign task', header: 'Quick action', - trackManual: 'Create expense', - trackScan: 'Scan receipt', - trackDistance: 'Track distance', noLongerHaveReportAccess: 'You no longer have access to your previous quick action destination. Pick a new one below.', updateDestination: 'Update destination', }, diff --git a/src/languages/es.ts b/src/languages/es.ts index ae5b540b4046..31425c9d9fcc 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -837,17 +837,15 @@ const translations = { }, quickAction: { scanReceipt: 'Escanear recibo', - recordDistance: 'Grabar distancia', + recordDistance: 'Crear gasto por desplazamiento', requestMoney: 'Crear gasto', + perDiem: 'Crear viático', splitBill: 'Dividir gasto', splitScan: 'Dividir recibo', splitDistance: 'Dividir distancia', paySomeone: ({name}: PaySomeoneParams = {}) => `Pagar a ${name ?? 'alguien'}`, assignTask: 'Assignar tarea', header: 'Acción rápida', - trackManual: 'Crear gasto', - trackScan: 'Escanear recibo', - trackDistance: 'Crear gasto por desplazamiento', noLongerHaveReportAccess: 'Ya no tienes acceso al destino previo de esta acción rápida. Escoge uno nuevo a continuación.', updateDestination: 'Actualiza el destino', }, diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 8d0c520ef4a5..79ba2d9f8581 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1028,7 +1028,15 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR const clearedPendingFields = Object.fromEntries(Object.keys(transaction.pendingFields ?? {}).map((key) => [key, null])); const optimisticData: OnyxUpdate[] = []; const successData: OnyxUpdate[] = []; - let newQuickAction: ValueOf = isScanRequest ? CONST.QUICK_ACTIONS.REQUEST_SCAN : CONST.QUICK_ACTIONS.REQUEST_MANUAL; + let newQuickAction: ValueOf; + if (isScanRequest) { + newQuickAction = CONST.QUICK_ACTIONS.REQUEST_SCAN; + } else if (isPerDiemRequest) { + newQuickAction = CONST.QUICK_ACTIONS.PER_DIEM; + } else { + newQuickAction = CONST.QUICK_ACTIONS.REQUEST_MANUAL; + } + if (isDistanceRequestTransactionUtils(transaction)) { newQuickAction = CONST.QUICK_ACTIONS.REQUEST_DISTANCE; } @@ -1380,7 +1388,7 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR }, ]; - if (!isOneOnOneSplit && !isPerDiemRequest) { + if (!isOneOnOneSplit) { optimisticData.push({ onyxMethod: Onyx.METHOD.SET, key: ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE, diff --git a/src/libs/actions/QuickActionNavigation.ts b/src/libs/actions/QuickActionNavigation.ts index b89d1a981417..cb05c42b2fa3 100644 --- a/src/libs/actions/QuickActionNavigation.ts +++ b/src/libs/actions/QuickActionNavigation.ts @@ -2,10 +2,11 @@ import {generateReportID} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import type {QuickActionName} from '@src/types/onyx/QuickAction'; import type QuickAction from '@src/types/onyx/QuickAction'; -import * as IOU from './IOU'; -import * as Task from './Task'; +import type {IOURequestType} from './IOU'; +import {startMoneyRequest} from './IOU'; +import {startOutCreateTaskQuickAction} from './Task'; -function getQuickActionRequestType(action: QuickActionName | undefined): IOU.IOURequestType | undefined { +function getQuickActionRequestType(action: QuickActionName | undefined): IOURequestType | undefined { if (!action) { return; } @@ -17,6 +18,8 @@ function getQuickActionRequestType(action: QuickActionName | undefined): IOU.IOU requestType = CONST.IOU.REQUEST_TYPE.SCAN; } else if ([CONST.QUICK_ACTIONS.REQUEST_DISTANCE, CONST.QUICK_ACTIONS.SPLIT_DISTANCE, CONST.QUICK_ACTIONS.TRACK_DISTANCE].some((a) => a === action)) { requestType = CONST.IOU.REQUEST_TYPE.DISTANCE; + } else if (action === CONST.QUICK_ACTIONS.PER_DIEM) { + requestType = CONST.IOU.REQUEST_TYPE.PER_DIEM; } return requestType; @@ -30,23 +33,24 @@ function navigateToQuickAction(isValidReport: boolean, quickActionReportID: stri case CONST.QUICK_ACTIONS.REQUEST_MANUAL: case CONST.QUICK_ACTIONS.REQUEST_SCAN: case CONST.QUICK_ACTIONS.REQUEST_DISTANCE: - selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.SUBMIT, reportID, requestType, true), true); + case CONST.QUICK_ACTIONS.PER_DIEM: + selectOption(() => startMoneyRequest(CONST.IOU.TYPE.SUBMIT, reportID, requestType, true), true); return; case CONST.QUICK_ACTIONS.SPLIT_MANUAL: case CONST.QUICK_ACTIONS.SPLIT_SCAN: case CONST.QUICK_ACTIONS.SPLIT_DISTANCE: - selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.SPLIT, reportID, requestType, true), true); + selectOption(() => startMoneyRequest(CONST.IOU.TYPE.SPLIT, reportID, requestType, true), true); return; case CONST.QUICK_ACTIONS.SEND_MONEY: - selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.PAY, reportID, undefined, true), false); + selectOption(() => startMoneyRequest(CONST.IOU.TYPE.PAY, reportID, undefined, true), false); return; case CONST.QUICK_ACTIONS.ASSIGN_TASK: - selectOption(() => Task.startOutCreateTaskQuickAction(isValidReport ? reportID : '', quickAction.targetAccountID ?? CONST.DEFAULT_NUMBER_ID), false); + selectOption(() => startOutCreateTaskQuickAction(isValidReport ? reportID : '', quickAction.targetAccountID ?? CONST.DEFAULT_NUMBER_ID), false); break; case CONST.QUICK_ACTIONS.TRACK_MANUAL: case CONST.QUICK_ACTIONS.TRACK_SCAN: case CONST.QUICK_ACTIONS.TRACK_DISTANCE: - selectOption(() => IOU.startMoneyRequest(CONST.IOU.TYPE.TRACK, reportID, requestType, true), false); + selectOption(() => startMoneyRequest(CONST.IOU.TYPE.TRACK, reportID, requestType, true), false); break; default: } diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index 0d78dd1950ed..3b6c05cafa16 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -93,6 +93,8 @@ const getQuickActionIcon = (action: QuickActionName): React.FC => { return Expensicons.ReceiptScan; case CONST.QUICK_ACTIONS.REQUEST_DISTANCE: return Expensicons.Car; + case CONST.QUICK_ACTIONS.PER_DIEM: + return Expensicons.CalendarSolid; case CONST.QUICK_ACTIONS.SPLIT_MANUAL: case CONST.QUICK_ACTIONS.SPLIT_SCAN: case CONST.QUICK_ACTIONS.SPLIT_DISTANCE: @@ -117,6 +119,7 @@ const getIouType = (action: QuickActionName) => { case CONST.QUICK_ACTIONS.REQUEST_MANUAL: case CONST.QUICK_ACTIONS.REQUEST_SCAN: case CONST.QUICK_ACTIONS.REQUEST_DISTANCE: + case CONST.QUICK_ACTIONS.PER_DIEM: return CONST.IOU.TYPE.SUBMIT; case CONST.QUICK_ACTIONS.SPLIT_MANUAL: case CONST.QUICK_ACTIONS.SPLIT_SCAN: @@ -136,23 +139,22 @@ const getIouType = (action: QuickActionName) => { const getQuickActionTitle = (action: QuickActionName): TranslationPaths => { switch (action) { case CONST.QUICK_ACTIONS.REQUEST_MANUAL: + case CONST.QUICK_ACTIONS.TRACK_MANUAL: return 'quickAction.requestMoney'; case CONST.QUICK_ACTIONS.REQUEST_SCAN: + case CONST.QUICK_ACTIONS.TRACK_SCAN: return 'quickAction.scanReceipt'; case CONST.QUICK_ACTIONS.REQUEST_DISTANCE: + case CONST.QUICK_ACTIONS.TRACK_DISTANCE: return 'quickAction.recordDistance'; + case CONST.QUICK_ACTIONS.PER_DIEM: + return 'quickAction.perDiem'; case CONST.QUICK_ACTIONS.SPLIT_MANUAL: return 'quickAction.splitBill'; case CONST.QUICK_ACTIONS.SPLIT_SCAN: return 'quickAction.splitScan'; case CONST.QUICK_ACTIONS.SPLIT_DISTANCE: return 'quickAction.splitDistance'; - case CONST.QUICK_ACTIONS.TRACK_MANUAL: - return 'quickAction.trackManual'; - case CONST.QUICK_ACTIONS.TRACK_SCAN: - return 'quickAction.trackScan'; - case CONST.QUICK_ACTIONS.TRACK_DISTANCE: - return 'quickAction.trackDistance'; case CONST.QUICK_ACTIONS.SEND_MONEY: return 'quickAction.paySomeone'; case CONST.QUICK_ACTIONS.ASSIGN_TASK: @@ -387,6 +389,9 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu}: Fl if (!!iouType && !canCreateRequest(quickActionReport, quickActionPolicy, iouType)) { return []; } + if (quickAction?.action === CONST.QUICK_ACTIONS.PER_DIEM && !quickActionPolicy?.arePerDiemRatesEnabled) { + return []; + } const onSelected = () => { interceptAnonymousUser(() => { hideProductTrainingTooltip(); diff --git a/tests/unit/QuickActionNavigationTest.ts b/tests/unit/QuickActionNavigationTest.ts index f094cb24332e..86c54ca51685 100644 --- a/tests/unit/QuickActionNavigationTest.ts +++ b/tests/unit/QuickActionNavigationTest.ts @@ -1,6 +1,6 @@ -import * as IOU from '@libs/actions/IOU'; -import * as QuickActionNavigate from '@libs/actions/QuickActionNavigation'; -import * as ReportUtils from '@libs/ReportUtils'; +import {startMoneyRequest} from '@libs/actions/IOU'; +import {navigateToQuickAction} from '@libs/actions/QuickActionNavigation'; +import {generateReportID} from '@libs/ReportUtils'; import CONST from '@src/CONST'; jest.mock('@libs/actions/IOU', () => ({ @@ -10,33 +10,42 @@ jest.mock('@libs/actions/IOU', () => ({ describe('IOU Utils', () => { // Given navigateToQuickAction is called with quick action argument when clicking on quick action button from Global create menu describe('navigateToQuickAction', () => { - const reportID = ReportUtils.generateReportID(); + const reportID = generateReportID(); it('should be navigated to Manual Submit Expense', () => { // When the quick action is REQUEST_MANUAL - QuickActionNavigate.navigateToQuickAction(true, reportID, {action: CONST.QUICK_ACTIONS.REQUEST_MANUAL}, (onSelected: () => void) => { + navigateToQuickAction(true, reportID, {action: CONST.QUICK_ACTIONS.REQUEST_MANUAL}, (onSelected: () => void) => { onSelected(); }); // Then we should start manual submit request flow - expect(IOU.startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SUBMIT, reportID, CONST.IOU.REQUEST_TYPE.MANUAL, true); + expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SUBMIT, reportID, CONST.IOU.REQUEST_TYPE.MANUAL, true); }); it('should be navigated to Scan receipt Split Expense', () => { // When the quick action is SPLIT_SCAN - QuickActionNavigate.navigateToQuickAction(true, reportID, {action: CONST.QUICK_ACTIONS.SPLIT_SCAN}, (onSelected: () => void) => { + navigateToQuickAction(true, reportID, {action: CONST.QUICK_ACTIONS.SPLIT_SCAN}, (onSelected: () => void) => { onSelected(); }); // Then we should start scan split request flow - expect(IOU.startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SPLIT, reportID, CONST.IOU.REQUEST_TYPE.SCAN, true); + expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SPLIT, reportID, CONST.IOU.REQUEST_TYPE.SCAN, true); }); it('should be navigated to Track distance Expense', () => { // When the quick action is TRACK_DISTANCE - QuickActionNavigate.navigateToQuickAction(true, reportID, {action: CONST.QUICK_ACTIONS.TRACK_DISTANCE}, (onSelected: () => void) => { + navigateToQuickAction(true, reportID, {action: CONST.QUICK_ACTIONS.TRACK_DISTANCE}, (onSelected: () => void) => { onSelected(); }); // Then we should start distance track request flow - expect(IOU.startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.TRACK, reportID, CONST.IOU.REQUEST_TYPE.DISTANCE, true); + expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.TRACK, reportID, CONST.IOU.REQUEST_TYPE.DISTANCE, true); + }); + + it('should be navigated to Per Diem Expense', () => { + // When the quick action is PER_DIEM + navigateToQuickAction(true, reportID, {action: CONST.QUICK_ACTIONS.PER_DIEM}, (onSelected: () => void) => { + onSelected(); + }); + // Then we should start per diem request flow + expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SUBMIT, reportID, CONST.IOU.REQUEST_TYPE.PER_DIEM, true); }); }); });