Skip to content

Commit

Permalink
Merge branch 'main' into mkzie2-issue/55083
Browse files Browse the repository at this point in the history
  • Loading branch information
mkzie2 committed Feb 8, 2025
2 parents 45c1d90 + b0b7709 commit f61fcf9
Show file tree
Hide file tree
Showing 75 changed files with 1,156 additions and 405 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
ENVIRONMENT=development
NEW_EXPENSIFY_URL=https://new.expensify.com/
SECURE_EXPENSIFY_URL=https://secure.expensify.com.dev/
EXPENSIFY_URL=https://www.expensify.com.dev/
Expand Down
2 changes: 1 addition & 1 deletion Mobile-Expensify
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
versionCode 1009009500
versionName "9.0.95-0"
versionCode 1009009502
versionName "9.0.95-2"
// Supported language variants must be declared here to avoid from being removed during the compilation.
// This also helps us to not include unnecessary language variants in the APK.
resConfigs "en", "es"
Expand Down
80 changes: 80 additions & 0 deletions docs/articles/new-expensify/workspaces/Set-up-rules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
title: Workspace Rules
description: Configure and manage rules for your workspace
---

Rules help you control and manage expenses, ensuring compliance with your company’s policies. Whether you want to set limits on spending, require receipts, or prevent self-approvals, rules give you the flexibility to tailor expense management to your needs.

{% include info.html %}
Rules are available for Control workspaces only. You must be a Workspace Admin to complete this process.
{% include end-info.html %}

# Enable rules

1. Click your profile image or icon in the bottom menu.
2. Scroll down and click **Workspaces** in the left menu.
3. Select the workspace you want to enable rules for.
4. Click **More features** in the left menu.
5. Under the Manage section, enable the **Rules** toggle.

# Configure workspace expense rules

Once Rules are enabled, you can customize individual expense settings:

1. Click **Rules** in the left menu.
2. Under **Expenses**, configure the following:

- **Receipt required amount**: Specify when receipts are required. Accepts decimal values.
- **Max expense amount**: Set a spending limit per expense. Accepts decimal values.
- **Max expense age (days)**: Define how old an expense can be. Accepts whole numbers only.
- **Billable default**: Set expenses as billable or non-billable by default.
- **eReceipts**: Enable eReceipts for most USD credit transactions. This is available when the Default currency set in the Overview is set to USD.

# Configure expense report rules

1. Click **Rules** in the left menu.
2. Under **Expense Reports**, configure the following:

- **Custom report names**: Create default titles for reports.
- **Prevent self-approvals**: Stop users from approving their own reports.
- **Auto-approve compliant reports**: Automatically approve reports below a set amount and set a random report audit percentage.
- **Auto-pay approved reports**: Automatically pay reports below a specific threshold when approved.

# Configure category rules

1. Go to **Categories** in the left menu within workspace settings
2. Select a category to open its details.
3. Under **Category Rules**, configure the following:

- **Enable category**: Make the category visible to workspace members when creating or submitting expenses
- **Require description**: Make descriptions mandatory for certain categories.
- **Approver**: Assign a specific approver per category.
- **Default tax rate**: Set a default tax rate for each category.
- **Max amount**: Define spending limits by category.
- **Require receipts over**: Specify when receipts are required for category expenses.

# Configure tag rules

1. Go to **Tags** in the left menu within workspace settings.
2. Select a tag to open its details.
3. Under **Tag Rules**, configure the following:

- **Tag approver**: Assign approvers for specific tags.

# Manage default categories and billable expenses

- **Default Categories**: Auto-categorize expenses based on the Merchant Category Code (MCC). Set in **Categories** under the **Settings** option on the top right of the page for credit card transactions and receipts.
- **Billable Expenses**: Set in **Tags** under the **Settings** option on the top right of the page to require tagging always or just when an expense is flagged as billable.

{% include faq-begin.md %}

**Who can manage rules?**

Only Workspace Admins can configure and manage rules.

**What happens if I disable rules?**

Disabling rules will remove any violations or warnings from draft or outstanding expenses that relied on those rules.

{% include faq-end.md %}

