Skip to content

Commit d3edd14

Browse files
committed
Merge branch 'main' into fix/67371-invalid-rate-error-not-seen
2 parents c70a6c7 + 17051f9 commit d3edd14

11 files changed

Lines changed: 302 additions & 82 deletions

File tree

__mocks__/react-native-onyx.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type ConnectionCallbackParams<TKey extends OnyxKey> = Parameters<ConnectionCallb
2222

2323
const reactNativeOnyxMock: ReactNativeOnyxMock = {
2424
...Onyx,
25-
connect: <TKey extends OnyxKey>(mapping: ConnectOptions<TKey>) => {
25+
connectWithoutView: <TKey extends OnyxKey>(mapping: ConnectOptions<TKey>) => {
2626
const callback = (...params: ConnectionCallbackParams<TKey>) => {
2727
if (connectCallbackDelay > 0) {
2828
setTimeout(() => {
@@ -32,7 +32,7 @@ const reactNativeOnyxMock: ReactNativeOnyxMock = {
3232
(mapping.callback as (...args: ConnectionCallbackParams<TKey>) => void)?.(...params);
3333
}
3434
};
35-
return Onyx.connect({
35+
return Onyx.connectWithoutView({
3636
...mapping,
3737
callback,
3838
});
@@ -41,4 +41,5 @@ const reactNativeOnyxMock: ReactNativeOnyxMock = {
4141
};
4242

4343
export default reactNativeOnyxMock;
44+
// eslint-disable-next-line
4445
export {withOnyx, useOnyx};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"test:debug": "TZ=utc NODE_OPTIONS='--inspect-brk --experimental-vm-modules' jest --runInBand",
4747
"perf-test": "NODE_OPTIONS=--experimental-vm-modules npx reassure",
4848
"typecheck": "NODE_OPTIONS=--max_old_space_size=8192 tsc",
49-
"lint": "NODE_OPTIONS=--max_old_space_size=8192 eslint . --max-warnings=254 --cache --cache-location=node_modules/.cache/eslint",
49+
"lint": "NODE_OPTIONS=--max_old_space_size=8192 eslint . --max-warnings=250 --cache --cache-location=node_modules/.cache/eslint",
5050
"lint-changed": "NODE_OPTIONS=--max_old_space_size=8192 ./scripts/lintChanged.sh",
5151
"lint-watch": "npx eslint-watch --watch --changed",
5252
"shellcheck": "./scripts/shellCheck.sh",

src/libs/Network/NetworkStore.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ function resetHasReadRequiredDataFromStorage() {
4949
});
5050
}
5151

52-
Onyx.connect({
52+
// Use connectWithoutView since this doesn't affect to any UI
53+
Onyx.connectWithoutView({
5354
key: ONYXKEYS.SESSION,
5455
callback: (val) => {
5556
authToken = val?.authToken ?? null;
@@ -59,7 +60,8 @@ Onyx.connect({
5960
},
6061
});
6162

62-
Onyx.connect({
63+
// Use connectWithoutView since this doesn't affect to any UI
64+
Onyx.connectWithoutView({
6365
key: ONYXKEYS.CREDENTIALS,
6466
callback: (val) => {
6567
credentials = val ?? null;
@@ -69,7 +71,8 @@ Onyx.connect({
6971

7072
// We subscribe to the online/offline status of the network to determine when we should fire off API calls
7173
// vs queueing them for later.
72-
Onyx.connect({
74+
// Use connectWithoutView since this doesn't affect to any UI
75+
Onyx.connectWithoutView({
7376
key: ONYXKEYS.NETWORK,
7477
callback: (network) => {
7578
if (!network) {

src/libs/actions/Policy/DistanceRate.ts

Lines changed: 28 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -14,48 +14,14 @@ import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
1414
import * as ErrorUtils from '@libs/ErrorUtils';
1515
import getIsNarrowLayout from '@libs/getIsNarrowLayout';
1616
import {buildOnyxDataForPolicyDistanceRateUpdates} from '@libs/PolicyDistanceRatesUtils';
17-
import {getDistanceRateCustomUnit, goBackWhenEnableFeature, removePendingFieldsFromCustomUnit} from '@libs/PolicyUtils';
18-
import * as ReportUtils from '@libs/ReportUtils';
17+
import {goBackWhenEnableFeature, removePendingFieldsFromCustomUnit} from '@libs/PolicyUtils';
1918
import CONST from '@src/CONST';
2019
import ONYXKEYS from '@src/ONYXKEYS';
21-
import type {Policy, Report, TransactionViolation} from '@src/types/onyx';
20+
import type {TransactionViolation} from '@src/types/onyx';
2221
import type {ErrorFields} from '@src/types/onyx/OnyxCommon';
2322
import type {CustomUnit, Rate} from '@src/types/onyx/Policy';
2423
import type {OnyxData} from '@src/types/onyx/Request';
2524

26-
const allPolicies: OnyxCollection<Policy> = {};
27-
Onyx.connect({
28-
key: ONYXKEYS.COLLECTION.POLICY,
29-
callback: (val, key) => {
30-
if (!key) {
31-
return;
32-
}
33-
if (val === null || val === undefined) {
34-
// If we are deleting a policy, we have to check every report linked to that policy
35-
// and unset the draft indicator (pencil icon) alongside removing any draft comments. Clearing these values will keep the newly archived chats from being displayed in the LHN.
36-
// More info: https://github.com/Expensify/App/issues/14260
37-
const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, '');
38-
const policyReports = ReportUtils.getAllPolicyReports(policyID);
39-
const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep<Report>> = {};
40-
const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {};
41-
policyReports.forEach((policyReport) => {
42-
if (!policyReport) {
43-
return;
44-
}
45-
const {reportID} = policyReport;
46-
cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null;
47-
cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null;
48-
});
49-
Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries);
50-
Onyx.multiSet(cleanUpSetQueries);
51-
delete allPolicies[key];
52-
return;
53-
}
54-
55-
allPolicies[key] = val;
56-
},
57-
});
58-
5925
/**
6026
* Takes array of customUnitRates and removes pendingFields and errorFields from each rate - we don't want to send those via API
6127
*/
@@ -81,7 +47,7 @@ function openPolicyDistanceRatesPage(policyID?: string) {
8147
API.read(READ_COMMANDS.OPEN_POLICY_DISTANCE_RATES_PAGE, params);
8248
}
8349

84-
function enablePolicyDistanceRates(policyID: string, enabled: boolean) {
50+
function enablePolicyDistanceRates(policyID: string, enabled: boolean, customUnit: CustomUnit | undefined) {
8551
const onyxData: OnyxData = {
8652
optimisticData: [
8753
{
@@ -120,39 +86,34 @@ function enablePolicyDistanceRates(policyID: string, enabled: boolean) {
12086
],
12187
};
12288

123-
if (!enabled) {
124-
const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`];
125-
const customUnit = getDistanceRateCustomUnit(policy);
126-
if (customUnit) {
127-
const customUnitID = customUnit.customUnitID;
128-
129-
const rateEntries = Object.entries(customUnit.rates ?? {});
130-
// find the rate to be enabled after disabling the distance rate feature
131-
const rateEntryToBeEnabled = rateEntries.at(0);
89+
if (!enabled && customUnit) {
90+
const customUnitID = customUnit.customUnitID;
91+
const rateEntries = Object.entries(customUnit.rates ?? {});
92+
// find the rate to be enabled after disabling the distance rate feature
93+
const rateEntryToBeEnabled = rateEntries.at(0);
13294

133-
onyxData.optimisticData?.push({
134-
onyxMethod: Onyx.METHOD.MERGE,
135-
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
136-
value: {
137-
customUnits: {
138-
[customUnitID]: {
139-
rates: Object.fromEntries(
140-
rateEntries.map((rateEntry) => {
141-
const [rateID, rate] = rateEntry;
142-
return [
143-
rateID,
144-
{
145-
...rate,
146-
enabled: rateID === rateEntryToBeEnabled?.at(0),
147-
},
148-
];
149-
}),
150-
),
151-
},
95+
onyxData.optimisticData?.push({
96+
onyxMethod: Onyx.METHOD.MERGE,
97+
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
98+
value: {
99+
customUnits: {
100+
[customUnitID]: {
101+
rates: Object.fromEntries(
102+
rateEntries.map((rateEntry) => {
103+
const [rateID, rate] = rateEntry;
104+
return [
105+
rateID,
106+
{
107+
...rate,
108+
enabled: rateID === rateEntryToBeEnabled?.at(0),
109+
},
110+
];
111+
}),
112+
),
152113
},
153114
},
154-
});
155-
}
115+
},
116+
});
156117
}
157118

158119
const parameters: EnablePolicyDistanceRatesParams = {policyID, enabled};

src/libs/actions/Tour.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,16 @@ import ROUTES from '@src/ROUTES';
55
import type {IntroSelected} from './Report';
66
import {completeTestDriveTask} from './Task';
77

8-
function startTestDrive(introSelected: IntroSelected | undefined, shouldUpdateSelfTourViewedOnlyLocally = false, hasUserBeenAddedToNudgeMigration = false) {
8+
function startTestDrive(
9+
introSelected: IntroSelected | undefined,
10+
shouldUpdateSelfTourViewedOnlyLocally: boolean,
11+
hasUserBeenAddedToNudgeMigration: boolean,
12+
isUserPaidPolicyMember: boolean,
13+
) {
914
InteractionManager.runAfterInteractions(() => {
1015
if (
1116
hasUserBeenAddedToNudgeMigration ||
17+
isUserPaidPolicyMember ||
1218
introSelected?.choice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ||
1319
introSelected?.choice === CONST.ONBOARDING_CHOICES.TEST_DRIVE_RECEIVER ||
1420
introSelected?.choice === CONST.ONBOARDING_CHOICES.TRACK_WORKSPACE ||

src/pages/AddUnreportedExpense.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ import useThemeStyles from '@hooks/useThemeStyles';
1717
import {fetchUnreportedExpenses} from '@libs/actions/UnreportedExpenses';
1818
import interceptAnonymousUser from '@libs/interceptAnonymousUser';
1919
import type {AddUnreportedExpensesParamList} from '@libs/Navigation/types';
20+
import {canSubmitPerDiemExpenseFromWorkspace, getPerDiemCustomUnit} from '@libs/PolicyUtils';
2021
import {isIOUReport} from '@libs/ReportUtils';
2122
import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils';
22-
import {createUnreportedExpenseSections} from '@libs/TransactionUtils';
23+
import {createUnreportedExpenseSections, isPerDiemRequest} from '@libs/TransactionUtils';
2324
import Navigation from '@navigation/Navigation';
2425
import type {PlatformStackScreenProps} from '@navigation/PlatformStackNavigation/types';
2526
import {convertBulkTrackedExpensesToIOU, startMoneyRequest} from '@userActions/IOU';
@@ -53,7 +54,22 @@ function AddUnreportedExpense({route}: AddUnreportedExpensePageType) {
5354
if (!transactions) {
5455
return [];
5556
}
56-
return Object.values(transactions || {}).filter((item) => item?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID || item?.reportID === '');
57+
return Object.values(transactions || {}).filter((item) => {
58+
const isUnreported = item?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID || item?.reportID === '';
59+
if (!isUnreported) {
60+
return false;
61+
}
62+
63+
if (isPerDiemRequest(item)) {
64+
// Only show per diem expenses if the target workspace has per diem enabled and the per diem expense was created in the same workspace
65+
const workspacePerDiemUnit = getPerDiemCustomUnit(policy);
66+
const perDiemCustomUnitID = item?.comment?.customUnit?.customUnitID;
67+
68+
return canSubmitPerDiemExpenseFromWorkspace(policy) && (!perDiemCustomUnitID || perDiemCustomUnitID === workspacePerDiemUnit?.customUnitID);
69+
}
70+
71+
return true;
72+
});
5773
}
5874

5975
const [transactions = getEmptyArray<Transaction>()] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {

src/pages/Search/EmptySearchView.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {startTestDrive} from '@libs/actions/Tour';
3232
import interceptAnonymousUser from '@libs/interceptAnonymousUser';
3333
import Navigation from '@libs/Navigation/Navigation';
3434
import {hasSeenTourSelector, tryNewDotOnyxSelector} from '@libs/onboardingSelectors';
35-
import {areAllGroupPoliciesExpenseChatDisabled, getGroupPaidPoliciesWithExpenseChatEnabled, isPaidGroupPolicy} from '@libs/PolicyUtils';
35+
import {areAllGroupPoliciesExpenseChatDisabled, getGroupPaidPoliciesWithExpenseChatEnabled, isPaidGroupPolicy, isPolicyMember} from '@libs/PolicyUtils';
3636
import {generateReportID} from '@libs/ReportUtils';
3737
import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils';
3838
import {showContextMenu} from '@pages/home/report/ContextMenu/ReportActionContextMenu';
@@ -91,6 +91,10 @@ function EmptySearchView({hash, type, groupBy, hasResults}: EmptySearchViewProps
9191
canBeMissing: true,
9292
});
9393
const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRY_NEW_DOT, {selector: tryNewDotOnyxSelector, canBeMissing: true});
94+
const [isUserPaidPolicyMember = false] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {
95+
canBeMissing: true,
96+
selector: (policies) => Object.values(policies ?? {}).some((policy) => isPaidGroupPolicy(policy) && isPolicyMember(currentUserPersonalDetails.login, policy?.id)),
97+
});
9498

9599
const groupPoliciesWithChatEnabled = getGroupPaidPoliciesWithExpenseChatEnabled();
96100

@@ -196,7 +200,7 @@ function EmptySearchView({hash, type, groupBy, hasResults}: EmptySearchViewProps
196200
}
197201

198202
const startTestDriveAction = () => {
199-
startTestDrive(introSelected, false, tryNewDot?.hasBeenAddedToNudgeMigration);
203+
startTestDrive(introSelected, false, tryNewDot?.hasBeenAddedToNudgeMigration ?? false, isUserPaidPolicyMember);
200204
};
201205

202206
// If we are grouping by reports, show a custom message rather than a type-specific message
@@ -370,6 +374,7 @@ function EmptySearchView({hash, type, groupBy, hasResults}: EmptySearchViewProps
370374
shouldRedirectToExpensifyClassic,
371375
transactions,
372376
tryNewDot?.hasBeenAddedToNudgeMigration,
377+
isUserPaidPolicyMember,
373378
]);
374379

375380
return (

src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import {
4040
canSendInvoice as canSendInvoicePolicyUtils,
4141
getGroupPaidPoliciesWithExpenseChatEnabled,
4242
isPaidGroupPolicy,
43+
isPolicyMember,
4344
shouldShowPolicy,
4445
} from '@libs/PolicyUtils';
4546
import {getQuickActionIcon, getQuickActionTitle, isQuickActionAllowed} from '@libs/QuickActionUtils';
@@ -136,6 +137,10 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT
136137
canBeMissing: true,
137138
});
138139
const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRY_NEW_DOT, {selector: tryNewDotOnyxSelector, canBeMissing: true});
140+
const [isUserPaidPolicyMember = false] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {
141+
canBeMissing: true,
142+
selector: (policies) => Object.values(policies ?? {}).some((policy) => isPaidGroupPolicy(policy) && isPolicyMember(currentUserPersonalDetails.login, policy?.id)),
143+
});
139144

140145
const groupPoliciesWithChatEnabled = getGroupPaidPoliciesWithExpenseChatEnabled();
141146

@@ -522,7 +527,8 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT
522527
iconStyles: styles.popoverIconCircle,
523528
iconFill: theme.icon,
524529
text: translate('testDrive.quickAction.takeATwoMinuteTestDrive'),
525-
onSelected: () => interceptAnonymousUser(() => startTestDrive(introSelected, isAnonymousUser(), tryNewDot?.hasBeenAddedToNudgeMigration)),
530+
onSelected: () =>
531+
interceptAnonymousUser(() => startTestDrive(introSelected, isAnonymousUser(), tryNewDot?.hasBeenAddedToNudgeMigration ?? false, isUserPaidPolicyMember)),
526532
},
527533
]
528534
: []),

src/pages/workspace/WorkspaceMoreFeaturesPage.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {getLatestErrorField} from '@libs/ErrorUtils';
2020
import Navigation from '@libs/Navigation/Navigation';
2121
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
2222
import type {WorkspaceSplitNavigatorParamList} from '@libs/Navigation/types';
23-
import {getPerDiemCustomUnit, isControlPolicy} from '@libs/PolicyUtils';
23+
import {getDistanceRateCustomUnit, getPerDiemCustomUnit, isControlPolicy} from '@libs/PolicyUtils';
2424
import {enablePolicyCategories} from '@userActions/Policy/Category';
2525
import {enablePolicyDistanceRates} from '@userActions/Policy/DistanceRate';
2626
import {enablePerDiem} from '@userActions/Policy/PerDiem';
@@ -99,6 +99,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
9999
const [isDisableWorkflowWarningModalOpen, setIsDisableWorkflowWarningModalOpen] = useState(false);
100100

101101
const perDiemCustomUnit = getPerDiemCustomUnit(policy);
102+
const distanceRateCustomUnit = getDistanceRateCustomUnit(policy);
102103

103104
const [cardList] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}`, {canBeMissing: true});
104105
const workspaceCards = getAllCardsForWorkspace(workspaceAccountID, cardList, cardFeeds);
@@ -132,7 +133,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
132133
if (!policyID) {
133134
return;
134135
}
135-
enablePolicyDistanceRates(policyID, isEnabled);
136+
enablePolicyDistanceRates(policyID, isEnabled, distanceRateCustomUnit);
136137
},
137138
},
138139
{

0 commit comments

Comments
 (0)