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
59 changes: 47 additions & 12 deletions src/components/ReportActionItem/MoneyRequestView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,15 @@ import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
import {getRateFromMerchant} from '@libs/MergeTransactionUtils';
import {hasEnabledOptions} from '@libs/OptionsListUtils';
import Parser from '@libs/Parser';
import {canSubmitPerDiemExpenseFromWorkspace, getLengthOfTag, getTagLists, hasDependentTags as hasDependentTagsPolicyUtils, isTaxTrackingEnabled} from '@libs/PolicyUtils';
import {
canSubmitPerDiemExpenseFromWorkspace,
getLengthOfTag,
getPerDiemCustomUnit,
getPolicyByCustomUnitID,
getTagLists,
hasDependentTags as hasDependentTagsPolicyUtils,
isTaxTrackingEnabled,
} from '@libs/PolicyUtils';
import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils';
import {getReportName} from '@libs/ReportNameUtils';
import {isSplitAction} from '@libs/ReportSecondaryActionUtils';
Expand Down Expand Up @@ -90,6 +98,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type * as OnyxTypes from '@src/types/onyx';
import type {TransactionPendingFieldsKey} from '@src/types/onyx/Transaction';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import MoneyRequestReceiptView from './MoneyRequestReceiptView';

type MoneyRequestViewProps = {
Expand Down Expand Up @@ -118,6 +127,17 @@ type MoneyRequestViewProps = {
mergeTransactionID?: string;
};

const perDiemPoliciesSelector = (policies: OnyxCollection<OnyxTypes.Policy>) => {
return Object.fromEntries(
Object.entries(policies ?? {}).filter(([, policy]) => {
const perDiemCustomUnit = getPerDiemCustomUnit(policy);
const hasPolicyPerDiemRates = !isEmptyObject(perDiemCustomUnit?.rates);

return policy?.arePerDiemRatesEnabled && hasPolicyPerDiemRates;
}),
);
};

function MoneyRequestView({
allReports,
report,
Expand Down Expand Up @@ -158,9 +178,28 @@ function MoneyRequestView({
const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(linkedTransactionID)}`, {canBeMissing: true});
const isExpenseUnreported = isExpenseUnreportedTransactionUtils(updatedTransaction ?? transaction);
const {policyForMovingExpensesID, policyForMovingExpenses, shouldSelectPolicy} = usePolicyForMovingExpenses();
// If the expense is unreported the policy should be the user's default policy, otherwise it should be the policy the expense was made for
const policy = isExpenseUnreported ? policyForMovingExpenses : expensePolicy;
const policyID = isExpenseUnreported ? policyForMovingExpensesID : report?.policyID;

const [policiesWithPerDiem] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {
selector: perDiemPoliciesSelector,
canBeMissing: true,
});
const isPerDiemRequest = isPerDiemRequestTransactionUtils(transaction);
const perDiemOriginalPolicy = getPolicyByCustomUnitID(transaction, policiesWithPerDiem);

let policy;
let policyID;
// If the expense is unreported the policy should be the user's default policy, if the expense is a per diem request and is unreported
// the policy should be the one where the per diem rates are enabled, otherwise it should be the expense's report policy
if (isExpenseUnreported && !isPerDiemRequest) {
policy = policyForMovingExpenses;
policyID = policyForMovingExpensesID;
} else if (isExpenseUnreported && isPerDiemRequest) {
policy = perDiemOriginalPolicy;
policyID = perDiemOriginalPolicy?.id;
} else {
policy = expensePolicy;
policyID = report?.policyID;
}

const allPolicyCategories = usePolicyCategories();
const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`];
Expand Down Expand Up @@ -220,7 +259,6 @@ function MoneyRequestView({
const isDistanceRequest = isDistanceRequestTransactionUtils(transaction);
const isManualDistanceRequest = isManualDistanceRequestTransactionUtils(transaction);
const isMapDistanceRequest = isDistanceRequest && !isManualDistanceRequest;
const isPerDiemRequest = isPerDiemRequestTransactionUtils(transaction);
const isTransactionScanning = isScanning(updatedTransaction ?? transaction);
const hasRoute = hasRouteTransactionUtils(transactionBackup ?? transaction, isDistanceRequest);

Expand Down Expand Up @@ -271,13 +309,10 @@ function MoneyRequestView({
const canEditDate = isEditable && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DATE, undefined, isChatReportArchived);
const canEditDistance = isEditable && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DISTANCE, undefined, isChatReportArchived);
const canEditDistanceRate = isEditable && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DISTANCE_RATE, undefined, isChatReportArchived);
const canEditReport = useMemo(
() =>
isEditable &&
canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.REPORT, undefined, isChatReportArchived, outstandingReportsByPolicyID) &&
(!isPerDiemRequest || canSubmitPerDiemExpenseFromWorkspace(policy)),
[isEditable, parentReportAction, isChatReportArchived, outstandingReportsByPolicyID, isPerDiemRequest, policy],
);
const canEditReport =
isEditable &&
canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.REPORT, undefined, isChatReportArchived, outstandingReportsByPolicyID) &&
(!isPerDiemRequest || canSubmitPerDiemExpenseFromWorkspace(policy) || (isExpenseUnreported && !!perDiemOriginalPolicy));

// A flag for verifying that the current report is a sub-report of a expense chat
// if the policy of the report is either Collect or Control, then this report must be tied to expense chat
Expand Down
21 changes: 20 additions & 1 deletion src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import INPUT_IDS from '@src/types/form/NetSuiteCustomFieldForm';
import type {OnyxInputOrEntry, Policy, PolicyCategories, PolicyEmployeeList, PolicyTagLists, PolicyTags, Report, TaxRate} from '@src/types/onyx';
import type {OnyxInputOrEntry, Policy, PolicyCategories, PolicyEmployeeList, PolicyTagLists, PolicyTags, Report, TaxRate, Transaction} from '@src/types/onyx';
import type {ErrorFields, PendingAction, PendingFields} from '@src/types/onyx/OnyxCommon';
import type {
ConnectionLastSync,
Expand Down Expand Up @@ -55,7 +55,7 @@

let allPolicies: OnyxCollection<Policy>;

Onyx.connect({

Check warning on line 58 in src/libs/PolicyUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY,
waitForCollectionCallback: true,
callback: (value) => (allPolicies = value),
Expand Down Expand Up @@ -182,6 +182,24 @@
return Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_PER_DIEM_INTERNATIONAL);
}

/**
* Finds a policy that contains the customUnitID from the transaction
*/
function getPolicyByCustomUnitID(transaction: OnyxEntry<Transaction>, policies: OnyxCollection<Policy>): OnyxEntry<Policy> {
const customUnitID = transaction?.comment?.customUnit?.customUnitID;

if (!customUnitID || !policies) {
return undefined;
}

return Object.values(policies).find((policy) => {
if (!policy?.customUnits || !policy?.arePerDiemRatesEnabled) {
return false;
}
return customUnitID in policy.customUnits;
});
}

/**
* Retrieves custom unit rate object from the given customUnitRateID
*/
Expand Down Expand Up @@ -1677,6 +1695,7 @@
getSageIntacctBankAccounts,
getDistanceRateCustomUnit,
getPerDiemCustomUnit,
getPolicyByCustomUnitID,
getDistanceRateCustomUnitRate,
getPerDiemRateCustomUnitRate,
sortWorkspacesBySelected,
Expand Down
11 changes: 8 additions & 3 deletions src/pages/iou/request/step/IOURequestEditReport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,12 @@ function IOURequestEditReport({route}: IOURequestEditReportProps) {
};

const createReportForPolicy = (shouldDismissEmptyReportsConfirmation?: boolean) => {
if (!policyForMovingExpensesID) {
if (!hasPerDiemTransactions && !policyForMovingExpensesID) {
return;
}

const optimisticReport = createNewReport(currentUserPersonalDetails, hasViolations, isASAPSubmitBetaEnabled, policyForMovingExpensesID, false, shouldDismissEmptyReportsConfirmation);
const policyForNewReportID = hasPerDiemTransactions ? selectedReport?.policyID : policyForMovingExpensesID;
const optimisticReport = createNewReport(currentUserPersonalDetails, hasViolations, isASAPSubmitBetaEnabled, policyForNewReportID, false, shouldDismissEmptyReportsConfirmation);
selectReport({value: optimisticReport.reportID}, optimisticReport);
};

Expand All @@ -114,7 +115,11 @@ function IOURequestEditReport({route}: IOURequestEditReportProps) {
});

const createReport = () => {
if (!policyForMovingExpensesID && !shouldSelectPolicy) {
if (hasPerDiemTransactions) {
handleCreateReport();

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 #86579, we need to check whether we should restrict the current user from billable actions

return;
}
if (!hasPerDiemTransactions && !policyForMovingExpensesID && !shouldSelectPolicy) {
return;
}
if (shouldSelectPolicy) {
Expand Down
17 changes: 12 additions & 5 deletions src/pages/iou/request/step/IOURequestEditReportCommon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ import useOnyx from '@hooks/useOnyx';
import usePolicy from '@hooks/usePolicy';
import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses';
import useReportTransactions from '@hooks/useReportTransactions';
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
import Navigation from '@libs/Navigation/Navigation';
import {canSubmitPerDiemExpenseFromWorkspace, getPersonalPolicy, isPolicyAdmin} from '@libs/PolicyUtils';
import {canSubmitPerDiemExpenseFromWorkspace, getPersonalPolicy, getPolicyByCustomUnitID, isPolicyAdmin} from '@libs/PolicyUtils';
import {
canAddTransaction,
getOutstandingReportsForUser,
Expand Down Expand Up @@ -79,6 +80,7 @@ function IOURequestEditReportCommon({
const {options} = useOptionsList();
const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID, {canBeMissing: true});
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
const [firstTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(transactionIDs?.at(0))}`, {canBeMissing: true});
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
const [selectedReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`, {canBeMissing: true});
const resolvedReportOwnerAccountID = useMemo(() => {
Expand All @@ -95,6 +97,7 @@ function IOURequestEditReportCommon({
const reportPolicy = usePolicy(selectedReport?.policyID);
const {policyForMovingExpenses} = usePolicyForMovingExpenses(isPerDiemRequest);

const perDiemOriginalPolicy = getPolicyByCustomUnitID(firstTransaction, allPolicies);
const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, {canBeMissing: true});

const [allPoliciesID] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: policiesSelector, canBeMissing: false});
Expand All @@ -114,8 +117,8 @@ function IOURequestEditReportCommon({
}

return reportTransactions
.filter((transaction) => transactionIDs.includes(transaction.transactionID))
.some((transaction) => transaction?.comment?.liabilityType === CONST.TRANSACTION.LIABILITY_TYPE.RESTRICT);
.filter((reportTransaction) => transactionIDs.includes(reportTransaction.transactionID))
.some((reportTransaction) => reportTransaction?.comment?.liabilityType === CONST.TRANSACTION.LIABILITY_TYPE.RESTRICT);
}, [transactionIDs, selectedReport, reportTransactions]);

const shouldShowRemoveFromReport =
Expand Down Expand Up @@ -164,6 +167,9 @@ function IOURequestEditReportCommon({
.filter((report) => !debouncedSearchValue || report?.reportName?.toLowerCase().includes(debouncedSearchValue.toLowerCase()))
.filter((report): report is NonNullable<typeof report> => report !== undefined)
.filter((report) => {
if (isPerDiemRequest && report?.policyID !== perDiemOriginalPolicy?.id) {
return false;
}
Comment thread
koko57 marked this conversation as resolved.
if (isPerDiemRequest && report?.policyID && selectedReportID !== report?.reportID) {
const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`];
return canSubmitPerDiemExpenseFromWorkspace(policy);
Expand Down Expand Up @@ -206,6 +212,7 @@ function IOURequestEditReportCommon({
allPolicies,
isPerDiemRequest,
currentUserPersonalDetails.accountID,
perDiemOriginalPolicy?.id,
]);

const navigateBack = () => {
Expand All @@ -223,11 +230,11 @@ function IOURequestEditReportCommon({
<MenuItem
onPress={createReport}
title={translate('report.newReport.createReport')}
description={policyForMovingExpenses?.name}
description={isPerDiemRequest ? perDiemOriginalPolicy?.name : policyForMovingExpenses?.name}
icon={icons.Document}
/>
);
}, [icons.Document, createReport, isEditing, isOwner, translate, policyForMovingExpenses?.name]);
}, [icons.Document, createReport, isEditing, isOwner, translate, policyForMovingExpenses?.name, perDiemOriginalPolicy?.name, isPerDiemRequest]);

// eslint-disable-next-line rulesdir/no-negated-variables
const shouldShowNotFoundPage = useMemo(() => {
Expand Down
16 changes: 12 additions & 4 deletions src/pages/iou/request/step/IOURequestStepReport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import useShowNotFoundPageInIOUStep from '@hooks/useShowNotFoundPageInIOUStep';
import {createNewReport} from '@libs/actions/Report';
import {changeTransactionsReport, setTransactionReport} from '@libs/actions/Transaction';
import Navigation from '@libs/Navigation/Navigation';
import {getPolicyByCustomUnitID} from '@libs/PolicyUtils';
import {getReportOrDraftReport, hasViolations as hasViolationsReportUtils, isPolicyExpenseChat, isReportOutstanding} from '@libs/ReportUtils';
import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils';
import {isPerDiemRequest} from '@libs/TransactionUtils';
Expand Down Expand Up @@ -61,6 +62,8 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) {
const hasViolations = hasViolationsReportUtils(undefined, transactionViolations, session?.accountID ?? CONST.DEFAULT_NUMBER_ID, session?.email ?? '');
const policyForMovingExpenses = policyForMovingExpensesID ? allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyForMovingExpensesID}`] : undefined;
useRestartOnReceiptFailure(transaction, reportIDFromRoute, iouType, action);
const isPerDiemTransaction = isPerDiemRequest(transaction);
const perDiemOriginalPolicy = getPolicyByCustomUnitID(transaction, allPolicies);

const handleGoBack = () => {
if (isEditing) {
Expand Down Expand Up @@ -175,11 +178,12 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) {
const shouldShowNotFoundPage = useShowNotFoundPageInIOUStep(action, iouType, reportActionID, reportOrDraftReport, transaction);

const createReportForPolicy = (shouldDismissEmptyReportsConfirmation?: boolean) => {
if (!policyForMovingExpensesID) {
if (!isPerDiemTransaction && !policyForMovingExpensesID) {
return;
}

const optimisticReport = createNewReport(currentUserPersonalDetails, hasViolations, isASAPSubmitBetaEnabled, policyForMovingExpensesID, false, shouldDismissEmptyReportsConfirmation);
const policyForNewReport = isPerDiemTransaction && perDiemOriginalPolicy ? perDiemOriginalPolicy.id : policyForMovingExpensesID;
const optimisticReport = createNewReport(currentUserPersonalDetails, hasViolations, isASAPSubmitBetaEnabled, policyForNewReport, false, shouldDismissEmptyReportsConfirmation);
handleRegularReportSelection({value: optimisticReport.reportID}, optimisticReport);
};

Expand All @@ -191,7 +195,11 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) {
});

const createReport = () => {
if (!policyForMovingExpensesID && !shouldSelectPolicy) {
if (isPerDiemTransaction) {
handleCreateReport();
return;
}
if (!isPerDiemTransaction && !policyForMovingExpensesID && !shouldSelectPolicy) {
return;
}
if (shouldSelectPolicy) {
Expand Down Expand Up @@ -220,7 +228,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) {
isUnreported={isUnreported}
shouldShowNotFoundPage={shouldShowNotFoundPage}
isPerDiemRequest={transaction ? isPerDiemRequest(transaction) : false}
createReport={action === CONST.IOU.ACTION.EDIT && (policyForMovingExpensesID || shouldSelectPolicy) ? createReport : undefined}
createReport={action === CONST.IOU.ACTION.EDIT && (policyForMovingExpensesID || shouldSelectPolicy || isPerDiemTransaction) ? createReport : undefined}
/>
</>
);
Expand Down
Loading