Skip to content

Commit 02b2574

Browse files
authored
Merge pull request Expensify#86818 from Expensify/claude-fixIsAttendeeTrackingEnabledDefault
[Payment due @suneox] Fix isAttendeeTrackingEnabled default to true for Classic backwards compat
2 parents da3ad9b + 986deed commit 02b2574

12 files changed

Lines changed: 63 additions & 31 deletions

File tree

src/components/MoneyRequestConfirmationList.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import Log from '@libs/Log';
2626
import {validateAmount} from '@libs/MoneyRequestUtils';
2727
import Navigation from '@libs/Navigation/Navigation';
2828
import {getIOUConfirmationOptionsFromPayeePersonalDetail, hasEnabledOptions} from '@libs/OptionsListUtils';
29-
import {getTagLists, isTaxTrackingEnabled} from '@libs/PolicyUtils';
29+
import {getTagLists, isAttendeeTrackingEnabled, isTaxTrackingEnabled} from '@libs/PolicyUtils';
3030
import {isSelectedManagerMcTest} from '@libs/ReportUtils';
3131
import type {OptionData} from '@libs/ReportUtils';
3232
import {hasEnabledTags, hasMatchingTag} from '@libs/TagsOptionsListUtils';
@@ -444,7 +444,7 @@ function MoneyRequestConfirmationList({
444444
policyTaxRates: policy?.taxRates?.taxes,
445445
iouAttendees,
446446
currentUserPersonalDetails,
447-
isAttendeeTrackingEnabled: policy?.isAttendeeTrackingEnabled,
447+
isAttendeeTrackingEnabled: isAttendeeTrackingEnabled(policy),
448448
isControlPolicy: policy?.type === CONST.POLICY.TYPE.CORPORATE,
449449
});
450450

