Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,7 @@ const CONST = {
CHANGE_WORKSPACE: 'changeWorkspace',
VIEW_DETAILS: 'viewDetails',
DELETE: 'delete',
ADD_EXPENSE: 'addExpense',
},
PRIMARY_ACTIONS: {
SUBMIT: 'submit',
Expand All @@ -1205,6 +1206,7 @@ const CONST = {
REMOVE_HOLD: 'removeHold',
REVIEW_DUPLICATES: 'reviewDuplicates',
MARK_AS_CASH: 'markAsCash',
ADD_EXPENSE: 'addExpense',
},
TRANSACTION_PRIMARY_ACTIONS: {
REMOVE_HOLD: 'removeHold',
Expand Down
29 changes: 27 additions & 2 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {
getNextApproverAccountID,
payInvoice,
payMoneyRequest,
startMoneyRequest,
submitReport,
unapproveExpenseReport,
} from '@userActions/IOU';
Expand Down Expand Up @@ -184,6 +185,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
const isPayAtEndExpense = isPayAtEndExpenseTransactionUtils(transaction);
const isArchivedReport = isArchivedReportWithID(moneyRequestReport?.reportID);
const [archiveReason] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${moneyRequestReport?.reportID}`, {selector: getArchiveReason, canBeMissing: true});
const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${moneyRequestReport?.reportID}`, {canBeMissing: true});

const getCanIOUBePaid = useCallback(
(onlyShowPayElsewhere = false, shouldCheckApprovedState = true) =>
Expand Down Expand Up @@ -360,8 +362,8 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
if (!moneyRequestReport) {
return '';
}
return getReportPrimaryAction(moneyRequestReport, transactions, violations, policy);
}, [isPaidAnimationRunning, moneyRequestReport, policy, transactions, violations]);
return getReportPrimaryAction(moneyRequestReport, transactions, violations, policy, reportNameValuePairs);
}, [isPaidAnimationRunning, moneyRequestReport, policy, reportNameValuePairs, transactions, violations]);

const primaryActionsImplementation = {
[CONST.REPORT.PRIMARY_ACTIONS.SUBMIT]: (
Expand Down Expand Up @@ -454,6 +456,18 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
}}
/>
),
[CONST.REPORT.PRIMARY_ACTIONS.ADD_EXPENSE]: (
<Button
success
text={translate('iou.addExpense')}
onPress={() => {
if (!moneyRequestReport?.reportID) {
return;
}
startMoneyRequest(CONST.IOU.TYPE.SUBMIT, moneyRequestReport?.reportID);
}}
/>
),
};

const secondaryActions = useMemo(() => {
Expand Down Expand Up @@ -589,6 +603,17 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
setIsDeleteModalVisible(true);
},
},
[CONST.REPORT.SECONDARY_ACTIONS.ADD_EXPENSE]: {
text: translate('iou.addExpense'),
icon: Expensicons.Plus,
value: CONST.REPORT.SECONDARY_ACTIONS.ADD_EXPENSE,
onSelected: () => {
if (!moneyRequestReport?.reportID) {
return;
}
startMoneyRequest(CONST.IOU.TYPE.SUBMIT, moneyRequestReport?.reportID);
},
},
};

