diff --git a/src/libs/CopyPolicySettingsUtils.ts b/src/libs/CopyPolicySettingsUtils.ts index 6bac05fb84c7..9e12d09e351f 100644 --- a/src/libs/CopyPolicySettingsUtils.ts +++ b/src/libs/CopyPolicySettingsUtils.ts @@ -9,7 +9,7 @@ import type {Part} from './actions/Policy/CopyPolicySettings'; import {isAuthenticationError} from './actions/connections'; import {PART_TO_POLICY_FEATURE} from './actions/Policy/CopyPolicySettings'; -import {canPolicyAccessFeature, isCollectPolicy, isTimeTrackingEnabled, isWorkspaceProvisionedForTravel} from './PolicyUtils'; +import {canPolicyAccessFeature, isCollectPolicy, isInvoiceFieldsEnabled, isTimeTrackingEnabled, isWorkspaceProvisionedForTravel} from './PolicyUtils'; type FeatureRow = { part: Part; @@ -316,7 +316,8 @@ function getControlOnlySelectedParts(targetPolicies: ReadonlyArray field.target === CONST.REPORT_FIELD_TARGETS.INVOICE); + const hasInvoiceFields = + isInvoiceFieldsEnabled(sourcePolicy ?? undefined) || Object.values(sourcePolicy?.fieldList ?? {}).some((field) => field.target === CONST.REPORT_FIELD_TARGETS.INVOICE); return selectedParts.filter((part) => { const featureName = part === 'invoices' && hasInvoiceFields ? CONST.POLICY.MORE_FEATURES.ARE_INVOICE_FIELDS_ENABLED : PART_TO_POLICY_FEATURE[part]; if (!featureName) { diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 39814246c0b1..233d3d19f85e 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -1793,10 +1793,21 @@ function arePolicyRulesEnabled(policy: OnyxEntry, policyCategories?: Pol return hasAnyCategoryRules(policyCategories ?? undefined); } +/** + * Whether Invoice Fields is enabled for the policy. + * Respects the `areInvoiceFieldsEnabled` toggle and verifies the policy has access to the feature (Control only). + */ +function isInvoiceFieldsEnabled(policy: OnyxEntry | null): boolean { + return !!policy?.areInvoiceFieldsEnabled && canPolicyAccessFeature(policy ?? undefined, CONST.POLICY.MORE_FEATURES.ARE_INVOICE_FIELDS_ENABLED); +} + function isPolicyFeatureEnabled(policy: OnyxEntry, featureName: PolicyFeatureName, policyCategories?: PolicyCategories | null): boolean { if (featureName === CONST.POLICY.MORE_FEATURES.ARE_RULES_ENABLED) { return arePolicyRulesEnabled(policy, policyCategories); } + if (featureName === CONST.POLICY.MORE_FEATURES.ARE_INVOICE_FIELDS_ENABLED) { + return isInvoiceFieldsEnabled(policy); + } if (featureName === CONST.POLICY.MORE_FEATURES.ARE_TAXES_ENABLED) { return !!policy?.tax?.trackingEnabled; } @@ -3702,6 +3713,7 @@ export { getActivePoliciesWithExpenseChatAndPerDiemEnabled, isPerDiemEnabled, isPerDiemEligiblePolicy, + isInvoiceFieldsEnabled, getTravelStep, isWorkspaceProvisionedForTravel, hasAcceptedTravelTerms, diff --git a/src/libs/WorkspaceReportFieldUtils.ts b/src/libs/WorkspaceReportFieldUtils.ts index 2ff9ffd1d74f..d8756396fb3e 100644 --- a/src/libs/WorkspaceReportFieldUtils.ts +++ b/src/libs/WorkspaceReportFieldUtils.ts @@ -112,10 +112,14 @@ function hasFormulaPartsInInitialValue(initialValue?: string): boolean { } /** - * Checks if a report field name already exists in the policy's field list (case-insensitive). + * Finds an existing report field with the specified name in the policy's field list (case-insensitive). */ -function isReportFieldNameExisting(fieldList: Record | undefined, fieldName: string, expectedTarget?: ValueOf): boolean { - return Object.values(fieldList ?? {}).some((reportField) => { +function getExistingReportFieldByName( + fieldList: Record | undefined, + fieldName: string, + expectedTarget?: ValueOf, +): PolicyReportField | undefined { + return Object.values(fieldList ?? {}).find((reportField) => { if (!isReportFieldTargetValid(reportField, expectedTarget)) { return false; } @@ -124,6 +128,13 @@ function isReportFieldNameExisting(fieldList: Record }); } +/** + * Checks if a report field name already exists in the policy's field list (case-insensitive). + */ +function isReportFieldNameExisting(fieldList: Record | undefined, fieldName: string, expectedTarget?: ValueOf): boolean { + return !!getExistingReportFieldByName(fieldList, fieldName, expectedTarget); +} + /** * Determines whether a report field matches the expected target. */ @@ -276,6 +287,7 @@ export { getUnsupportedReportFieldFormulaParts, hasFormulaPartsInInitialValue, isReportFieldNameExisting, + getExistingReportFieldByName, isReportFieldTargetValid, getReportFieldsForTarget, isReportFieldImportedFromIntegration, diff --git a/src/pages/workspace/copyPolicySettings/CopyPolicySettingsSelectFeaturesPage.tsx b/src/pages/workspace/copyPolicySettings/CopyPolicySettingsSelectFeaturesPage.tsx index 2e66b78373e6..b089f53a2448 100644 --- a/src/pages/workspace/copyPolicySettings/CopyPolicySettingsSelectFeaturesPage.tsx +++ b/src/pages/workspace/copyPolicySettings/CopyPolicySettingsSelectFeaturesPage.tsx @@ -32,7 +32,14 @@ import { import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {PolicyCopySettingsNavigatorParamList} from '@libs/Navigation/types'; -import {createFilteredMemberCountSelector, createInvoiceConfigurationTextSelector, getDistanceRateCustomUnit, getPerDiemCustomUnit, isCollectPolicy} from '@libs/PolicyUtils'; +import { + createFilteredMemberCountSelector, + createInvoiceConfigurationTextSelector, + getDistanceRateCustomUnit, + getPerDiemCustomUnit, + isCollectPolicy, + isInvoiceFieldsEnabled, +} from '@libs/PolicyUtils'; import {formatAddressToString} from '@libs/ReportActionsUtils'; import {getReportFieldsByPolicyID} from '@libs/ReportUtils'; @@ -135,7 +142,7 @@ function CopyPolicySettingsSelectFeaturesPage() { hasWorkflowRules: !!workflows?.length, hasWorkspaceRules: !!rules?.length, codingRulesCount, - hasInvoiceConfiguration: !!sourcePolicy?.areInvoicesEnabled && (!!invoiceConfigurationText || invoiceFieldsCount > 0), + hasInvoiceConfiguration: !!sourcePolicy?.areInvoicesEnabled && (!!invoiceConfigurationText || invoiceFieldsCount > 0 || isInvoiceFieldsEnabled(sourcePolicy)), isCollectPolicy: isCollectPolicy(sourcePolicy), }; diff --git a/src/pages/workspace/fields/CreateFieldsPage.tsx b/src/pages/workspace/fields/CreateFieldsPage.tsx index c7549bd2e101..171d0ad356e8 100644 --- a/src/pages/workspace/fields/CreateFieldsPage.tsx +++ b/src/pages/workspace/fields/CreateFieldsPage.tsx @@ -16,7 +16,7 @@ import {addErrorMessage} from '@libs/ErrorUtils'; import {hasCircularReferences} from '@libs/Formula'; import Navigation from '@libs/Navigation/Navigation'; import {isRequiredFulfilled} from '@libs/ValidationUtils'; -import {getReportFieldsForTarget, getUnsupportedReportFieldFormulaParts, hasFormulaPartsInInitialValue, isReportFieldNameExisting} from '@libs/WorkspaceReportFieldUtils'; +import {getExistingReportFieldByName, getReportFieldsForTarget, getUnsupportedReportFieldFormulaParts, hasFormulaPartsInInitialValue} from '@libs/WorkspaceReportFieldUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import InitialListValueSelector from '@pages/workspace/reports/InitialListValueSelector'; @@ -88,63 +88,71 @@ function CreateFieldsPage({policy, policyID, isInvoiceField, listValuesRoute, ge [availableListValuesLength, formDraft, isInvoiceField, policy, policyReportIDs], ); - const validateForm = useCallback( - (values: FormOnyxValues): FormInputErrors => { - const {name, type, initialValue: formInitialValue} = values; - const errors: FormInputErrors = {}; - - if (!isRequiredFulfilled(name)) { - errors[INPUT_IDS.NAME] = translate(isInvoiceField ? 'workspace.invoiceFields.invoiceFieldNameRequiredError' : 'workspace.reportFields.reportFieldNameRequiredError'); - } else if (isReportFieldNameExisting(policy?.fieldList, name)) { - errors[INPUT_IDS.NAME] = translate(isInvoiceField ? 'workspace.invoiceFields.existingInvoiceFieldNameError' : 'workspace.reportFields.existingReportFieldNameError'); - } else if ([...name].length > CONST.WORKSPACE_REPORT_FIELD_POLICY_MAX_LENGTH) { - addErrorMessage(errors, INPUT_IDS.NAME, translate('common.error.characterLimitExceedCounter', [...name].length, CONST.WORKSPACE_REPORT_FIELD_POLICY_MAX_LENGTH)); - } + const getExistingFieldNameError = (name: string) => { + const existingField = getExistingReportFieldByName(policy?.fieldList, name); + if (!existingField) { + return undefined; + } - if (!isRequiredFulfilled(type)) { - errors[INPUT_IDS.TYPE] = translate(isInvoiceField ? 'workspace.invoiceFields.invoiceFieldTypeRequiredError' : 'workspace.reportFields.reportFieldTypeRequiredError'); - } + return translate( + existingField.target === CONST.REPORT_FIELD_TARGETS.INVOICE ? 'workspace.invoiceFields.existingInvoiceFieldNameError' : 'workspace.reportFields.existingReportFieldNameError', + ); + }; - if (type === CONST.REPORT_FIELD_TYPES.TEXT && !!formInitialValue && formInitialValue.length > CONST.WORKSPACE_REPORT_FIELD_POLICY_MAX_LENGTH) { - errors[INPUT_IDS.INITIAL_VALUE] = translate('common.error.characterLimitExceedCounter', formInitialValue.length, CONST.WORKSPACE_REPORT_FIELD_POLICY_MAX_LENGTH); - } + const validateForm = (values: FormOnyxValues): FormInputErrors => { + const {name, type, initialValue: formInitialValue} = values; + const errors: FormInputErrors = {}; - if ( - (type === CONST.REPORT_FIELD_TYPES.TEXT || type === CONST.REPORT_FIELD_TYPES.FORMULA) && - hasCircularReferences(formInitialValue, name, getReportFieldsForTarget(policy?.fieldList, fieldTarget)) - ) { - errors[INPUT_IDS.INITIAL_VALUE] = translate('workspace.reportFields.circularReferenceError'); - } + const existingFieldNameError = getExistingFieldNameError(name); - if ((type === CONST.REPORT_FIELD_TYPES.TEXT || type === CONST.REPORT_FIELD_TYPES.FORMULA) && !!formInitialValue && !errors[INPUT_IDS.INITIAL_VALUE]) { - const unsupportedFormulaParts = getUnsupportedReportFieldFormulaParts(formInitialValue); - if (unsupportedFormulaParts.length > 0) { - errors[INPUT_IDS.INITIAL_VALUE] = translate('workspace.reportFields.unsupportedFormulaValueError', unsupportedFormulaParts.join(', ')); - } - } + if (!isRequiredFulfilled(name)) { + errors[INPUT_IDS.NAME] = translate(isInvoiceField ? 'workspace.invoiceFields.invoiceFieldNameRequiredError' : 'workspace.reportFields.reportFieldNameRequiredError'); + } else if (existingFieldNameError) { + errors[INPUT_IDS.NAME] = existingFieldNameError; + } else if ([...name].length > CONST.WORKSPACE_REPORT_FIELD_POLICY_MAX_LENGTH) { + addErrorMessage(errors, INPUT_IDS.NAME, translate('common.error.characterLimitExceedCounter', [...name].length, CONST.WORKSPACE_REPORT_FIELD_POLICY_MAX_LENGTH)); + } - if (type === CONST.REPORT_FIELD_TYPES.LIST && availableListValuesLength > 0 && !isRequiredFulfilled(formInitialValue)) { - errors[INPUT_IDS.INITIAL_VALUE] = translate( - isInvoiceField ? 'workspace.invoiceFields.invoiceFieldInitialValueRequiredError' : 'workspace.reportFields.reportFieldInitialValueRequiredError', - ); - } + if (!isRequiredFulfilled(type)) { + errors[INPUT_IDS.TYPE] = translate(isInvoiceField ? 'workspace.invoiceFields.invoiceFieldTypeRequiredError' : 'workspace.reportFields.reportFieldTypeRequiredError'); + } - return errors; - }, - [availableListValuesLength, fieldTarget, isInvoiceField, policy?.fieldList, translate], - ); + if (type === CONST.REPORT_FIELD_TYPES.TEXT && !!formInitialValue && formInitialValue.length > CONST.WORKSPACE_REPORT_FIELD_POLICY_MAX_LENGTH) { + errors[INPUT_IDS.INITIAL_VALUE] = translate('common.error.characterLimitExceedCounter', formInitialValue.length, CONST.WORKSPACE_REPORT_FIELD_POLICY_MAX_LENGTH); + } - const validateName = useCallback( - (values: Record) => { - const errors: Record = {}; - const name = values[INPUT_IDS.NAME]; - if (isReportFieldNameExisting(policy?.fieldList, name)) { - errors[INPUT_IDS.NAME] = translate(isInvoiceField ? 'workspace.invoiceFields.existingInvoiceFieldNameError' : 'workspace.reportFields.existingReportFieldNameError'); + if ( + (type === CONST.REPORT_FIELD_TYPES.TEXT || type === CONST.REPORT_FIELD_TYPES.FORMULA) && + hasCircularReferences(formInitialValue, name, getReportFieldsForTarget(policy?.fieldList, fieldTarget)) + ) { + errors[INPUT_IDS.INITIAL_VALUE] = translate('workspace.reportFields.circularReferenceError'); + } + + if ((type === CONST.REPORT_FIELD_TYPES.TEXT || type === CONST.REPORT_FIELD_TYPES.FORMULA) && !!formInitialValue && !errors[INPUT_IDS.INITIAL_VALUE]) { + const unsupportedFormulaParts = getUnsupportedReportFieldFormulaParts(formInitialValue); + if (unsupportedFormulaParts.length > 0) { + errors[INPUT_IDS.INITIAL_VALUE] = translate('workspace.reportFields.unsupportedFormulaValueError', unsupportedFormulaParts.join(', ')); } - return errors; - }, - [isInvoiceField, policy?.fieldList, translate], - ); + } + + if (type === CONST.REPORT_FIELD_TYPES.LIST && availableListValuesLength > 0 && !isRequiredFulfilled(formInitialValue)) { + errors[INPUT_IDS.INITIAL_VALUE] = translate( + isInvoiceField ? 'workspace.invoiceFields.invoiceFieldInitialValueRequiredError' : 'workspace.reportFields.reportFieldInitialValueRequiredError', + ); + } + + return errors; + }; + + const validateName = (values: Record) => { + const errors: Record = {}; + const name = values[INPUT_IDS.NAME]; + const existingFieldNameError = getExistingFieldNameError(name); + if (existingFieldNameError) { + errors[INPUT_IDS.NAME] = existingFieldNameError; + } + return errors; + }; const handleOnValueCommitted = (initialValue: string) => { setDraftValues(ONYXKEYS.FORMS.WORKSPACE_REPORT_FIELDS_FORM, { diff --git a/src/pages/workspace/invoices/WorkspaceInvoiceFieldsSection.tsx b/src/pages/workspace/invoices/WorkspaceInvoiceFieldsSection.tsx index 622ce84759cb..afd80a275ee6 100644 --- a/src/pages/workspace/invoices/WorkspaceInvoiceFieldsSection.tsx +++ b/src/pages/workspace/invoices/WorkspaceInvoiceFieldsSection.tsx @@ -1,6 +1,7 @@ import usePolicy from '@hooks/usePolicy'; import {enablePolicyInvoiceFields} from '@libs/actions/Policy/Policy'; +import {isInvoiceFieldsEnabled} from '@libs/PolicyUtils'; import WorkspaceFieldsSection from '@pages/workspace/fields/WorkspaceFieldsSection'; @@ -22,7 +23,7 @@ function WorkspaceInvoiceFieldsSection({policyID}: WorkspaceInvoiceFieldsSection field.target === CONST.REPORT_FIELD_TARGETS.INVOICE} titleKey="workspace.common.invoiceFields" diff --git a/tests/unit/CopyPolicySettingsUtilsTest.ts b/tests/unit/CopyPolicySettingsUtilsTest.ts index e3747879931e..6b213a474f58 100644 --- a/tests/unit/CopyPolicySettingsUtilsTest.ts +++ b/tests/unit/CopyPolicySettingsUtilsTest.ts @@ -270,6 +270,18 @@ describe('CopyPolicySettingsUtils', () => { distancePolicy.areDistanceRatesEnabled = true; expect(isCopyPolicySettingsPartEnabledOnSource('distanceRates', {...baseContext, policy: distancePolicy})).toBe(true); }); + + it('shows invoices only when the feature is enabled and has invoice configuration', () => { + const invoicePolicy = createRandomPolicy(12); + invoicePolicy.areInvoicesEnabled = true; + + expect(isCopyPolicySettingsPartEnabledOnSource('invoices', {...baseContext, policy: invoicePolicy, hasInvoiceConfiguration: true})).toBe(true); + expect(isCopyPolicySettingsPartEnabledOnSource('invoices', {...baseContext, policy: invoicePolicy, hasInvoiceConfiguration: false})).toBe(false); + + const disabledInvoicePolicy = createRandomPolicy(13); + disabledInvoicePolicy.areInvoicesEnabled = false; + expect(isCopyPolicySettingsPartEnabledOnSource('invoices', {...baseContext, policy: disabledInvoicePolicy, hasInvoiceConfiguration: true})).toBe(false); + }); }); describe('isTargetCompatibleForAccountingPart', () => { @@ -448,6 +460,18 @@ describe('CopyPolicySettingsUtils', () => { expect(getControlOnlySelectedParts([collectTarget(1)], ['rules'] as Part[])).toEqual([]); }); + it('treats invoices as Control-only when source policy has invoice fields enabled', () => { + const sourcePolicy = {...createRandomPolicy(2, CONST.POLICY.TYPE.CORPORATE), areInvoiceFieldsEnabled: true}; + const result = getControlOnlySelectedParts([collectTarget(1)], ['invoices'] as Part[], sourcePolicy); + expect(result).toContain('invoices'); + }); + + it('does not treat invoices as Control-only when source policy has no invoice fields', () => { + const sourcePolicy = {...createRandomPolicy(2, CONST.POLICY.TYPE.CORPORATE), areInvoiceFieldsEnabled: false}; + const result = getControlOnlySelectedParts([collectTarget(1)], ['invoices'] as Part[], sourcePolicy); + expect(result).not.toContain('invoices'); + }); + it('returns nothing when there are no Collect targets', () => { expect(getControlOnlySelectedParts([controlTarget(1)], ['perDiem'] as Part[])).toEqual([]); }); diff --git a/tests/unit/PolicyUtilsTest.ts b/tests/unit/PolicyUtilsTest.ts index d76367dc5e33..d6140e2103df 100644 --- a/tests/unit/PolicyUtilsTest.ts +++ b/tests/unit/PolicyUtilsTest.ts @@ -74,11 +74,13 @@ import { hasVendorFeature, isArchivedPolicy, isDualEntryVendorMatchingActive, + isInvoiceFieldsEnabled, isMatchingVendorListLoaded, isMaxExpenseAmountSet, isMergeHRCompleteSetupNeededSelector, isPerDiemEligiblePolicy, isPerDiemEnabled, + isPolicyFeatureEnabled, isPolicyMemberWithoutPendingDelete, isSubmitterApproveBlockedOnSubmitWorkspace, isRilletVendorMatchingActive, @@ -3602,6 +3604,44 @@ describe('PolicyUtils', () => { }); }); + describe('isInvoiceFieldsEnabled', () => { + it('returns true for a control policy with areInvoiceFieldsEnabled explicitly true', () => { + const policy = {...createRandomPolicy(1, CONST.POLICY.TYPE.CORPORATE), areInvoiceFieldsEnabled: true}; + expect(isInvoiceFieldsEnabled(policy)).toBe(true); + }); + + it('returns false for a control policy with areInvoiceFieldsEnabled explicitly false', () => { + const policy = {...createRandomPolicy(1, CONST.POLICY.TYPE.CORPORATE), areInvoiceFieldsEnabled: false}; + expect(isInvoiceFieldsEnabled(policy)).toBe(false); + }); + + it('returns false for a collect policy even when areInvoiceFieldsEnabled is true', () => { + const policy = {...createRandomPolicy(1, CONST.POLICY.TYPE.TEAM), areInvoiceFieldsEnabled: true}; + expect(isInvoiceFieldsEnabled(policy)).toBe(false); + }); + + it('returns false for an undefined policy', () => { + expect(isInvoiceFieldsEnabled(undefined)).toBe(false); + }); + }); + + describe('isPolicyFeatureEnabled for ARE_INVOICE_FIELDS_ENABLED', () => { + it('returns true for a control policy with invoice fields enabled', () => { + const policy = {...createRandomPolicy(1, CONST.POLICY.TYPE.CORPORATE), areInvoiceFieldsEnabled: true}; + expect(isPolicyFeatureEnabled(policy, CONST.POLICY.MORE_FEATURES.ARE_INVOICE_FIELDS_ENABLED)).toBe(true); + }); + + it('returns false for a collect policy even with areInvoiceFieldsEnabled true', () => { + const policy = {...createRandomPolicy(1, CONST.POLICY.TYPE.TEAM), areInvoiceFieldsEnabled: true}; + expect(isPolicyFeatureEnabled(policy, CONST.POLICY.MORE_FEATURES.ARE_INVOICE_FIELDS_ENABLED)).toBe(false); + }); + + it('returns false for a control policy with invoice fields disabled', () => { + const policy = {...createRandomPolicy(1, CONST.POLICY.TYPE.CORPORATE), areInvoiceFieldsEnabled: false}; + expect(isPolicyFeatureEnabled(policy, CONST.POLICY.MORE_FEATURES.ARE_INVOICE_FIELDS_ENABLED)).toBe(false); + }); + }); + describe('getPolicyByCustomUnitID', () => { const transactionWithPerDiemUnit: Transaction = { ...createRandomTransaction(0), diff --git a/tests/unit/WorkspaceReportFieldUtilsTest.ts b/tests/unit/WorkspaceReportFieldUtilsTest.ts index 06b0aa499eb0..dea0eb991506 100644 --- a/tests/unit/WorkspaceReportFieldUtilsTest.ts +++ b/tests/unit/WorkspaceReportFieldUtilsTest.ts @@ -1,4 +1,10 @@ -import {getUnsupportedReportFieldFormulaParts, hasFormulaPartsInInitialValue, isReportFieldImportedFromIntegration, isReportFieldNameExisting} from '@libs/WorkspaceReportFieldUtils'; +import { + getExistingReportFieldByName, + getUnsupportedReportFieldFormulaParts, + hasFormulaPartsInInitialValue, + isReportFieldImportedFromIntegration, + isReportFieldNameExisting, +} from '@libs/WorkspaceReportFieldUtils'; import CONST from '@src/CONST'; import type {Policy} from '@src/types/onyx'; @@ -117,6 +123,29 @@ describe('WorkspaceReportFieldUtils.isReportFieldNameExisting', () => { }); }); +describe('WorkspaceReportFieldUtils.getExistingReportFieldByName', () => { + const invoiceField = createMock({name: 'Field1', type: 'text', target: CONST.REPORT_FIELD_TARGETS.INVOICE}); + const expenseField = createMock({name: 'Field2', type: 'text', target: CONST.REPORT_FIELD_TARGETS.EXPENSE}); + const fieldList: Record = { + invoiceField, + expenseField, + }; + + it('should return undefined when field name does not exist', () => { + expect(getExistingReportFieldByName(fieldList, 'Field3')).toBeUndefined(); + }); + + it('should return the matching report field case-insensitively', () => { + expect(getExistingReportFieldByName(fieldList, 'field1')).toEqual(invoiceField); + expect(getExistingReportFieldByName(fieldList, 'FIELD2')).toEqual(expenseField); + }); + + it('should filter by expectedTarget when provided', () => { + expect(getExistingReportFieldByName(fieldList, 'Field1', CONST.REPORT_FIELD_TARGETS.EXPENSE)).toBeUndefined(); + expect(getExistingReportFieldByName(fieldList, 'Field1', CONST.REPORT_FIELD_TARGETS.INVOICE)).toEqual(invoiceField); + }); +}); + describe('WorkspaceReportFieldUtils.isReportFieldImportedFromIntegration', () => { const reportFieldWithOrigin = (origin: string | undefined) => createMock({name: 'Field', type: 'text', origin}); const policyConnectedTo = (...connectionNames: ConnectionName[]) => createMock({connections: Object.fromEntries(connectionNames.map((connectionName) => [connectionName, {}]))});