2 changes: 1 addition & 1 deletion ios/NewExpensify/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>9.0.95.0</string>
<string>9.0.95.2</string>
<key>FullStory</key>
<dict>
<key>OrgId</key>
Expand Down
2 changes: 1 addition & 1 deletion ios/NewExpensifyTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>9.0.95.0</string>
<string>9.0.95.2</string>
</dict>
</plist>
2 changes: 1 addition & 1 deletion ios/NotificationServiceExtension/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<key>CFBundleShortVersionString</key>
<string>9.0.95</string>
<key>CFBundleVersion</key>
<string>9.0.95.0</string>
<string>9.0.95.2</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
"version": "9.0.95-0",
"version": "9.0.95-2",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
Expand Down
14 changes: 11 additions & 3 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -688,8 +688,7 @@ const CONST = {
IS_ANYONE_ELSE_BENEFICIAL_OWNER: 2,
BENEFICIAL_OWNER_DETAILS_FORM: 3,
ARE_THERE_MORE_BENEFICIAL_OWNERS: 4,
OWNERSHIP_CHART: 5,
BENEFICIAL_OWNERS_LIST: 6,
BENEFICIAL_OWNERS_LIST: 5,
},
BENEFICIAL_OWNER_DATA: {
BENEFICIAL_OWNER_KEYS: 'beneficialOwnerKeys',
Expand All @@ -703,7 +702,13 @@ const CONST = {
CITY: 'city',
STATE: 'state',
ZIP_CODE: 'zipCode',
COUNTRY: 'country',
COUNTRY: 'nationality',
PROOF_OF_OWNERSHIP: 'proofOfBeneficialOwner',
COPY_OF_ID: 'copyOfIDForBeneficialOwner',
ADDRESS_PROOF: 'addressProofForBeneficialOwner',
CODICE_FISCALE: 'codiceFisclaleTaxID',
FULL_NAME: 'fullName',
RESIDENTIAL_ADDRESS: 'residentialAddress',
},
CURRENT_USER_KEY: 'currentUser',
},
Expand Down Expand Up @@ -741,6 +746,7 @@ const CONST = {
NEWDOT_MANAGER_MCTEST: 'newDotManagerMcTest',
NEWDOT_INTERNATIONAL_DEPOSIT_BANK_ACCOUNT: 'newDotInternationalDepositBankAccount',
NSQS: 'nsqs',
CUSTOM_RULES: 'customRules',
},
BUTTON_STATES: {
DEFAULT: 'default',
Expand All @@ -758,6 +764,7 @@ const CONST = {
AU: 'AU',
CA: 'CA',
GB: 'GB',
IT: 'IT',
},
DESKTOP_DEEPLINK_APP_STATE: {
CHECKING: 'checking',
Expand Down Expand Up @@ -4992,6 +4999,7 @@ const CONST = {
PER_DAY_LIMIT: 'perDayLimit',
RECEIPT_NOT_SMART_SCANNED: 'receiptNotSmartScanned',
RECEIPT_REQUIRED: 'receiptRequired',
CUSTOM_RULES: 'customRules',
RTER: 'rter',
SMARTSCAN_FAILED: 'smartscanFailed',
SOME_TAG_LEVELS_REQUIRED: 'someTagLevelsRequired',
Expand Down
3 changes: 3 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,8 @@ const ONYXKEYS = {
RULES_MAX_EXPENSE_AMOUNT_FORM_DRAFT: 'rulesMaxExpenseAmountFormDraft',
RULES_MAX_EXPENSE_AGE_FORM: 'rulesMaxExpenseAgeForm',
RULES_MAX_EXPENSE_AGE_FORM_DRAFT: 'rulesMaxExpenseAgeFormDraft',
RULES_CUSTOM_FORM: 'rulesCustomForm',
RULES_CUSTOM_FORM_DRAFT: 'rulesCustomFormDraft',
DEBUG_DETAILS_FORM: 'debugDetailsForm',
DEBUG_DETAILS_FORM_DRAFT: 'debugDetailsFormDraft',
WORKSPACE_PER_DIEM_FORM: 'workspacePerDiemForm',
Expand Down Expand Up @@ -850,6 +852,7 @@ type OnyxFormValuesMapping = {
[ONYXKEYS.FORMS.RULES_REQUIRED_RECEIPT_AMOUNT_FORM]: FormTypes.RulesRequiredReceiptAmountForm;
[ONYXKEYS.FORMS.RULES_MAX_EXPENSE_AMOUNT_FORM]: FormTypes.RulesMaxExpenseAmountForm;
[ONYXKEYS.FORMS.RULES_MAX_EXPENSE_AGE_FORM]: FormTypes.RulesMaxExpenseAgeForm;
[ONYXKEYS.FORMS.RULES_CUSTOM_FORM]: FormTypes.RulesCustomForm;
[ONYXKEYS.FORMS.SEARCH_SAVED_SEARCH_RENAME_FORM]: FormTypes.SearchSavedSearchRenameForm;
[ONYXKEYS.FORMS.DEBUG_DETAILS_FORM]: FormTypes.DebugReportForm | FormTypes.DebugReportActionForm | FormTypes.DebugTransactionForm | FormTypes.DebugTransactionViolationForm;
[ONYXKEYS.FORMS.INTERNATIONAL_BANK_ACCOUNT_FORM]: FormTypes.InternationalBankAccountForm;
Expand Down
4 changes: 4 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1624,6 +1624,10 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/rules/billable',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/rules/billable` as const,
},
RULES_CUSTOM: {
route: 'settings/workspaces/:policyID/rules/custom',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/rules/custom` as const,
},
// Referral program promotion
REFERRAL_DETAILS_MODAL: {
route: 'referral/:contentType',
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,7 @@ const SCREENS = {
RULES_MAX_EXPENSE_AMOUNT: 'Rules_Max_Expense_Amount',
RULES_MAX_EXPENSE_AGE: 'Rules_Max_Expense_Age',
RULES_BILLABLE_DEFAULT: 'Rules_Billable_Default',
RULES_CUSTOM: 'Rules_Custom',
PER_DIEM: 'Per_Diem',
PER_DIEM_IMPORT: 'Per_Diem_Import',
PER_DIEM_IMPORTED: 'Per_Diem_Imported',
Expand Down
4 changes: 3 additions & 1 deletion src/components/ReportActionItem/MoneyRequestView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -407,10 +407,12 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals
}, [transactionViolations, translate]);

const receiptRequiredViolation = transactionViolations?.some((violation) => violation.name === CONST.VIOLATIONS.RECEIPT_REQUIRED);
const customRulesViolation = transactionViolations?.some((violation) => violation.name === CONST.VIOLATIONS.CUSTOM_RULES);

// Whether to show receipt audit result (e.g.`Verified`, `Issue Found`) and messages (e.g. `Receipt not verified. Please confirm accuracy.`)
// `!!(receiptViolations.length || didReceiptScanSucceed)` is for not showing `Verified` when `receiptViolations` is empty and `didReceiptScanSucceed` is false.
const shouldShowAuditMessage = !isReceiptBeingScanned && (hasReceipt || receiptRequiredViolation) && !!(receiptViolations.length || didReceiptScanSucceed) && isPaidGroupPolicy(report);
const shouldShowAuditMessage =
!isReceiptBeingScanned && (hasReceipt || !!receiptRequiredViolation || !!customRulesViolation) && !!(receiptViolations.length || didReceiptScanSucceed) && isPaidGroupPolicy(report);
const shouldShowReceiptAudit = isReceiptAllowed && (shouldShowReceiptEmptyState || hasReceipt);

const errors = {
Expand Down
3 changes: 3 additions & 0 deletions src/components/SubStepForms/YesNoStep.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, {useMemo, useState} from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import FormProvider from '@components/Form/FormProvider';
import type {Choice} from '@components/RadioButtons';
import RadioButtons from '@components/RadioButtons';
Expand Down Expand Up @@ -28,6 +29,7 @@ type YesNoStepProps = {
function YesNoStep({title, description, defaultValue, onSelectedValue, submitButtonStyles}: YesNoStepProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT);
const [value, setValue] = useState(defaultValue);

const handleSubmit = () => {
Expand Down Expand Up @@ -55,6 +57,7 @@ function YesNoStep({title, description, defaultValue, onSelectedValue, submitBut
onSubmit={handleSubmit}
style={[styles.mh5, styles.flexGrow1]}
submitButtonStyles={submitButtonStyles}
isLoading={reimbursementAccount?.isSavingCorpayOnboardingBeneficialOwnersFields}
>
<Text style={[styles.textHeadlineLineHeightXXL]}>{title}</Text>
<Text style={[styles.pv3, styles.textSupporting]}>{description}</Text>
Expand Down
12 changes: 3 additions & 9 deletions src/hooks/useHtmlPaste/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {useCallback, useEffect} from 'react';
import {isMobile} from '@libs/Browser';
import Parser from '@libs/Parser';
import CONST from '@src/CONST';
import type UseHtmlPaste from './types';
Expand Down Expand Up @@ -90,14 +89,9 @@ const useHtmlPaste: UseHtmlPaste = (textInputRef, preHtmlPasteCallback, isActive
*/
const handlePastePlainText = useCallback(
(event: ClipboardEvent) => {
const markdownText = event.clipboardData?.getData('text/plain');
// Updated paste logic to address issue #53718
// When copying from a chat conversation, the clipboard contains markdown-formatted text.
// On desktop web, users have the option to paste as plain text, but this feature is unavailable on mobile web.
// A conditional check is added to determine whether to retain markdown or convert it to plain text based on the platform.
if (markdownText) {
const parsedText = isMobile() ? markdownText : Parser.htmlToText(Parser.replace(markdownText));
paste(parsedText);
const plainText = event.clipboardData?.getData('text/plain');
if (plainText) {
paste(plainText);
}
},
[paste],
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useSubStep/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ export default function useSubStep<TProps extends SubStepProps>({bodyContent, on
[screenIndex, lastScreenIndex, skipSteps, onFinished, onNextSubStep],
);

const moveTo = useCallback((step: number) => {
isEditing.current = true;
const moveTo = useCallback((step: number, turnOnEditMode?: boolean) => {
isEditing.current = !(turnOnEditMode !== undefined && !turnOnEditMode);
setScreenIndex(step);
}, []);

Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useSubStep/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type SubStepProps = {
onNext: (data?: unknown) => void;

/** moves user to passed sub step */
onMove: (step: number) => void;
onMove: (step: number, turnOnEditMode?: boolean) => void;

/** index of currently displayed sub step */
screenIndex?: number;
Expand Down
Loading

0 comments on commit f61fcf9

Please sign in to comment.