Skip to content

Commit bd180d7

Browse files
authored
Merge pull request Expensify#76159 from callstack-internal/feat/72239-per-diem-field-create-report
Moving Per Diem expense from self DM
2 parents 50f5b59 + e59dbca commit bd180d7

5 files changed

Lines changed: 99 additions & 25 deletions

File tree

src/components/ReportActionItem/MoneyRequestView.tsx

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,15 @@ import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
3838
import {getRateFromMerchant} from '@libs/MergeTransactionUtils';
3939
import {hasEnabledOptions} from '@libs/OptionsListUtils';
4040
import Parser from '@libs/Parser';
41-
import {canSubmitPerDiemExpenseFromWorkspace, getLengthOfTag, getTagLists, hasDependentTags as hasDependentTagsPolicyUtils, isTaxTrackingEnabled} from '@libs/PolicyUtils';
41+
import {
42+
canSubmitPerDiemExpenseFromWorkspace,
43+
getLengthOfTag,
44+
getPerDiemCustomUnit,
45+
getPolicyByCustomUnitID,
46+
getTagLists,
47+
hasDependentTags as hasDependentTagsPolicyUtils,
48+
isTaxTrackingEnabled,
49+
} from '@libs/PolicyUtils';
4250
import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils';
4351
import {getReportName} from '@libs/ReportNameUtils';
4452
import {isSplitAction} from '@libs/ReportSecondaryActionUtils';
@@ -90,6 +98,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
9098
import ROUTES from '@src/ROUTES';
9199
import type * as OnyxTypes from '@src/types/onyx';
92100
import type {TransactionPendingFieldsKey} from '@src/types/onyx/Transaction';
101+
import {isEmptyObject} from '@src/types/utils/EmptyObject';
93102
import MoneyRequestReceiptView from './MoneyRequestReceiptView';
94103

