Skip to content

Commit

Permalink
Merge pull request Expensify#57475 from software-mansion-labs/jnowako…
Browse files Browse the repository at this point in the history
…w/simplified-actions-get-primary-action

[No QA] Implement report primary action getter
  • Loading branch information
luacmartins authored Mar 4, 2025
2 parents b649380 + e9d65e1 commit 23fa95c
Show file tree
Hide file tree
Showing 13 changed files with 522 additions and 82 deletions.
9 changes: 9 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,15 @@ const CONST = {
MIN_INITIAL_REPORT_ACTION_COUNT: 15,
UNREPORTED_REPORTID: '0',
SPLIT_REPORTID: '-2',
PRIMARY_ACTIONS: {
SUBMIT: 'submit',
APPROVE: 'approve',
PAY: 'pay',
EXPORT_TO_ACCOUNTING: 'exportToAccounting',
REMOVE_HOLD: 'removeHold',
REVIEW_DUPLICATES: 'reviewDuplicates',
MARK_AS_CASH: 'markAsCash',
},
ACTIONS: {
LIMIT: 50,
// OldDot Actions render getMessage from Web-Expensify/lib/Report/Action PHP files via getMessageOfOldDotReportAction in ReportActionsUtils.ts
Expand Down
4 changes: 2 additions & 2 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import {
isPayAtEndExpense as isPayAtEndExpenseTransactionUtils,
isPending,
isReceiptBeingScanned,
shouldShowBrokenConnectionViolation as shouldShowBrokenConnectionViolationTransactionUtils,
shouldShowBrokenConnectionViolationForMultipleTransactions,
shouldShowRTERViolationMessage,
} from '@libs/TransactionUtils';
import variables from '@styles/variables';
Expand Down Expand Up @@ -162,7 +162,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
// Check if there is pending rter violation in all transactionViolations with given transactionIDs.
const hasAllPendingRTERViolations = allHavePendingRTERViolation(transactionIDs, violations);
// Check if user should see broken connection violation warning.
const shouldShowBrokenConnectionViolation = shouldShowBrokenConnectionViolationTransactionUtils(transactionIDs, moneyRequestReport, policy, violations);
const shouldShowBrokenConnectionViolation = shouldShowBrokenConnectionViolationForMultipleTransactions(transactionIDs, moneyRequestReport, policy, violations);
const hasOnlyHeldExpenses = hasOnlyHeldExpensesReportUtils(moneyRequestReport?.reportID);
const isPayAtEndExpense = isPayAtEndExpenseTransactionUtils(transaction);
const isArchivedReport = isArchivedReportWithID(moneyRequestReport?.reportID);
Expand Down
2 changes: 1 addition & 1 deletion src/components/MoneyRequestHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre

const hasPendingRTERViolation = hasPendingRTERViolationTransactionUtils(transactionViolations);

const shouldShowBrokenConnectionViolation = shouldShowBrokenConnectionViolationTransactionUtils(transaction, parentReport, policy, transactionViolations);
const shouldShowBrokenConnectionViolation = shouldShowBrokenConnectionViolationTransactionUtils(parentReport, policy, transactionViolations);
const shouldShowMarkAsCashButton = checkIfShouldShowMarkAsCashButton(hasPendingRTERViolation, shouldShowBrokenConnectionViolation, parentReport, policy);

const markAsCash = useCallback(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ function MoneyRequestPreviewContent({
};

const getPendingMessageProps: () => PendingMessageProps = () => {
if (shouldShowBrokenConnectionViolation(transaction, iouReport, policy, violations)) {
if (shouldShowBrokenConnectionViolation(iouReport, policy, violations)) {
return {shouldShow: true, messageIcon: Hourglass, messageDescription: translate('violations.brokenConnection530Error')};
}
return {shouldShow: false};
Expand Down
4 changes: 2 additions & 2 deletions src/components/ReportActionItem/ReportPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ import {
isPartialMerchant,
isPending,
isReceiptBeingScanned,
shouldShowBrokenConnectionViolation as shouldShowBrokenConnectionViolationTransactionUtils,
shouldShowBrokenConnectionViolationForMultipleTransactions,
} from '@libs/TransactionUtils';
import type {ContextMenuAnchor} from '@pages/home/report/ContextMenu/ReportActionContextMenu';
import variables from '@styles/variables';
Expand Down Expand Up @@ -236,7 +236,7 @@ function ReportPreview({
const lastThreeReceipts = lastThreeTransactions.map((transaction) => ({...getThumbnailAndImageURIs(transaction), transaction}));
const lastTransactionViolations = useTransactionViolations(lastTransaction?.transactionID);
const showRTERViolationMessage = numberOfRequests === 1 && hasPendingUI(lastTransaction, lastTransactionViolations);
const shouldShowBrokenConnectionViolation = numberOfRequests === 1 && shouldShowBrokenConnectionViolationTransactionUtils(transactionIDList, iouReport, policy, violations);
const shouldShowBrokenConnectionViolation = numberOfRequests === 1 && shouldShowBrokenConnectionViolationForMultipleTransactions(transactionIDList, iouReport, policy, violations);
let formattedMerchant = numberOfRequests === 1 ? getMerchant(lastTransaction) : null;
const formattedDescription = numberOfRequests === 1 ? getDescription(lastTransaction) : null;

Expand Down
31 changes: 30 additions & 1 deletion src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import type PolicyEmployee from '@src/types/onyx/PolicyEmployee';
import type {SearchPolicy} from '@src/types/onyx/SearchResults';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import {hasSynchronizationErrorMessage} from './actions/connections';
import {getCurrentUserAccountID} from './actions/Report';
import {getCurrentUserAccountID, getCurrentUserEmail} from './actions/Report';
import {getCategoryApproverRule} from './CategoryUtils';
import {translateLocal} from './Localize';
import Navigation from './Navigation/Navigation';
Expand Down Expand Up @@ -1354,6 +1354,33 @@ function canEnablePreventSelfApprovals(policy: OnyxEntry<Policy>): boolean {
return employeeEmails.length > 1;
}

function isPrefferedExporter(policy: Policy) {
const user = getCurrentUserEmail();
const exporters = [
policy.connections?.intacct?.config?.export?.exporter,
policy.connections?.netsuite?.options?.config?.exporter,
policy.connections?.netsuiteQuickStart?.config?.exporter,
policy.connections?.quickbooksDesktop?.config?.export?.exporter,
policy.connections?.quickbooksOnline?.config?.export?.exporter,
policy.connections?.xero?.config?.export?.exporter,
];

return exporters.some((exporter) => exporter && exporter === user);
}

function isAutoSyncEnabled(policy: Policy) {
const values = [
policy.connections?.intacct?.config?.autoSync?.enabled,
policy.connections?.netsuite?.config?.autoSync?.enabled,
policy.connections?.netsuiteQuickStart?.config?.autoSync?.enabled,
policy.connections?.quickbooksDesktop?.config?.autoSync?.enabled,
policy.connections?.quickbooksOnline?.config?.autoSync?.enabled,
policy.connections?.xero?.config?.autoSync?.enabled,
];

return values.some((value) => !!value);
}

export {
canEditTaxRate,
canEnablePreventSelfApprovals,
Expand Down Expand Up @@ -1492,6 +1519,8 @@ export {
getPolicyNameByID,
getMostFrequentEmailDomain,
getDescriptionForPolicyDomainCard,
isPrefferedExporter,
isAutoSyncEnabled,
};

export type {MemberEmailsToAccountIDs};
188 changes: 188 additions & 0 deletions src/libs/ReportPrimaryActionUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
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 {isApprover as isApprovedMember} from './actions/Policy/Member';
import {getCurrentUserAccountID} from './actions/Report';
import {arePaymentsEnabled, getCorrectedAutoReportingFrequency, hasAccountingConnections, isAutoSyncEnabled, isPrefferedExporter} from './PolicyUtils';
import {
isClosedReport,
isCurrentUserSubmitter,
isExpenseReport,
isHoldCreator,
isInvoiceReport,
isIOUReport,
isOpenReport,
isPayer,
isProcessingReport,
isReportApproved,
isSettled,
} from './ReportUtils';
import {getSession} from './SessionUtils';
import {allHavePendingRTERViolation, isDuplicate, isOnHold as isOnHoldTransactionUtils, shouldShowBrokenConnectionViolationForMultipleTransactions} from './TransactionUtils';

function isSubmitAction(report: Report, policy: Policy) {
const isExpense = isExpenseReport(report);
const isSubmitter = isCurrentUserSubmitter(report.reportID);
const isOpen = isOpenReport(report);
const isManualSubmitEnabled = getCorrectedAutoReportingFrequency(policy) === CONST.POLICY.AUTO_REPORTING_FREQUENCIES.MANUAL;

return isExpense && isSubmitter && isOpen && isManualSubmitEnabled;
}

function isApproveAction(report: Report, policy: Policy, reportTransactions: Transaction[]) {
const isExpense = isExpenseReport(report);
const isApprover = isApprovedMember(policy, getCurrentUserAccountID());
const isApprovalEnabled = policy.approvalMode && policy.approvalMode !== CONST.POLICY.APPROVAL_MODE.OPTIONAL;

if (!isExpense || !isApprover || !isApprovalEnabled) {
return false;
}

const isOneExpenseReport = isExpense && reportTransactions.length === 1;
const isOnHold = reportTransactions.some(isOnHoldTransactionUtils);
const isProcessing = isProcessingReport(report);
const isOneExpenseReportOnHold = isOneExpenseReport && isOnHold;

if (isProcessing || isOneExpenseReportOnHold) {
return true;
}

return false;
}

function isPayAction(report: Report, policy: Policy) {
const isExpense = isExpenseReport(report);
const isReportPayer = isPayer(getSession(), report, false, policy);
const isPaymentsEnabled = arePaymentsEnabled(policy);
const isApproved = isReportApproved({report});
const isClosed = isClosedReport(report);
const isFinished = isApproved || isClosed;

if (isReportPayer && isExpense && isPaymentsEnabled && isFinished) {
return true;
}

const isProcessing = isProcessingReport(report);
const isInvoice = isInvoiceReport(report);
const isIOU = isIOUReport(report);

if ((isInvoice || isIOU) && isProcessing) {
return true;
}

return false;
}

function isExportAction(report: Report, policy: Policy) {
const hasAccountingConnection = hasAccountingConnections(policy);
if (!hasAccountingConnection) {
return false;
}

const isExporter = isPrefferedExporter(policy);
if (!isExporter) {
return false;
}

const syncEnabled = isAutoSyncEnabled(policy);
if (syncEnabled) {
return false;
}

const isReimbursed = isSettled(report);
const isApproved = isReportApproved({report});
const isClosed = isClosedReport(report);

if (isApproved || isReimbursed || isClosed) {
return true;
}

return false;
}

function isRemoveHoldAction(report: Report, reportTransactions: Transaction[]) {
const isOnHold = reportTransactions.some(isOnHoldTransactionUtils);
const isHolder = reportTransactions.some((transaction) => isHoldCreator(transaction, report.reportID));

return isOnHold && isHolder;
}

function isReviewDuplicatesAction(report: Report, policy: Policy, reportTransactions: Transaction[]) {
const hasDuplicates = reportTransactions.some((transaction) => isDuplicate(transaction.transactionID));

if (!hasDuplicates) {
return false;
}

const isApprover = isApprovedMember(policy, getCurrentUserAccountID());
const isSubmitter = isCurrentUserSubmitter(report.reportID);
const isProcessing = isProcessingReport(report);
const isOpen = isOpenReport(report);

const isSubmitterOrApprover = isSubmitter || isApprover;
const isActive = isOpen || isProcessing;

if (isSubmitterOrApprover && isActive) {
return true;
}

return false;
}

function isMarkAsCashAction(report: Report, policy: Policy, reportTransactions: Transaction[], violations: OnyxCollection<TransactionViolation[]>) {
const transactionIDs = reportTransactions.map((t) => t.transactionID);
const hasAllPendingRTERViolations = allHavePendingRTERViolation(transactionIDs, violations);

if (hasAllPendingRTERViolations) {
return true;
}

const isSubmitter = isCurrentUserSubmitter(report.reportID);
const isApprover = isApprovedMember(policy, getCurrentUserAccountID());
const isAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN;

const shouldShowBrokenConnectionViolation = shouldShowBrokenConnectionViolationForMultipleTransactions(transactionIDs, report, policy, violations);

const userControlsReport = isSubmitter || isApprover || isAdmin;
return userControlsReport && shouldShowBrokenConnectionViolation;
}

function getPrimaryAction(
report: Report,
policy: Policy,
reportTransactions: Transaction[],
violations: OnyxCollection<TransactionViolation[]>,
): ValueOf<typeof CONST.REPORT.PRIMARY_ACTIONS> | '' {
if (isSubmitAction(report, policy)) {
return CONST.REPORT.PRIMARY_ACTIONS.SUBMIT;
}

if (isApproveAction(report, policy, reportTransactions)) {
return CONST.REPORT.PRIMARY_ACTIONS.APPROVE;
}

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

if (isExportAction(report, policy)) {
return CONST.REPORT.PRIMARY_ACTIONS.EXPORT_TO_ACCOUNTING;
}

if (isRemoveHoldAction(report, reportTransactions)) {
return CONST.REPORT.PRIMARY_ACTIONS.REMOVE_HOLD;
}

if (isReviewDuplicatesAction(report, policy, reportTransactions)) {
return CONST.REPORT.PRIMARY_ACTIONS.REVIEW_DUPLICATES;
}

if (isMarkAsCashAction(report, policy, reportTransactions, violations)) {
return CONST.REPORT.PRIMARY_ACTIONS.MARK_AS_CASH;
}

return '';
}

export default getPrimaryAction;
5 changes: 5 additions & 0 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1589,6 +1589,10 @@ function isProcessingReport(report: OnyxEntry<Report>): boolean {
return report?.stateNum === CONST.REPORT.STATE_NUM.SUBMITTED && report?.statusNum === CONST.REPORT.STATUS_NUM.SUBMITTED;
}

function isOpenReport(report: OnyxEntry<Report>): boolean {
return report?.stateNum === CONST.REPORT.STATE_NUM.OPEN && report?.statusNum === CONST.REPORT.STATUS_NUM.OPEN;
}

function isAwaitingFirstLevelApproval(report: OnyxEntry<Report>): boolean {
if (!report) {
return false;
Expand Down Expand Up @@ -9446,6 +9450,7 @@ export {
isPolicyExpenseChat,
isPolicyExpenseChatAdmin,
isProcessingReport,
isOpenReport,
isReportIDApproved,
isAwaitingFirstLevelApproval,
isPublicAnnounceRoom,
Expand Down
Loading

0 comments on commit 23fa95c

Please sign in to comment.