const applicableSecondaryActions = secondaryActions.map((action) => secondaryActionsImplemenation[action]);
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,7 @@ const translations = {
share: 'Share',
participants: 'Participants',
createExpense: 'Create expense',
addExpense: 'Add expense',
chooseRecipient: 'Choose recipient',
createExpenseWithAmount: ({amount}: {amount: string}) => `Create ${amount} expense`,
confirmDetails: 'Confirm details',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,7 @@ const translations = {
original: 'Original',
split: 'Dividir',
splitExpense: 'Dividir gasto',
addExpense: 'Agregar gasto',
expense: 'Gasto',
categorize: 'Categorizar',
share: 'Compartir',
Expand Down
22 changes: 17 additions & 5 deletions src/libs/ReportPrimaryActionUtils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type {OnyxCollection} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import CONST from '@src/CONST';
import type {Policy, Report, Transaction, TransactionViolation} from '@src/types/onyx';
import type {Policy, Report, ReportNameValuePairs, Transaction, TransactionViolation} from '@src/types/onyx';
import {isApprover as isApproverUtils} from './actions/Policy/Member';
import {getCurrentUserAccountID} from './actions/Report';
import {
Expand All @@ -15,9 +15,9 @@ import {
} from './PolicyUtils';
import {getAllReportActions, getOneTransactionThreadReportID} from './ReportActionsUtils';
import {
canAddTransaction as canAddTransactionUtil,
getMoneyRequestSpendBreakdown,
getParentReport,
getReportNameValuePairs,
isArchivedReport,
isClosedReport as isClosedReportUtils,
isCurrentUserSubmitter,
Expand All @@ -42,6 +42,14 @@ import {
shouldShowBrokenConnectionViolation as shouldShowBrokenConnectionViolationTransactionUtils,
} from './TransactionUtils';

function isAddExpenseAction(report: Report, reportTransactions: Transaction[]) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Coming from: #62958, we should add the condition to check if it's ChatReportArchived

const isExpenseReport = isExpenseReportUtils(report);
const isReportSubmitter = isCurrentUserSubmitter(report.reportID);
const canAddTransaction = canAddTransactionUtil(report);

return isExpenseReport && canAddTransaction && isReportSubmitter && reportTransactions.length === 0;
}

function isSubmitAction(report: Report, reportTransactions: Transaction[], policy?: Policy) {
const isExpenseReport = isExpenseReportUtils(report);
const isReportSubmitter = isCurrentUserSubmitter(report.reportID);
Expand Down Expand Up @@ -92,7 +100,7 @@ function isApproveAction(report: Report, reportTransactions: Transaction[], poli
return false;
}

function isPayAction(report: Report, policy?: Policy) {
function isPayAction(report: Report, policy?: Policy, reportNameValuePairs?: ReportNameValuePairs) {
const isExpenseReport = isExpenseReportUtils(report);
const isReportPayer = isPayer(getSession(), report, false, policy);
const arePaymentsEnabled = arePaymentsEnabledUtils(policy);
Expand All @@ -106,7 +114,6 @@ function isPayAction(report: Report, policy?: Policy) {
const isReportFinished = (isReportApproved && !report.isWaitingOnBankAccount) || isSubmittedWithoutApprovalsEnabled || isReportClosed;
const {reimbursableSpend} = getMoneyRequestSpendBreakdown(report);

const reportNameValuePairs = getReportNameValuePairs(report.chatReportID);
const isChatReportArchived = isArchivedReport(reportNameValuePairs);

if (isChatReportArchived) {
Expand Down Expand Up @@ -248,7 +255,12 @@ function getReportPrimaryAction(
reportTransactions: Transaction[],
violations: OnyxCollection<TransactionViolation[]>,
policy?: Policy,
reportNameValuePairs?: ReportNameValuePairs,
): ValueOf<typeof CONST.REPORT.PRIMARY_ACTIONS> | '' {
if (isAddExpenseAction(report, reportTransactions)) {
return CONST.REPORT.PRIMARY_ACTIONS.ADD_EXPENSE;
}

if (isReviewDuplicatesAction(report, reportTransactions, policy)) {
return CONST.REPORT.PRIMARY_ACTIONS.REVIEW_DUPLICATES;
}
Expand All @@ -265,7 +277,7 @@ function getReportPrimaryAction(
return CONST.REPORT.PRIMARY_ACTIONS.APPROVE;
}

if (isPayAction(report, policy)) {
if (isPayAction(report, policy, reportNameValuePairs)) {
return CONST.REPORT.PRIMARY_ACTIONS.PAY;
}

Expand Down
19 changes: 15 additions & 4 deletions src/libs/ReportSecondaryActionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from './PolicyUtils';
import {getIOUActionForReportID, getReportActions, isPayAction} from './ReportActionsUtils';
import {
canAddTransaction,
isClosedReport as isClosedReportUtils,
isCurrentUserSubmitter,
isExpenseReport as isExpenseReportUtils,
Expand All @@ -34,6 +35,16 @@ import {
import {getSession} from './SessionUtils';
import {allHavePendingRTERViolation, isDuplicate, isOnHold as isOnHoldTransactionUtils, shouldShowBrokenConnectionViolationForMultipleTransactions} from './TransactionUtils';

function isAddExpenseAction(report: Report, reportTransactions: Transaction[]) {
const isReportSubmitter = isCurrentUserSubmitter(report.reportID);

if (!isReportSubmitter || reportTransactions.length === 0) {
return false;
}

return canAddTransaction(report);
}

function isSubmitAction(report: Report, reportTransactions: Transaction[], policy?: Policy): boolean {
const transactionAreComplete = reportTransactions.every((transaction) => transaction.amount !== 0 || transaction.modifiedAmount !== 0);

Expand All @@ -49,25 +60,21 @@ function isSubmitAction(report: Report, reportTransactions: Transaction[], polic

const isReportSubmitter = isCurrentUserSubmitter(report.reportID);
const isReportApprover = isApproverUtils(policy, getCurrentUserAccountID());

if (!isReportSubmitter && !isReportApprover) {
return false;
}

const isOpenReport = isOpenReportUtils(report);

if (!isOpenReport) {
return false;
}

const submitToAccountID = getSubmitToAccountID(policy, report);

if (submitToAccountID === report.ownerAccountID && policy?.preventSelfApproval) {
return false;
}

const isAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN;

if (isAdmin) {
return true;
}
Expand Down Expand Up @@ -398,6 +405,10 @@ function getSecondaryReportActions(
): Array<ValueOf<typeof CONST.REPORT.SECONDARY_ACTIONS>> {
const options: Array<ValueOf<typeof CONST.REPORT.SECONDARY_ACTIONS>> = [];

if (isAddExpenseAction(report, reportTransactions)) {
options.push(CONST.REPORT.SECONDARY_ACTIONS.ADD_EXPENSE);
}

if (isSubmitAction(report, reportTransactions, policy)) {
options.push(CONST.REPORT.SECONDARY_ACTIONS.SUBMIT);
}
Expand Down
4 changes: 2 additions & 2 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8485,9 +8485,9 @@ function canCreateRequest(report: OnyxEntry<Report>, policy: OnyxEntry<Policy>,
return requestOptions.includes(iouType);
}

function getWorkspaceChats(policyID: string, accountIDs: number[], reports: OnyxCollection<Report> = allReports): Array<OnyxEntry<Report>> {
function getWorkspaceChats(policyID: string | undefined, accountIDs: number[], reports: OnyxCollection<Report> = allReports): Array<OnyxEntry<Report>> {
return Object.values(reports ?? {}).filter(
(report) => isPolicyExpenseChat(report) && report?.policyID === policyID && report?.ownerAccountID && accountIDs.includes(report?.ownerAccountID),
(report) => isPolicyExpenseChat(report) && !!policyID && report?.policyID === policyID && report?.ownerAccountID && accountIDs.includes(report?.ownerAccountID),
);
}

Expand Down
9 changes: 7 additions & 2 deletions src/pages/NewReportWorkspaceSelectionPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import Navigation from '@libs/Navigation/Navigation';
import {getHeaderMessageForNonUserList} from '@libs/OptionsListUtils';
import {isPolicyAdmin, shouldShowPolicy} from '@libs/PolicyUtils';
import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils';
import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
Expand All @@ -37,9 +38,9 @@ function NewReportWorkspaceSelectionPage() {
const {translate} = useLocalize();
const {shouldUseNarrowLayout} = useResponsiveLayout();

const [policies, fetchStatus] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
const [policies, fetchStatus] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP);
const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true});
const shouldShowLoadingIndicator = isLoadingApp && !isOffline;

const navigateToNewReport = useCallback(
Expand All @@ -66,6 +67,10 @@ function NewReportWorkspaceSelectionPage() {
if (!policyID) {
return;
}
if (shouldRestrictUserBillableActions(policyID)) {
Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(policyID));
return;
}
const optimisticReportID = createNewReport(currentUserPersonalDetails, policyID);
navigateToNewReport(optimisticReportID);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,19 +131,21 @@ function AttachmentPickerWithMenuItems({
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, {canBeMissing: true});
const {canUseTableReportView, canUseLeftHandBar} = usePermissions();

/**
* Returns the list of IOU Options
*/
const moneyRequestOptions = useMemo(() => {
const selectOption = (onSelected: () => void, shouldRestrictAction: boolean) => {
const selectOption = useCallback(
(onSelected: () => void, shouldRestrictAction: boolean) => {
if (shouldRestrictAction && policy && shouldRestrictUserBillableActions(policy.id)) {
Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(policy.id));
return;
}

onSelected();
};

},
[policy],
);
/**
* Returns the list of IOU Options
*/
const moneyRequestOptions = useMemo(() => {
const options: MoneyRequestOptions = {
[CONST.IOU.TYPE.SPLIT]: {
icon: Expensicons.Transfer,
Expand Down Expand Up @@ -190,7 +192,7 @@ function AttachmentPickerWithMenuItems({
}));

return moneyRequestOptionsList.filter((item, index, self) => index === self.findIndex((t) => t.text === item.text));
}, [translate, report, policy, reportParticipantIDs, isDelegateAccessRestricted, shouldUseNarrowLayout]);
}, [translate, shouldUseNarrowLayout, report, policy, reportParticipantIDs, selectOption, isDelegateAccessRestricted]);

const createReportOption: PopoverMenuItem[] = useMemo(() => {
if (!canUseTableReportView || !isPolicyExpenseChat(report) || !isPaidGroupPolicy(report) || !isReportOwner(report)) {
Expand All @@ -201,12 +203,10 @@ function AttachmentPickerWithMenuItems({
{
icon: Expensicons.Document,
text: translate('report.newReport.createReport'),
onSelected: () => {
createNewReport(currentUserPersonalDetails, report?.policyID);
},
onSelected: () => selectOption(() => createNewReport(currentUserPersonalDetails, report?.policyID), true),
},
];
}, [canUseTableReportView, currentUserPersonalDetails, report, translate]);
}, [canUseTableReportView, currentUserPersonalDetails, report, selectOption, translate]);

/**
* Determines if we can show the task option
Expand Down
20 changes: 10 additions & 10 deletions src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,13 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT
const [quickActionReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${quickAction?.chatReportID}`, {canBeMissing: true});
const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${quickActionReport?.reportID}`, {canBeMissing: true});
const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: false});
const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: false});
const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: true});
const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, {canBeMissing: false});
const policyChatForActivePolicy = useMemo(() => {
if (isEmptyObject(activePolicy) || !activePolicy?.isPolicyExpenseChatEnabled) {
return {} as OnyxTypes.Report;
}
const policyChatsForActivePolicy = getWorkspaceChats(`${activePolicyID ?? CONST.DEFAULT_NUMBER_ID}`, [session?.accountID ?? CONST.DEFAULT_NUMBER_ID], allReports);
const policyChatsForActivePolicy = getWorkspaceChats(activePolicyID, [session?.accountID ?? CONST.DEFAULT_NUMBER_ID], allReports);
return policyChatsForActivePolicy.length > 0 ? policyChatsForActivePolicy.at(0) : ({} as OnyxTypes.Report);
}, [activePolicy, activePolicyID, session?.accountID, allReports]);
const [quickActionPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${quickActionReport?.policyID}`, {canBeMissing: true});
Expand Down Expand Up @@ -513,24 +513,24 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT
return;
}

let workspaceIDForReportCreation: string | undefined;

// If the user's default workspace is a paid group workspace with chat enabled, we create a report with it by default
if (activePolicy && activePolicy.isPolicyExpenseChatEnabled && isPaidGroupPolicy(activePolicy)) {
const createdReportID = createNewReport(currentUserPersonalDetails, activePolicyID);
Navigation.setNavigationActionToMicrotaskQueue(() => {
Navigation.navigate(ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID: createdReportID, backTo: Navigation.getActiveRoute()}));
});
return;
workspaceIDForReportCreation = activePolicyID;
} else if (groupPoliciesWithChatEnabled.length === 1) {
workspaceIDForReportCreation = groupPoliciesWithChatEnabled.at(0)?.id;
}

if (groupPoliciesWithChatEnabled.length === 1) {
const createdReportID = createNewReport(currentUserPersonalDetails, groupPoliciesWithChatEnabled.at(0)?.id);
if (workspaceIDForReportCreation && !shouldRestrictUserBillableActions(workspaceIDForReportCreation)) {
const createdReportID = createNewReport(currentUserPersonalDetails, workspaceIDForReportCreation);
Navigation.setNavigationActionToMicrotaskQueue(() => {
Navigation.navigate(ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID: createdReportID, backTo: Navigation.getActiveRoute()}));
});
return;
}

// If the user's default workspace is personal and the user has more than one group workspace which is paid and has chat enabled, we need to redirect them to the workspace selection screen
// If the user's default workspace is personal and the user has more than one group workspace, which is paid and has chat enabled, or a chosen workspace is past the grace period, we need to redirect them to the workspace selection screen
Navigation.navigate(ROUTES.NEW_REPORT_WORKSPACE_SELECTION);
});
},
Expand Down