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
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function BaseFloatingCameraButton({icon}: BaseFloatingCameraButtonProps) {
interceptAnonymousUser(() => {
if (
policyChatForActivePolicy?.policyID &&
shouldRestrictUserBillableActions(policyChatForActivePolicy.policyID, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, amountOwed)
shouldRestrictUserBillableActions(policyChatForActivePolicy.policyID, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, amountOwed, activePolicy)
) {
Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(policyChatForActivePolicy.policyID));
return;
Expand Down
2 changes: 1 addition & 1 deletion src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2188,7 +2188,7 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa
if (!moneyRequestReport?.reportID) {
return;
}
if (policy && shouldRestrictUserBillableActions(policy.id, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, amountOwed)) {
if (policy && shouldRestrictUserBillableActions(policy.id, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, amountOwed, policy)) {
Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(policy.id));
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ function SearchMoneyRequestReportEmptyState({report, policy, onLayout}: {report:
if (!reportId) {
return;
}
if (policy && shouldRestrictUserBillableActions(policy.id, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, amountOwed)) {
if (policy && shouldRestrictUserBillableActions(policy.id, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, amountOwed, policy)) {
Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(policy.id));
return;
}
Expand All @@ -59,7 +59,7 @@ function SearchMoneyRequestReportEmptyState({report, policy, onLayout}: {report:
if (!reportId) {
return;
}
if (policy && shouldRestrictUserBillableActions(policy.id, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, amountOwed)) {
if (policy && shouldRestrictUserBillableActions(policy.id, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, amountOwed, policy)) {
Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(policy.id));
return;
}
Expand All @@ -71,7 +71,7 @@ function SearchMoneyRequestReportEmptyState({report, policy, onLayout}: {report:
text: translate('iou.addUnreportedExpense'),
icon: icons.ReceiptPlus,
onSelected: () => {
if (policy && shouldRestrictUserBillableActions(policy.id, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, amountOwed)) {
if (policy && shouldRestrictUserBillableActions(policy.id, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, amountOwed, policy)) {
Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(policy.id));
return;
}
Expand Down
6 changes: 4 additions & 2 deletions src/components/Navigation/QuickCreationActionsBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,14 @@ function QuickCreationActionsBar() {

if (
!workspaceIDForReportCreation ||
(shouldRestrictUserBillableActions(workspaceIDForReportCreation, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds) && groupPoliciesWithChatEnabled.length > 1)
(shouldRestrictUserBillableActions(workspaceIDForReportCreation, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, undefined, defaultChatEnabledPolicy) &&
groupPoliciesWithChatEnabled.length > 1)
) {
Navigation.navigate(ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute());
return;
}

if (!shouldRestrictUserBillableActions(workspaceIDForReportCreation, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds)) {
if (!shouldRestrictUserBillableActions(workspaceIDForReportCreation, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, undefined, defaultChatEnabledPolicy)) {
if (shouldShowEmptyReportConfirmationForDefaultChatEnabledPolicy) {
openCreateReportConfirmation();
} else {
Expand All @@ -199,6 +200,7 @@ function QuickCreationActionsBar() {
defaultChatEnabledPolicyID,
userBillingGracePeriodEnds,
ownerBillingGracePeriodEnd,
defaultChatEnabledPolicy,
groupPoliciesWithChatEnabled.length,
shouldShowEmptyReportConfirmationForDefaultChatEnabledPolicy,
openCreateReportConfirmation,
Expand Down
2 changes: 1 addition & 1 deletion src/components/SettlementButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ function SettlementButton({
return true;
}

if (policy && shouldRestrictUserBillableActions(policy.id, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, amountOwed)) {
if (policy && shouldRestrictUserBillableActions(policy.id, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, amountOwed, policy)) {
Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(policy.id));
return true;
}
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useReceiptScanDrop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ function useReceiptScanDrop() {
if (
isPaidGroupPolicy(activePolicy) &&
activePolicy?.isPolicyExpenseChatEnabled &&
!shouldRestrictUserBillableActions(activePolicy.id, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, amountOwed)
!shouldRestrictUserBillableActions(activePolicy.id, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, amountOwed, activePolicy)
) {
const shouldAutoReport = !!activePolicy?.autoReporting || !!personalPolicy?.autoReporting;
const report = shouldAutoReport ? getPolicyExpenseChat(currentUserPersonalDetails.accountID, activePolicy?.id) : selfDMReport;
Expand Down
16 changes: 11 additions & 5 deletions src/hooks/useSearchBulkActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ import {canIOUBePaid, dismissRejectUseExplanation, initBulkEditDraftTransaction}
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {BillingGraceEndPeriod, Report, SearchResults, Transaction, TransactionViolations} from '@src/types/onyx';
import type {BillingGraceEndPeriod, Policy, Report, SearchResults, Transaction, TransactionViolations} from '@src/types/onyx';
import type DeepValueOf from '@src/types/utils/DeepValueOf';
import useAllTransactions from './useAllTransactions';
import useBulkPayOptions from './useBulkPayOptions';
Expand Down Expand Up @@ -85,10 +85,15 @@ function getRestrictedPolicyID(
billingGracePeriods: OnyxCollection<BillingGraceEndPeriod>,
ownerBillingGracePeriodEnd: OnyxEntry<number>,
amountOwed: OnyxEntry<number>,
allPolicies: OnyxCollection<Policy>,
): string | undefined {
return items
.map((item) => item.policyID)
.find((policyID): policyID is string => !!policyID && shouldRestrictUserBillableActions(policyID, ownerBillingGracePeriodEnd, billingGracePeriods, amountOwed));
.find(
(policyID): policyID is string =>
!!policyID &&
shouldRestrictUserBillableActions(policyID, ownerBillingGracePeriodEnd, billingGracePeriods, amountOwed, allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]),
);
}

function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) {
Expand Down Expand Up @@ -400,7 +405,7 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) {

const selectedItems = selectedReports.length ? selectedReports : Object.values(selectedTransactions);

const restrictedPolicyID = getRestrictedPolicyID(selectedItems, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, amountOwed);
const restrictedPolicyID = getRestrictedPolicyID(selectedItems, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, amountOwed, policies);
if (restrictedPolicyID) {
Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(restrictedPolicyID));
return;
Expand Down Expand Up @@ -428,6 +433,7 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) {
userBillingGracePeriodEnds,
ownerBillingGracePeriodEnd,
amountOwed,
policies,
]);

const {expenseCount, uniqueReportCount} = useMemo(() => {
Expand Down Expand Up @@ -557,7 +563,7 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) {
const selectedOptions = selectedReports.length ? selectedReports : Object.values(selectedTransactions);
const expenseReportBankAccountID = additionalData?.bankAccountID;

const restrictedPolicyID = getRestrictedPolicyID(selectedOptions, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, amountOwed);
const restrictedPolicyID = getRestrictedPolicyID(selectedOptions, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, amountOwed, policies);
if (restrictedPolicyID) {
Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(restrictedPolicyID));
return;
Expand Down Expand Up @@ -1018,7 +1024,7 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) {

const itemList = !selectedReports.length ? Object.values(selectedTransactions).map((transaction) => transaction) : (selectedReports?.filter((report) => !!report) ?? []);

const restrictedPolicyID = getRestrictedPolicyID(itemList, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, amountOwed);
const restrictedPolicyID = getRestrictedPolicyID(itemList, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, amountOwed, policies);
if (restrictedPolicyID) {
Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(restrictedPolicyID));
return;
Expand Down
3 changes: 1 addition & 2 deletions src/libs/SubscriptionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
};

let currentUserAccountID = -1;
Onyx.connect({

Check warning on line 56 in src/libs/SubscriptionUtils.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.SESSION,
callback: (value) => {
currentUserAccountID = value?.accountID ?? CONST.DEFAULT_NUMBER_ID;
Expand All @@ -61,13 +61,13 @@
});

let privateAmountOwed: OnyxEntry<number>;
Onyx.connect({

Check warning on line 64 in src/libs/SubscriptionUtils.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.NVP_PRIVATE_AMOUNT_OWED,
callback: (value) => (privateAmountOwed = value),
});

let deprecatedAllPolicies: OnyxCollection<Policy>;
Onyx.connect({

Check warning on line 70 in src/libs/SubscriptionUtils.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,
callback: (value) => (deprecatedAllPolicies = value),
waitForCollectionCallback: true,
Expand Down Expand Up @@ -460,11 +460,10 @@
ownerBillingGracePeriodEnd: OnyxEntry<number>,
userBillingGracePeriodEnds: OnyxCollection<BillingGraceEndPeriod>,
amountOwed: OnyxEntry<number> = privateAmountOwed,
policy: OnyxEntry<Policy> = deprecatedAllPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`],
): boolean {
const currentDate = new Date();

const policy = deprecatedAllPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`];

// This logic will be executed if the user is a workspace's non-owner (normal user or admin).
// We should restrict the workspace's non-owner actions if it's member of a workspace where the owner is
// past due and is past its grace period end.
Expand Down
6 changes: 5 additions & 1 deletion src/pages/AddUnreportedExpense.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,11 @@ function AddUnreportedExpense({route}: AddUnreportedExpensePageType) {
{
buttonText: translate('iou.createExpense'),
buttonAction: () => {
if (report && report.policyID && shouldRestrictUserBillableActions(report.policyID, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds)) {
if (
report &&
report.policyID &&
shouldRestrictUserBillableActions(report.policyID, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, undefined, policy)
) {
Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(report.policyID));
return;
}
Expand Down
116 changes: 116 additions & 0 deletions tests/unit/SubscriptionUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,122 @@ describe('SubscriptionUtils', () => {
});
});

describe('shouldRestrictUserBillableActions - policy parameter', () => {
afterEach(async () => {
await Onyx.clear();
await Onyx.multiSet({
[ONYXKEYS.SESSION]: null,
[ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END]: null,
[ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED]: null,
[ONYXKEYS.COLLECTION.POLICY]: null,
});
});

it('should restrict when policy is passed directly and owner is past due', async () => {
const accountID = 1;
const policyID = '2001';
const policy = {
...createRandomPolicy(Number(policyID)),
ownerAccountID: accountID,
};

await Onyx.multiSet({
[ONYXKEYS.SESSION]: {email: '', accountID},
[ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED]: 8010,
});

expect(shouldRestrictUserBillableActions(policyID, getUnixTime(subDays(new Date(), 3)), undefined, undefined, policy)).toBeTruthy();
});

it('should not restrict when policy is passed directly but owner is not past due', async () => {
const accountID = 1;
const policyID = '2001';
const policy = {
...createRandomPolicy(Number(policyID)),
ownerAccountID: accountID,
};

await Onyx.multiSet({
[ONYXKEYS.SESSION]: {email: '', accountID},
[ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED]: 8010,
});

expect(shouldRestrictUserBillableActions(policyID, getUnixTime(addDays(new Date(), 3)), undefined, undefined, policy)).toBeFalsy();
});

it('should not restrict when policy is passed as undefined', () => {
expect(shouldRestrictUserBillableActions('nonexistent', getUnixTime(subDays(new Date(), 3)), undefined, 500, undefined)).toBeFalsy();
});

it('should restrict for non-owner when policy is passed directly and billing grace period is overdue', async () => {
const policyID = '2001';
const ownerAccountID = 2001;
const policy = {
...createRandomPolicy(Number(policyID)),
ownerAccountID,
};

expect(
shouldRestrictUserBillableActions(
policyID,
undefined,
{
[`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END}${ownerAccountID}` as const]: {
...billingGraceEndPeriod,
value: getUnixTime(subDays(new Date(), 3)),
},
},
undefined,
policy,
),
).toBeTruthy();
});

it('should not restrict for non-owner when policy is passed directly but billing grace period is not overdue', async () => {
const policyID = '2001';
const ownerAccountID = 2001;
const policy = {
...createRandomPolicy(Number(policyID)),
ownerAccountID,
};

expect(
shouldRestrictUserBillableActions(
policyID,
undefined,
{
[`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END}${ownerAccountID}` as const]: {
...billingGraceEndPeriod,
value: getUnixTime(addDays(new Date(), 3)),
},
},
undefined,
policy,
),
).toBeFalsy();
});

it('should use passed policy and ignore Onyx-stored policies', async () => {
const accountID = 1;
const policyID = '2001';
const differentOwnerPolicy = {
...createRandomPolicy(Number(policyID)),
ownerAccountID: 9999,
};

await Onyx.multiSet({
[ONYXKEYS.SESSION]: {email: '', accountID},
[ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED]: 8010,
[`${ONYXKEYS.COLLECTION.POLICY}${policyID}` as const]: {
...createRandomPolicy(Number(policyID)),
ownerAccountID: accountID,
},
});

expect(shouldRestrictUserBillableActions(policyID, getUnixTime(subDays(new Date(), 3)), undefined, undefined, differentOwnerPolicy)).toBeFalsy();
});
});

describe('getSubscriptionStatus', () => {
afterEach(async () => {
await Onyx.clear();
Expand Down
Loading