@@ -816,7 +816,7 @@ function MoneyRequestConfirmationList({
816816
iouCategory,
817817
iouAttendees,
818818
currentUserPersonalDetails,
819-
policy?.isAttendeeTrackingEnabled,
819+
isAttendeeTrackingEnabled(policy),
820820
policy?.type === CONST.POLICY.TYPE.CORPORATE,
821821
);
822822
if (isMissingAttendeesViolation) {

src/components/ReportActionItem/MoneyRequestView.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import {
5353
getPolicyByCustomUnitID,
5454
getTagLists,
5555
hasDependentTags as hasDependentTagsPolicyUtils,
56+
isAttendeeTrackingEnabled,
5657
isPolicyAccessible,
5758
isTaxTrackingEnabled,
5859
} from '@libs/PolicyUtils';
@@ -572,7 +573,7 @@ function MoneyRequestView({
572573
updatedTransaction?.category ?? categoryForDisplay,
573574
actualAttendees,
574575
currentUserPersonalDetails,
575-
policy?.isAttendeeTrackingEnabled,
576+
isAttendeeTrackingEnabled(policy),
576577
policy?.type === CONST.POLICY.TYPE.CORPORATE,
577578
);
578579

src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
2222
import {handleActionButtonPress} from '@libs/actions/Search';
2323
import {syncMissingAttendeesViolation} from '@libs/AttendeeUtils';
2424
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
25+
import {isAttendeeTrackingEnabled} from '@libs/PolicyUtils';
2526
import {isInvoiceReport, isOpenExpenseReport, isProcessingReport, isReportPendingDelete} from '@libs/ReportUtils';
2627
import {isViolationDismissed, shouldShowViolation} from '@libs/TransactionUtils';
2728
import variables from '@styles/variables';
@@ -96,7 +97,7 @@ function ExpenseReportListItem<TItem extends ListItem>({
9697
// Sync missingAttendees violation at render time for each transaction in the report
9798
// This ensures violations show immediately when category settings change, without needing to click the row
9899
const hasSyncedMissingAttendeesViolation = useMemo(() => {
99-
if (!policyForViolations?.isAttendeeTrackingEnabled || policyForViolations?.type !== CONST.POLICY.TYPE.CORPORATE) {
100+
if (!isAttendeeTrackingEnabled(policyForViolations)) {
100101
return false;
101102
}
102103

@@ -114,7 +115,7 @@ function ExpenseReportListItem<TItem extends ListItem>({
114115
transaction.category ?? '',
115116
transaction.attendees,
116117
currentUserDetails,
117-
policyForViolations.isAttendeeTrackingEnabled ?? false,
118+
isAttendeeTrackingEnabled(policyForViolations),
118119
policyForViolations.type === CONST.POLICY.TYPE.CORPORATE,
119120
isInvoice,
120121
);

src/components/Search/SearchList/ListItem/TransactionListItem.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import type {TransactionPreviewData} from '@libs/actions/Search';
2727
import {handleActionButtonPress as handleActionButtonPressUtil} from '@libs/actions/Search';
2828
import {syncMissingAttendeesViolation} from '@libs/AttendeeUtils';
2929
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
30+
import {isAttendeeTrackingEnabled} from '@libs/PolicyUtils';
3031
import {isInvoiceReport} from '@libs/ReportUtils';
3132
import {isViolationDismissed, mergeProhibitedViolations, shouldShowViolation} from '@libs/TransactionUtils';
3233
import variables from '@styles/variables';
@@ -145,7 +146,7 @@ function TransactionListItem<TItem extends ListItem>({
145146
transaction?.category ?? transactionItem.category ?? '',
146147
transaction?.comment?.attendees ?? transactionItem.attendees,
147148
currentUserDetails,
148-
policyForViolations?.isAttendeeTrackingEnabled ?? false,
149+
isAttendeeTrackingEnabled(policyForViolations),
149150
policyForViolations?.type === CONST.POLICY.TYPE.CORPORATE,
150151
isInvoice,
151152
);

src/hooks/useAdvancedSearchFilters.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ function useAdvancedSearchFilters() {
376376
if (key === CONST.SEARCH.SYNTAX_ROOT_KEYS.VIEW && !shouldDisplayViewFilter) {
377377
return;
378378
}
379-
if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.ATTENDEE && !(policyDerived?.isAttendeeTrackingEnabled ?? false)) {
379+
if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.ATTENDEE && !(policyDerived?.isAttendeeTrackingEnabled ?? true)) {
380380
return;
381381
}
382382
if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.REPORT_FIELD && !(policyDerived?.hasReportFields ?? false)) {

src/libs/PolicyUtils.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,14 @@ function isControlPolicy(policy: OnyxEntry<Policy>): boolean {
813813
return policy?.type === CONST.POLICY.TYPE.CORPORATE;
814814
}
815815

816+
/**
817+
* For backwards compatibility with Expensify Classic, attendee tracking defaults to enabled
818+
* on Control policies when the property is undefined.
819+
*/
820+
function isAttendeeTrackingEnabled(policy: OnyxEntry<Policy>): boolean {
821+
return (isControlPolicy(policy) && policy?.isAttendeeTrackingEnabled) ?? true;
822+
}
823+
816824
/**
817825
* Whether the policy can access a feature based on plan level.
818826
* Corporate-only features are restricted to control (Corporate) policies.
@@ -1014,6 +1022,9 @@ function isPolicyFeatureEnabled(policy: OnyxEntry<Policy>, featureName: PolicyFe
10141022
if (featureName === CONST.POLICY.MORE_FEATURES.IS_TIME_TRACKING_ENABLED) {
10151023
return isTimeTrackingEnabled(policy);
10161024
}
1025+
if (featureName === CONST.POLICY.MORE_FEATURES.IS_ATTENDEE_TRACKING_ENABLED) {
1026+
return isAttendeeTrackingEnabled(policy);
1027+
}
10171028

10181029
return !!policy?.[featureName];
10191030
}
@@ -2161,6 +2172,7 @@ export {
21612172
getApprovalWorkflow,
21622173
getReimburserAccountID,
21632174
isControlPolicy,
2175+
isAttendeeTrackingEnabled,
21642176
isCollectPolicy,
21652177
isNetSuiteCustomSegmentRecord,
21662178
getNameFromNetSuiteCustomField,

src/libs/TransactionUtils/index.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
getDistanceRateCustomUnit,
2626
getDistanceRateCustomUnitRate,
2727
getTaxByID,
28+
isAttendeeTrackingEnabled as isAttendeeTrackingEnabledForPolicy,
2829
isInstantSubmitEnabled,
2930
isMultiLevelTags as isMultiLevelTagsPolicyUtils,
3031
isPolicyAdmin,
@@ -596,9 +597,7 @@ function shouldShowAttendees(iouType: IOUType, policy: OnyxEntry<Policy>): boole
596597
return false;
597598
}
598599

599-
// For backwards compatibility with Expensify Classic, we assume that Attendee Tracking is enabled by default on
600-
// Control policies if the policy does not contain the attribute
601-
return policy?.isAttendeeTrackingEnabled ?? true;
600+
return isAttendeeTrackingEnabledForPolicy(policy);
602601
}
603602

604603
/**
@@ -1670,8 +1669,6 @@ function shouldShowViolation(
16701669
const isSubmitter = isCurrentUserSubmitter(iouReport);
16711670
const isPolicyMember = isPolicyMemberPolicyUtils(policy, currentUserEmail);
16721671
const isReportOpen = isOpenExpenseReport(iouReport);
1673-
const isAttendeeTrackingEnabled = policy?.isAttendeeTrackingEnabled ?? false;
1674-
16751672
if (violationName === CONST.VIOLATIONS.AUTO_REPORTED_REJECTED_EXPENSE) {
16761673
return isSubmitter || isPolicyAdmin(policy);
16771674
}
@@ -1689,7 +1686,7 @@ function shouldShowViolation(
16891686
}
16901687

16911688
if (violationName === CONST.VIOLATIONS.MISSING_ATTENDEES) {
1692-
return isAttendeeTrackingEnabled;
1689+
return isAttendeeTrackingEnabledForPolicy(policy);
16931690
}
16941691

16951692
if (violationName === CONST.VIOLATIONS.MISSING_CATEGORY && isCategoryBeingAnalyzed(transaction)) {

src/libs/Violations/ViolationsUtils.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@ import DistanceRequestUtils from '@libs/DistanceRequestUtils';
1313
import {isReceiptError} from '@libs/ErrorUtils';
1414
import {getCurrentUserEmail} from '@libs/Network/NetworkStore';
1515
import Parser from '@libs/Parser';
16-
import {getDistanceRateCustomUnitRate, getPerDiemRateCustomUnitRate, getSortedTagKeys, isDefaultTagName, isTaxTrackingEnabled} from '@libs/PolicyUtils';
16+
import {
17+
getDistanceRateCustomUnitRate,
18+
getPerDiemRateCustomUnitRate,
19+
getSortedTagKeys,
20+
isAttendeeTrackingEnabled as isAttendeeTrackingEnabledForPolicy,
21+
isDefaultTagName,
22+
isTaxTrackingEnabled,
23+
} from '@libs/PolicyUtils';
1724
import {isCurrentUserSubmitter} from '@libs/ReportUtils';
1825
import * as TransactionUtils from '@libs/TransactionUtils';
1926
import {hasValidModifiedAmount, isViolationDismissed, shouldShowViolation} from '@libs/TransactionUtils';
@@ -511,7 +518,7 @@ const ViolationsUtils = {
511518
!isInvoiceTransaction && policyCategories?.[categoryName ?? '']?.areCommentsRequired && !updatedTransaction.comment?.comment && isControlPolicy && policy?.areRulesEnabled;
512519
const rawAttendees = updatedTransaction.modifiedAttendees ?? updatedTransaction.comment?.attendees;
513520
const attendees = Array.isArray(rawAttendees) ? rawAttendees : [];
514-
const isAttendeeTrackingEnabled = policy.isAttendeeTrackingEnabled ?? false;
521+
const isAttendeeTrackingEnabled = isAttendeeTrackingEnabledForPolicy(policy);
515522
// Filter out the owner/creator when checking attendance count - expense is valid if at least one non-owner attendee is present
516523
const ownerAccountID = iouReport?.ownerAccountID;
517524
// Calculate attendees minus owner. When ownerAccountID is known, filter by accountID.

src/pages/workspace/categories/CategoryRequiredFieldsPage.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
1313
import {getDecodedCategoryName} from '@libs/CategoryUtils';
1414
import Navigation from '@libs/Navigation/Navigation';
1515
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
16+
import {isAttendeeTrackingEnabled} from '@libs/PolicyUtils';
1617
import type {SettingsNavigatorParamList} from '@navigation/types';
1718
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
1819
import {setPolicyCategoryAttendeesRequired, setPolicyCategoryDescriptionRequired} from '@userActions/Policy/Category';
@@ -37,8 +38,6 @@ function CategoryRequiredFieldsPage({
3738
const policyCategory = policyCategories?.[categoryName];
3839
const areCommentsRequired = policyCategory?.areCommentsRequired ?? false;
3940
const areAttendeesRequired = policyCategory?.areAttendeesRequired ?? false;
40-
const isAttendeeTrackingEnabled = policy?.isAttendeeTrackingEnabled ?? false;
41-
4241
return (
4342
<AccessOrNotFoundWrapper
4443
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.CONTROL]}
@@ -82,7 +81,7 @@ function CategoryRequiredFieldsPage({
8281
</View>
8382
</View>
8483
</OfflineWithFeedback>
85-
{isAttendeeTrackingEnabled && (
84+
{isAttendeeTrackingEnabled(policy) && (
8685
<OfflineWithFeedback pendingAction={policyCategory?.pendingFields?.areAttendeesRequired}>
8786
<View style={[styles.mh5]}>
8887
<View style={[styles.flexRow, styles.mv5, styles.mr2, styles.alignItemsCenter, styles.justifyContentBetween]}>

src/pages/workspace/categories/CategorySettingsPage.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import Navigation from '@libs/Navigation/Navigation';
3434
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
3535
import {isDisablingOrDeletingLastEnabledCategory} from '@libs/OptionsListUtils';
3636
import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils';
37-
import {getWorkflowApprovalsUnavailable, hasTags, isControlPolicy} from '@libs/PolicyUtils';
37+
import {getWorkflowApprovalsUnavailable, hasTags, isAttendeeTrackingEnabled, isControlPolicy} from '@libs/PolicyUtils';
3838
import type {SettingsNavigatorParamList} from '@navigation/types';
3939
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
4040
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
@@ -155,17 +155,17 @@ function CategorySettingsPage({
155155
if (!policyCategory) {
156156
return '';
157157
}
158-
return formatRequiredFieldsTitle(translate, policyCategory, policy?.isAttendeeTrackingEnabled);
159-
}, [policyCategory, translate, policy?.isAttendeeTrackingEnabled]);
158+
return formatRequiredFieldsTitle(translate, policyCategory, isAttendeeTrackingEnabled(policy));
159+
}, [policyCategory, translate, policy]);
160160

161161
const requireFieldsPendingAction = useMemo(() => {
162-
if (policy?.isAttendeeTrackingEnabled) {
162+
if (isAttendeeTrackingEnabled(policy)) {
163163
// Pending fields are objects so we can't use nullish coalescing
164164
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
165165
return policyCategory?.pendingFields?.areAttendeesRequired || policyCategory?.pendingFields?.areCommentsRequired;
166166
}
167167
return policyCategory?.pendingFields?.areCommentsRequired;
168-
}, [policyCategory?.pendingFields, policy?.isAttendeeTrackingEnabled]);
168+
}, [policyCategory?.pendingFields, policy]);
169169

170170
// eslint-disable-next-line rulesdir/no-negated-variables
171171
const showCannotDeleteOrDisableLastCategoryModal = useCallback(() => {

0 commit comments

Comments
 (0)