95104
type MoneyRequestViewProps = {
@@ -118,6 +127,17 @@ type MoneyRequestViewProps = {
118127
mergeTransactionID?: string;
119128
};
120129

130+
const perDiemPoliciesSelector = (policies: OnyxCollection<OnyxTypes.Policy>) => {
131+
return Object.fromEntries(
132+
Object.entries(policies ?? {}).filter(([, policy]) => {
133+
const perDiemCustomUnit = getPerDiemCustomUnit(policy);
134+
const hasPolicyPerDiemRates = !isEmptyObject(perDiemCustomUnit?.rates);
135+
136+
return policy?.arePerDiemRatesEnabled && hasPolicyPerDiemRates;
137+
}),
138+
);
139+
};
140+
121141
function MoneyRequestView({
122142
allReports,
123143
report,
@@ -158,9 +178,28 @@ function MoneyRequestView({
158178
const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(linkedTransactionID)}`, {canBeMissing: true});
159179
const isExpenseUnreported = isExpenseUnreportedTransactionUtils(updatedTransaction ?? transaction);
160180
const {policyForMovingExpensesID, policyForMovingExpenses, shouldSelectPolicy} = usePolicyForMovingExpenses();
161-
// 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
162-
const policy = isExpenseUnreported ? policyForMovingExpenses : expensePolicy;
163-
const policyID = isExpenseUnreported ? policyForMovingExpensesID : report?.policyID;
181+
182+
const [policiesWithPerDiem] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {
183+
selector: perDiemPoliciesSelector,
184+
canBeMissing: true,
185+
});
186+
const isPerDiemRequest = isPerDiemRequestTransactionUtils(transaction);
187+
const perDiemOriginalPolicy = getPolicyByCustomUnitID(transaction, policiesWithPerDiem);
188+
189+
let policy;
190+
let policyID;
191+
// 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
192+
// the policy should be the one where the per diem rates are enabled, otherwise it should be the expense's report policy
193+
if (isExpenseUnreported && !isPerDiemRequest) {
194+
policy = policyForMovingExpenses;
195+
policyID = policyForMovingExpensesID;
196+
} else if (isExpenseUnreported && isPerDiemRequest) {
197+
policy = perDiemOriginalPolicy;
198+
policyID = perDiemOriginalPolicy?.id;
199+
} else {
200+
policy = expensePolicy;
201+
policyID = report?.policyID;
202+
}
164203

165204
const allPolicyCategories = usePolicyCategories();
166205
const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`];
@@ -220,7 +259,6 @@ function MoneyRequestView({
220259
const isDistanceRequest = isDistanceRequestTransactionUtils(transaction);
221260
const isManualDistanceRequest = isManualDistanceRequestTransactionUtils(transaction);
222261
const isMapDistanceRequest = isDistanceRequest && !isManualDistanceRequest;
223-
const isPerDiemRequest = isPerDiemRequestTransactionUtils(transaction);
224262
const isTransactionScanning = isScanning(updatedTransaction ?? transaction);
225263
const hasRoute = hasRouteTransactionUtils(transactionBackup ?? transaction, isDistanceRequest);
226264

@@ -271,13 +309,10 @@ function MoneyRequestView({
271309
const canEditDate = isEditable && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DATE, undefined, isChatReportArchived);
272310
const canEditDistance = isEditable && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DISTANCE, undefined, isChatReportArchived);
273311
const canEditDistanceRate = isEditable && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DISTANCE_RATE, undefined, isChatReportArchived);
274-
const canEditReport = useMemo(
275-
() =>
276-
isEditable &&
277-
canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.REPORT, undefined, isChatReportArchived, outstandingReportsByPolicyID) &&
278-
(!isPerDiemRequest || canSubmitPerDiemExpenseFromWorkspace(policy)),
279-
[isEditable, parentReportAction, isChatReportArchived, outstandingReportsByPolicyID, isPerDiemRequest, policy],
280-
);
312+
const canEditReport =
313+
isEditable &&
314+
canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.REPORT, undefined, isChatReportArchived, outstandingReportsByPolicyID) &&
315+
(!isPerDiemRequest || canSubmitPerDiemExpenseFromWorkspace(policy) || (isExpenseUnreported && !!perDiemOriginalPolicy));
281316

282317
// A flag for verifying that the current report is a sub-report of a expense chat
283318
// if the policy of the report is either Collect or Control, then this report must be tied to expense chat

src/libs/PolicyUtils.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import CONST from '@src/CONST';
88
import ONYXKEYS from '@src/ONYXKEYS';
99
import ROUTES from '@src/ROUTES';
1010
import INPUT_IDS from '@src/types/form/NetSuiteCustomFieldForm';
11-
import type {OnyxInputOrEntry, Policy, PolicyCategories, PolicyEmployeeList, PolicyTagLists, PolicyTags, Report, TaxRate} from '@src/types/onyx';
11+
import type {OnyxInputOrEntry, Policy, PolicyCategories, PolicyEmployeeList, PolicyTagLists, PolicyTags, Report, TaxRate, Transaction} from '@src/types/onyx';
1212
import type {ErrorFields, PendingAction, PendingFields} from '@src/types/onyx/OnyxCommon';
1313
import type {
1414
ConnectionLastSync,
@@ -182,6 +182,24 @@ function getPerDiemCustomUnit(policy: OnyxEntry<Policy>): CustomUnit | undefined
182182
return Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_PER_DIEM_INTERNATIONAL);
183183
}
184184

185+
/**
186+
* Finds a policy that contains the customUnitID from the transaction
187+
*/
188+
function getPolicyByCustomUnitID(transaction: OnyxEntry<Transaction>, policies: OnyxCollection<Policy>): OnyxEntry<Policy> {
189+
const customUnitID = transaction?.comment?.customUnit?.customUnitID;
190+
191+
if (!customUnitID || !policies) {
192+
return undefined;
193+
}
194+
195+
return Object.values(policies).find((policy) => {
196+
if (!policy?.customUnits || !policy?.arePerDiemRatesEnabled) {
197+
return false;
198+
}
199+
return customUnitID in policy.customUnits;
200+
});
201+
}
202+
185203
/**
186204
* Retrieves custom unit rate object from the given customUnitRateID
187205
*/
@@ -1677,6 +1695,7 @@ export {
16771695
getSageIntacctBankAccounts,
16781696
getDistanceRateCustomUnit,
16791697
getPerDiemCustomUnit,
1698+
getPolicyByCustomUnitID,
16801699
getDistanceRateCustomUnitRate,
16811700
getPerDiemRateCustomUnitRate,
16821701
sortWorkspacesBySelected,

src/pages/iou/request/step/IOURequestEditReport.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,12 @@ function IOURequestEditReport({route}: IOURequestEditReportProps) {
9898
};
9999

100100
const createReportForPolicy = (shouldDismissEmptyReportsConfirmation?: boolean) => {
101-
if (!policyForMovingExpensesID) {
101+
if (!hasPerDiemTransactions && !policyForMovingExpensesID) {
102102
return;
103103
}
104104

105-
const optimisticReport = createNewReport(currentUserPersonalDetails, hasViolations, isASAPSubmitBetaEnabled, policyForMovingExpensesID, false, shouldDismissEmptyReportsConfirmation);
105+
const policyForNewReportID = hasPerDiemTransactions ? selectedReport?.policyID : policyForMovingExpensesID;
106+
const optimisticReport = createNewReport(currentUserPersonalDetails, hasViolations, isASAPSubmitBetaEnabled, policyForNewReportID, false, shouldDismissEmptyReportsConfirmation);
106107
selectReport({value: optimisticReport.reportID}, optimisticReport);
107108
};
108109

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

116117
const createReport = () => {
117-
if (!policyForMovingExpensesID && !shouldSelectPolicy) {
118+
if (hasPerDiemTransactions) {
119+
handleCreateReport();
120+
return;
121+
}
122+
if (!hasPerDiemTransactions && !policyForMovingExpensesID && !shouldSelectPolicy) {
118123
return;
119124
}
120125
if (shouldSelectPolicy) {

src/pages/iou/request/step/IOURequestEditReportCommon.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ import useOnyx from '@hooks/useOnyx';
1616
import usePolicy from '@hooks/usePolicy';
1717
import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses';
1818
import useReportTransactions from '@hooks/useReportTransactions';
19+
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
1920
import Navigation from '@libs/Navigation/Navigation';
20-
import {canSubmitPerDiemExpenseFromWorkspace, getPersonalPolicy, isPolicyAdmin} from '@libs/PolicyUtils';
21+
import {canSubmitPerDiemExpenseFromWorkspace, getPersonalPolicy, getPolicyByCustomUnitID, isPolicyAdmin} from '@libs/PolicyUtils';
2122
import {
2223
canAddTransaction,
2324
getOutstandingReportsForUser,
@@ -79,6 +80,7 @@ function IOURequestEditReportCommon({
7980
const {options} = useOptionsList();
8081
const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID, {canBeMissing: true});
8182
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
83+
const [firstTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(transactionIDs?.at(0))}`, {canBeMissing: true});
8284
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
8385
const [selectedReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`, {canBeMissing: true});
8486
const resolvedReportOwnerAccountID = useMemo(() => {
@@ -95,6 +97,7 @@ function IOURequestEditReportCommon({
9597
const reportPolicy = usePolicy(selectedReport?.policyID);
9698
const {policyForMovingExpenses} = usePolicyForMovingExpenses(isPerDiemRequest);
9799

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

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

116119
return reportTransactions
117-
.filter((transaction) => transactionIDs.includes(transaction.transactionID))
118-
.some((transaction) => transaction?.comment?.liabilityType === CONST.TRANSACTION.LIABILITY_TYPE.RESTRICT);
120+
.filter((reportTransaction) => transactionIDs.includes(reportTransaction.transactionID))
121+
.some((reportTransaction) => reportTransaction?.comment?.liabilityType === CONST.TRANSACTION.LIABILITY_TYPE.RESTRICT);
119122
}, [transactionIDs, selectedReport, reportTransactions]);
120123

121124
const shouldShowRemoveFromReport =
@@ -164,6 +167,9 @@ function IOURequestEditReportCommon({
164167
.filter((report) => !debouncedSearchValue || report?.reportName?.toLowerCase().includes(debouncedSearchValue.toLowerCase()))
165168
.filter((report): report is NonNullable<typeof report> => report !== undefined)
166169
.filter((report) => {
170+
if (isPerDiemRequest && report?.policyID !== perDiemOriginalPolicy?.id) {
171+
return false;
172+
}
167173
if (isPerDiemRequest && report?.policyID && selectedReportID !== report?.reportID) {
168174
const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`];
169175
return canSubmitPerDiemExpenseFromWorkspace(policy);
@@ -206,6 +212,7 @@ function IOURequestEditReportCommon({
206212
allPolicies,
207213
isPerDiemRequest,
208214
currentUserPersonalDetails.accountID,
215+
perDiemOriginalPolicy?.id,
209216
]);
210217

211218
const navigateBack = () => {
@@ -223,11 +230,11 @@ function IOURequestEditReportCommon({
223230
<MenuItem
224231
onPress={createReport}
225232
title={translate('report.newReport.createReport')}
226-
description={policyForMovingExpenses?.name}
233+
description={isPerDiemRequest ? perDiemOriginalPolicy?.name : policyForMovingExpenses?.name}
227234
icon={icons.Document}
228235
/>
229236
);
230-
}, [icons.Document, createReport, isEditing, isOwner, translate, policyForMovingExpenses?.name]);
237+
}, [icons.Document, createReport, isEditing, isOwner, translate, policyForMovingExpenses?.name, perDiemOriginalPolicy?.name, isPerDiemRequest]);
231238

232239
// eslint-disable-next-line rulesdir/no-negated-variables
233240
const shouldShowNotFoundPage = useMemo(() => {

src/pages/iou/request/step/IOURequestStepReport.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import useShowNotFoundPageInIOUStep from '@hooks/useShowNotFoundPageInIOUStep';
1414
import {createNewReport} from '@libs/actions/Report';
1515
import {changeTransactionsReport, setTransactionReport} from '@libs/actions/Transaction';
1616
import Navigation from '@libs/Navigation/Navigation';
17+
import {getPolicyByCustomUnitID} from '@libs/PolicyUtils';
1718
import {getReportOrDraftReport, hasViolations as hasViolationsReportUtils, isPolicyExpenseChat, isReportOutstanding} from '@libs/ReportUtils';
1819
import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils';
1920
import {isPerDiemRequest} from '@libs/TransactionUtils';
@@ -61,6 +62,8 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) {
6162
const hasViolations = hasViolationsReportUtils(undefined, transactionViolations, session?.accountID ?? CONST.DEFAULT_NUMBER_ID, session?.email ?? '');
6263
const policyForMovingExpenses = policyForMovingExpensesID ? allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyForMovingExpensesID}`] : undefined;
6364
useRestartOnReceiptFailure(transaction, reportIDFromRoute, iouType, action);
65+
const isPerDiemTransaction = isPerDiemRequest(transaction);
66+
const perDiemOriginalPolicy = getPolicyByCustomUnitID(transaction, allPolicies);
6467

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

177180
const createReportForPolicy = (shouldDismissEmptyReportsConfirmation?: boolean) => {
178-
if (!policyForMovingExpensesID) {
181+
if (!isPerDiemTransaction && !policyForMovingExpensesID) {
179182
return;
180183
}
181184

182-
const optimisticReport = createNewReport(currentUserPersonalDetails, hasViolations, isASAPSubmitBetaEnabled, policyForMovingExpensesID, false, shouldDismissEmptyReportsConfirmation);
185+
const policyForNewReport = isPerDiemTransaction && perDiemOriginalPolicy ? perDiemOriginalPolicy.id : policyForMovingExpensesID;
186+
const optimisticReport = createNewReport(currentUserPersonalDetails, hasViolations, isASAPSubmitBetaEnabled, policyForNewReport, false, shouldDismissEmptyReportsConfirmation);
183187
handleRegularReportSelection({value: optimisticReport.reportID}, optimisticReport);
184188
};
185189

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

193197
const createReport = () => {
194-
if (!policyForMovingExpensesID && !shouldSelectPolicy) {
198+
if (isPerDiemTransaction) {
199+
handleCreateReport();
200+
return;
201+
}
202+
if (!isPerDiemTransaction && !policyForMovingExpensesID && !shouldSelectPolicy) {
195203
return;
196204
}
197205
if (shouldSelectPolicy) {
@@ -220,7 +228,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) {
220228
isUnreported={isUnreported}
221229
shouldShowNotFoundPage={shouldShowNotFoundPage}
222230
isPerDiemRequest={transaction ? isPerDiemRequest(transaction) : false}
223-
createReport={action === CONST.IOU.ACTION.EDIT && (policyForMovingExpensesID || shouldSelectPolicy) ? createReport : undefined}
231+
createReport={action === CONST.IOU.ACTION.EDIT && (policyForMovingExpensesID || shouldSelectPolicy || isPerDiemTransaction) ? createReport : undefined}
224232
/>
225233
</>
226234
);

0 commit comments

Comments
 (0)