diff --git a/.eslintrc.js b/.eslintrc.js index 0661183101ab..33be8cb62fcd 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -51,6 +51,10 @@ const restrictedImportPaths = [ name: '@styles/theme/illustrations', message: 'Do not import theme illustrations directly. Please use the `useThemeIllustrations` hook instead.', }, + { + name: 'date-fns/locale', + message: "Do not import 'date-fns/locale' directly. Please use the submodule import instead, like 'date-fns/locale/en-GB'.", + }, ]; const restrictedImportPatterns = [ diff --git a/android/app/build.gradle b/android/app/build.gradle index c919f731795e..c30938a6da5c 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -98,8 +98,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001045302 - versionName "1.4.53-2" + versionCode 1001045401 + versionName "1.4.54-1" } flavorDimensions "default" diff --git a/docs/articles/expensify-classic/expenses/expenses/Add-an-expense.md b/docs/articles/expensify-classic/expenses/expenses/Add-an-expense.md new file mode 100644 index 000000000000..461748c6af9e --- /dev/null +++ b/docs/articles/expensify-classic/expenses/expenses/Add-an-expense.md @@ -0,0 +1,102 @@ +--- +title: Add an expense +description: Create a new expense in Expensify +--- +
+ +You can add an expense automatically with SmartScan or enter the expense details manually. + +# SmartScan a receipt + +{% include selector.html values="desktop, mobile" %} + +{% include option.html value="desktop" %} + +You can upload pictures of your receipts to Expensify and SmartScan will automatically capture the receipt details including the merchant, date, total, and currency. + +1. Click the **Expenses** tab. +2. Click the + icon in the top right and select **Scan receipt**. +3. Upload a saved image of a receipt. + +{% include end-option.html %} + +{% include option.html value="mobile" %} + +You can use the Expensify mobile app to take a picture of your receipts and SmartScan will automatically capture the receipt details including the merchant, date, total, and currency. + +1. Open the mobile app and tap the camera icon in the bottom right corner. +2. Upload or take a photo of your receipt. + - **To upload a photo** of a receipt you have saved on your phone, tap the photo icon in the left corner. + - **To take a photo**, tap the camera icon in the right corner to select the mode, make sure all of the transaction details are clearly visible,and then take the photo. + - Normal Mode: Upload one receipt. + - Rapid Fire Mode: Upload multiple receipts at once. + +You can open any receipt and click **Fill out details myself** to add or edit the merchant, date, current, total, description, category, or add attendees for group expenses. You can also add the expense to a report, determine if it is a reimbursable expense, or split the expense if multiple expense categories are included on one receipt. + +{% include info.html %} +**For iPhones**: You can also hard press the Expensify app icon on your phone to open a shortcut that automatically opens the camera to SmartScan a receipt. +{% include end-info.html %} + +{% include end-option.html %} + +{% include end-selector.html %} + +# Email a receipt + +You can also email receipts to SmartScan by sending them to receipts@expensify.com from an email address tied to your Expensify account (either a primary or secondary email). SmartScan will automatically pull all of the details from the receipt, fill them in for you, and add the receipt to the Expenses tab on your account. + +{% include info.html %} +**For copilots**: To ensure a receipt is routed to the Expensify account you are copiloting instead of your own account, email the receipt to receipts@expensify.com with the email address of the account you are copiloting as the subject line of the email. +{% include end-info.html %} + +# Add an expense manually + +{% include selector.html values="desktop, mobile" %} + +{% include option.html value="desktop" %} + +1. Click the **Expenses** tab. +2. Click the + icon in the top right. +3. Select the type of expense. + - **Manually create**: Manually enter receipt details. + - **Scan receipt**: Upload a saved image of a receipt. + - **Create multiple**: Manually enter multiple expenses at once. + - **Time**: Create an expense based on hours. + - **Distance**: Create an expense based on distance. + - Manually Create: Manually enter the distance details for the expense. + - Create from Map: Enter the start and end destination and Expensify will help you create a receipt for the trip. +4. Click **Save**. +{% include end-option.html %} + +{% include option.html value="mobile" %} + +1. Tap the ☰ menu icon in the top left. +2. Tap **Expenses**. +3. Tap the + icon in the top right. +4. Tap the correct expense type and enter the expense details. + - **Manually create**: Manually enter receipt details. + - **Time**: Enter work time and rate. + - **Manually create (Distance)**: Manually enter trip details by total distance. + - **Odometer**: Manually enter trip details by start and end odometer readings. + - **Start GPS**: Track distance while using the Expensify app to automatically calculate the distance in real time during the trip. +5. Tap **Save**. +{% include end-option.html %} + +{% include end-selector.html %} + +{% include info.html %} +If you are an employee under a company workspace, you may not see all of the different expense type options depending on your company’s workspace settings. +{% include end-info.html %} + +# FAQs + +**What’s the difference between a reimbursable and non-reimbursable expense?** + +- Reimbursable expenses are things that you pay for with your own money that the company has agreed to pay you back for (like business travel paid for with personal funds). +- Non-reimbursable expenses are things you pay for with company money that need to be documented for accounting purposes (like a lunch paid for with a company card). + +{% include info.html %} +If you are an employee under a company workspace, your expenses may automatically be configured as reimbursable or non-reimbursable depending on the details that are entered. If an expense is incorrectly labeled, you must reach out to an admin to have it corrected. +{% include end-info.html %} + +
diff --git a/docs/articles/expensify-classic/expenses/expenses/Create-Expenses.md b/docs/articles/expensify-classic/expenses/expenses/Create-Expenses.md deleted file mode 100644 index 7fa714189542..000000000000 --- a/docs/articles/expensify-classic/expenses/expenses/Create-Expenses.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -title: Create-Expenses.md -description: This is an article that shows you all the ways that you can create Expenses in Expensify! ---- - - -# About -Whether you're using SmartScan for automatic expense creation, or manually creating, splitting, or duplicating expenses, you can rest assured your expenses will be correctly tracked in Expensify. - -# How-to Create Expenses -## Using SmartScan -Use the big green camera button within the Expensify mobile app to snap a photo of your physical receipt to have it SmartScanned. -For digital or emailed receipts, simply forward them to receipts@expensify.com and it will be SmartScanned and added to your Expensify account. - -There’s no need to keep the app open and most SmartScans are finished within the hour. If more details are needed, Concierge will reach out to you with a friendly message. -## Using the Mobile App -Simply tap the **+** icon in the top-right corner -Choose **Expense** and then select **Manually Create**. -If you don't have a receipt handy or want to add it later, fill in your expense details and click the **Save** button. -## Using the Expensify Website -Log into the Expensify website -Click on the **Expenses** page and find the **New Expense** dropdown. -Select your expense type, hit the **Save** button and you're all set. -You can then add details like the Merchant and Category, attach a receipt image, and even add a description. -# How to Split an Expense -Splitting an expense in Expensify allows you to break down a single expense into multiple expenses. Each split expense is treated as an individual expense which can be categorized and tagged separately. The same receipt image will be attached to all of the split expenses, allowing you to divide a single expense into smaller, more manageable expenses. -To split an expense on the mobile app: - -1. Open an expense. -2. At the bottom of the screen, tap **More Options**. -3. Then, use the **Split** button to divide the expense. - -To split an expense on the Expensify website: - -1. Click on the expense you want to split. -2. Click on the **Split** button. - - On the Expenses page, this button is at the top. - - Within an individual expense, you'll find it at the bottom. -3. This will automatically be split in two, but you can decide how many expenses you want to split it into by clicking on the **Add Split** button. - - Remember, the total of all pieces must add up to the original expense amount, and no piece can have a $0.00 amount (or you won't be able to save the changes). - -# How to Create Bulk Expenses - -If you have multiple saved receipt images or PDFs to upload, you can drag and drop them onto your Expenses page in batches of ten - this will start the SmartScan process for all of them. - -You can also create a number of future 'placeholder' expenses for your recurring expenses (such as recurring bills or subscriptions) which you don't have receipts for by clicking *New Expense > Create Multiple* to quickly add multiple expenses in batches of up to ten. - -# How to Edit Bulk Expenses -Editing expenses in bulk will allow you to apply the same coding across multiple expenses and is a web-only feature. To bulk edit expenses: -Go to the Expenses page. -To narrow down your selection, use the filters (e.g. "Merchant" and "Draft") to find the specific expenses you want to edit. -Select all the expenses you want to edit. -Click on the **Edit Multiple** button at the top of the page. -# How to Edit Expenses on a Report -If you’d like to edit expenses within a Draft report: - -1. Click on the Report containing all the expenses. -2. Click on **Details**. -3. Click on the Pencil icon. -3. Select the **Edit Multiple** button. - -If you've already submitted your report, you'll need to Retract it or have it Unapproved first before you can edit the expenses. - -# FAQ - -## Does Expensify account for duplicates? - -Yes, Expensify will account for duplicates. Expensify works behind the scenes to identify duplicate expenses before they are submitted, warning employees when they exist. If a duplicate expense is submitted, the same warning will be shown to the approver responsible for reviewing the report. - -If two expenses are SmartScanned on the same day for the same amount, they will be flagged as duplicates unless: -The expenses were split from a single expense, -The expenses were imported from a credit card, or -Matching email receipts sent to receipts@expensify.com were received with different timestamps. - -## How do I resolve a duplicate expense? - -If Concierge has let you know it's flagged a receipt as a duplicate, scanning the receipt again will trigger the same duplicate flagging.Users have the ability to resolve duplicates by either deleting the duplicated transactions, merging them, or ignoring them (if they are legitimately separate expenses of the same date and amount). - -## How do I recover a duplicate or undelete an expense? - -To recover a duplicate or undelete an expense: -Log into your Expensify account on the website and navigate to the Expenses page -Use the filters to search for deleted expenses by selecting the "Deleted" filter -Select the checkbox next to the expenses you want to restore -Click the **Undelete** button and you're all set. You’ll find the expense on your Expenses page again. - -# Deep Dive - -## What are the different Expense statuses? - -There are a number of different expense statuses in Expensify: -1. **Personal**: Personal expenses are not yet part of a report (and therefore unsubmitted) and are not viewable by anyone but the expense creator/owner. -2. **Draft**: Draft expenses are seen as still in progress, and are unsubmitted. Your Policy Admin will be able to view them, making this a collaborative step toward reimbursement. -3. **Processing**: Processing expenses are submitted, but waiting for approval. -4. **Approved**: If it's a non-reimbursable expense, the workflow is complete at this point. If it's a reimbursable expense, you're one step closer to getting paid. -5. **Reimbursed**: Reimbursed expenses are fully settled. You can check the Report Comments to see when you'll get paid. -6. **Closed**: Sometimes an expense accidentally ends up on your Individual Policy, falling into the Closed status. You’ll need to reopen the report and change the Policy by clicking on the **Details** tab in order to resubmit your report. - -## What are Violations? - -Violations represent errors or discrepancies that Expensify has picked up and need to be corrected before a report can be successfully submitted. The one exception is when an expense comment is added, it will override the violation - as the user is providing a valid reason for submission. - -To enable or configure violations according to your policy, go to **Settings > Policies > _Policy Name_ > Expenses > Expense Violations**. Keep in mind that Expensify includes certain system mandatory violations that can't be disabled, even if your policy has violations turned off. - -You can spot violations by the exclamation marks (!) attached to expenses. Hovering over the symbol will provide a brief description and you can find more detailed information below the list of expenses. The two types of violations are: -1. **Red**: These indicate violations directly tied to your report's Policy settings. They are clear rule violations that must be addressed before submission. -2. **Yellow**: Concierge will highlight items that require attention but may not necessarily need corrective action. For example, if a receipt was SmartScanned and then the amount was modified, we’ll bring it to your attention so that it can be manually reviewed. - -## How to Track Attendees - -Attendee tracking makes it easy to track shared expenses and maintain transparency in your group spending. - -Internal attendees are considered users within your policies or domain. To add internal attendees on mobile or web: -1. Click or tap the **Attendee** field within your expense. -2. Select the internal attendees you'd like to add from the list of searchable users. -3. You can continue adding more attendees or save the Expense. - -External attendees are considered users outside your group policy or domain. To add external attendees: -1. Click or tap the **Attendee** field within your expense. -2. Type in the individual's name or email address. -3. Tap **Add** to include the attendee. -4. You can continue adding more attendees or save the Expense. - -To remove an attendee from an expense: - -1. Open the expense. -2. Click or tap the **Attendees** field to display the list of attendees. -3. From the list, de-select the attendees you'd like to remove from the expense. diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 2e4c69affd9b..eb51f4499583 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.4.53 + 1.4.54 CFBundleSignature ???? CFBundleURLTypes @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.53.2 + 1.4.54.1 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index b6aab5371ba4..faf2eea9f738 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.4.53 + 1.4.54 CFBundleSignature ???? CFBundleVersion - 1.4.53.2 + 1.4.54.1 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 6b0ad08aad65..8abab817dfb1 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -11,9 +11,9 @@ CFBundleName $(PRODUCT_NAME) CFBundleShortVersionString - 1.4.53 + 1.4.54 CFBundleVersion - 1.4.53.2 + 1.4.54.1 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index a84a72dd5167..ba15feeacae6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.53-2", + "version": "1.4.54-1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.53-2", + "version": "1.4.54-1", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -56,7 +56,6 @@ "expo-av": "~13.10.4", "expo-image": "1.10.1", "expo-image-manipulator": "11.8.0", - "fbjs": "^3.0.2", "htmlparser2": "^7.2.0", "idb-keyval": "^6.2.1", "jest-expo": "50.0.1", @@ -65,7 +64,6 @@ "lottie-react-native": "6.4.1", "mapbox-gl": "^2.15.0", "onfido-sdk-ui": "14.15.0", - "patch-package": "^8.0.0", "process": "^0.11.10", "prop-types": "^15.7.2", "pusher-js": "8.3.0", @@ -89,7 +87,6 @@ "react-native-gesture-handler": "2.14.1", "react-native-google-places-autocomplete": "2.5.6", "react-native-haptic-feedback": "^2.2.0", - "react-native-image-pan-zoom": "^2.1.12", "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#8393b7e58df6ff65fd41f60aee8ece8822c91e2b", "react-native-key-command": "^1.0.6", @@ -127,7 +124,6 @@ "react-web-config": "^1.0.0", "react-webcam": "^7.1.1", "react-window": "^1.8.9", - "save": "^2.4.0", "semver": "^7.5.2", "shim-keyboard-event-key": "^1.0.3", "underscore": "^1.13.1" @@ -225,6 +221,7 @@ "jest-transformer-svg": "^2.0.1", "memfs": "^4.6.0", "onchange": "^7.1.0", + "patch-package": "^8.0.0", "portfinder": "^1.0.28", "prettier": "^2.8.8", "pusher-js-mock": "^0.3.3", @@ -19786,6 +19783,9 @@ }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true, "license": "BSD-2-Clause" }, "node_modules/7zip-bin": { @@ -20721,6 +20721,9 @@ }, "node_modules/async": { "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true, "license": "MIT" }, "node_modules/async-each": { @@ -25442,6 +25445,9 @@ }, "node_modules/duplexer": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true, "license": "MIT" }, "node_modules/duplexify": { @@ -27058,19 +27064,6 @@ "node": ">= 0.6" } }, - "node_modules/event-stream": { - "version": "4.0.1", - "license": "MIT", - "dependencies": { - "duplexer": "^0.1.1", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" - } - }, "node_modules/event-target-shim": { "version": "5.0.1", "license": "MIT", @@ -28551,10 +28544,6 @@ "node": ">= 0.6" } }, - "node_modules/from": { - "version": "0.1.7", - "license": "MIT" - }, "node_modules/from2": { "version": "2.3.0", "dev": true, @@ -34135,6 +34124,9 @@ }, "node_modules/klaw-sync": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.1.11" @@ -34413,10 +34405,6 @@ "version": "4.17.21", "license": "MIT" }, - "node_modules/lodash.assign": { - "version": "4.2.0", - "license": "MIT" - }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "dev": true, @@ -34855,10 +34843,6 @@ "dev": true, "license": "MIT" }, - "node_modules/map-stream": { - "version": "0.0.7", - "license": "MIT" - }, "node_modules/map-visit": { "version": "1.0.0", "devOptional": true, @@ -35996,10 +35980,6 @@ "node": ">=4" } }, - "node_modules/mingo": { - "version": "1.3.3", - "license": "MIT" - }, "node_modules/minimalistic-assert": { "version": "1.0.1", "license": "ISC" @@ -37409,6 +37389,9 @@ }, "node_modules/patch-package": { "version": "8.0.0", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", + "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", + "dev": true, "license": "MIT", "dependencies": { "@yarnpkg/lockfile": "^1.1.0", @@ -37437,6 +37420,9 @@ }, "node_modules/patch-package/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -37450,6 +37436,9 @@ }, "node_modules/patch-package/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -37464,6 +37453,9 @@ }, "node_modules/patch-package/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -37474,10 +37466,16 @@ }, "node_modules/patch-package/node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, "node_modules/patch-package/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -37485,6 +37483,9 @@ }, "node_modules/patch-package/node_modules/open": { "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, "license": "MIT", "dependencies": { "is-docker": "^2.0.0", @@ -37499,6 +37500,9 @@ }, "node_modules/patch-package/node_modules/rimraf": { "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, "license": "ISC", "dependencies": { "glob": "^7.1.3" @@ -37509,6 +37513,9 @@ }, "node_modules/patch-package/node_modules/slash": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -37516,6 +37523,9 @@ }, "node_modules/patch-package/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -37526,6 +37536,9 @@ }, "node_modules/patch-package/node_modules/tmp": { "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, "license": "MIT", "dependencies": { "os-tmpdir": "~1.0.2" @@ -37622,16 +37635,6 @@ "node": ">=8" } }, - "node_modules/pause-stream": { - "version": "0.0.11", - "license": [ - "MIT", - "Apache2" - ], - "dependencies": { - "through": "~2.3" - } - }, "node_modules/pbf": { "version": "3.2.1", "license": "BSD-3-Clause", @@ -39089,14 +39092,6 @@ "react-native": ">=0.60.0" } }, - "node_modules/react-native-image-pan-zoom": { - "version": "2.1.12", - "license": "ISC", - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, "node_modules/react-native-image-picker": { "version": "7.0.3", "license": "MIT", @@ -41261,16 +41256,6 @@ "truncate-utf8-bytes": "^1.0.0" } }, - "node_modules/save": { - "version": "2.5.0", - "license": "ISC", - "dependencies": { - "async": "^3.2.2", - "event-stream": "^4.0.1", - "lodash.assign": "^4.2.0", - "mingo": "1" - } - }, "node_modules/sax": { "version": "1.2.4", "license": "ISC" @@ -42461,14 +42446,6 @@ "node": ">= 0.10.0" } }, - "node_modules/stream-combiner": { - "version": "0.2.2", - "license": "MIT", - "dependencies": { - "duplexer": "~0.1.1", - "through": "~2.3.4" - } - }, "node_modules/stream-each": { "version": "1.2.3", "dev": true, diff --git a/package.json b/package.json index e0f357fd0f8a..d1974b99b91e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.53-2", + "version": "1.4.54-1", "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.", @@ -107,7 +107,6 @@ "expo-av": "~13.10.4", "expo-image": "1.10.1", "expo-image-manipulator": "11.8.0", - "fbjs": "^3.0.2", "htmlparser2": "^7.2.0", "idb-keyval": "^6.2.1", "jest-expo": "50.0.1", @@ -116,7 +115,6 @@ "lottie-react-native": "6.4.1", "mapbox-gl": "^2.15.0", "onfido-sdk-ui": "14.15.0", - "patch-package": "^8.0.0", "process": "^0.11.10", "prop-types": "^15.7.2", "pusher-js": "8.3.0", @@ -140,7 +138,6 @@ "react-native-gesture-handler": "2.14.1", "react-native-google-places-autocomplete": "2.5.6", "react-native-haptic-feedback": "^2.2.0", - "react-native-image-pan-zoom": "^2.1.12", "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#8393b7e58df6ff65fd41f60aee8ece8822c91e2b", "react-native-key-command": "^1.0.6", @@ -178,7 +175,6 @@ "react-web-config": "^1.0.0", "react-webcam": "^7.1.1", "react-window": "^1.8.9", - "save": "^2.4.0", "semver": "^7.5.2", "shim-keyboard-event-key": "^1.0.3", "underscore": "^1.13.1" @@ -276,6 +272,7 @@ "jest-transformer-svg": "^2.0.1", "memfs": "^4.6.0", "onchange": "^7.1.0", + "patch-package": "^8.0.0", "portfinder": "^1.0.28", "prettier": "^2.8.8", "pusher-js-mock": "^0.3.3", diff --git a/src/CONST.ts b/src/CONST.ts index 0135070cdff3..d15d82cd7c7c 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -43,10 +43,21 @@ const keyInputRightArrow = KeyCommand?.constants?.keyInputRightArrow ?? 'keyInpu // describes if a shortcut key can cause navigation const KEYBOARD_SHORTCUT_NAVIGATION_TYPE = 'NAVIGATION_SHORTCUT'; +const chatTypes = { + POLICY_ANNOUNCE: 'policyAnnounce', + POLICY_ADMINS: 'policyAdmins', + DOMAIN_ALL: 'domainAll', + POLICY_ROOM: 'policyRoom', + POLICY_EXPENSE_CHAT: 'policyExpenseChat', + SELF_DM: 'selfDM', +} as const; + // Explicit type annotation is required const cardActiveStates: number[] = [2, 3, 4, 7]; const CONST = { + MERGED_ACCOUNT_PREFIX: 'MERGED_', + DEFAULT_POLICY_ROOM_CHAT_TYPES: [chatTypes.POLICY_ADMINS, chatTypes.POLICY_ANNOUNCE, chatTypes.DOMAIN_ALL], ANDROID_PACKAGE_NAME, ANIMATED_TRANSITION: 300, ANIMATED_TRANSITION_FROM_VALUE: 100, @@ -346,6 +357,9 @@ const CONST = { INSTALLED: 'installed', NOT_INSTALLED: 'not-installed', }, + TAX_RATES: { + NAME_MAX_LENGTH: 50, + }, PLATFORM: { IOS: 'ios', ANDROID: 'android', @@ -732,14 +746,7 @@ const CONST = { IOU: 'iou', TASK: 'task', }, - CHAT_TYPE: { - POLICY_ANNOUNCE: 'policyAnnounce', - POLICY_ADMINS: 'policyAdmins', - DOMAIN_ALL: 'domainAll', - POLICY_ROOM: 'policyRoom', - POLICY_EXPENSE_CHAT: 'policyExpenseChat', - SELF_DM: 'selfDM', - }, + CHAT_TYPE: chatTypes, WORKSPACE_CHAT_ROOMS: { ANNOUNCE: '#announce', ADMINS: '#admins', diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index d7f3104cd8b4..e91b4d491423 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -339,6 +339,8 @@ const ONYXKEYS = { WORKSPACE_DESCRIPTION_FORM_DRAFT: 'workspaceDescriptionFormDraft', WORKSPACE_RATE_AND_UNIT_FORM: 'workspaceRateAndUnitForm', WORKSPACE_RATE_AND_UNIT_FORM_DRAFT: 'workspaceRateAndUnitFormDraft', + WORKSPACE_TAX_CUSTOM_NAME: 'workspaceTaxCustomName', + WORKSPACE_TAX_CUSTOM_NAME_DRAFT: 'workspaceTaxCustomNameDraft', POLICY_CREATE_DISTANCE_RATE_FORM: 'policyCreateDistanceRateForm', POLICY_CREATE_DISTANCE_RATE_FORM_DRAFT: 'policyCreateDistanceRateFormDraft', CLOSE_ACCOUNT_FORM: 'closeAccount', @@ -411,6 +413,8 @@ const ONYXKEYS = { EXIT_SURVEY_RESPONSE_FORM_DRAFT: 'exitSurveyResponseFormDraft', POLICY_TAG_NAME_FORM: 'policyTagNameForm', POLICY_TAG_NAME_FORM_DRAFT: 'policyTagNameFormDraft', + WORKSPACE_NEW_TAX_FORM: 'workspaceNewTaxForm', + WORKSPACE_NEW_TAX_FORM_DRAFT: 'workspaceNewTaxFormDraft', }, } as const; @@ -422,6 +426,7 @@ type OnyxFormValuesMapping = { [ONYXKEYS.FORMS.WORKSPACE_CATEGORY_FORM]: FormTypes.WorkspaceCategoryForm; [ONYXKEYS.FORMS.WORKSPACE_TAG_CREATE_FORM]: FormTypes.WorkspaceTagCreateForm; [ONYXKEYS.FORMS.WORKSPACE_RATE_AND_UNIT_FORM]: FormTypes.WorkspaceRateAndUnitForm; + [ONYXKEYS.FORMS.WORKSPACE_TAX_CUSTOM_NAME]: FormTypes.WorkspaceTaxCustomName; [ONYXKEYS.FORMS.CLOSE_ACCOUNT_FORM]: FormTypes.CloseAccountForm; [ONYXKEYS.FORMS.PROFILE_SETTINGS_FORM]: FormTypes.ProfileSettingsForm; [ONYXKEYS.FORMS.DISPLAY_NAME_FORM]: FormTypes.DisplayNameForm; @@ -458,6 +463,7 @@ type OnyxFormValuesMapping = { [ONYXKEYS.FORMS.PERSONAL_BANK_ACCOUNT]: FormTypes.PersonalBankAccountForm; [ONYXKEYS.FORMS.WORKSPACE_DESCRIPTION_FORM]: FormTypes.WorkspaceDescriptionForm; [ONYXKEYS.FORMS.POLICY_TAG_NAME_FORM]: FormTypes.PolicyTagNameForm; + [ONYXKEYS.FORMS.WORKSPACE_NEW_TAX_FORM]: FormTypes.WorkspaceNewTaxForm; [ONYXKEYS.FORMS.POLICY_CREATE_DISTANCE_RATE_FORM]: FormTypes.PolicyCreateDistanceRateForm; }; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 7dc6785f444f..5769b60a8284 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -584,6 +584,22 @@ const ROUTES = { route: 'settings/workspaces/:policyID/taxes', getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes` as const, }, + WORKSPACE_TAXES_SETTINGS: { + route: 'settings/workspaces/:policyID/taxes/settings', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes/settings` as const, + }, + WORKSPACE_TAXES_SETTINGS_WORKSPACE_CURRENCY_DEFAULT: { + route: 'settings/workspaces/:policyID/taxes/settings/workspace-currency', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes/settings/workspace-currency` as const, + }, + WORKSPACE_TAXES_SETTINGS_FOREIGN_CURRENCY_DEFAULT: { + route: 'settings/workspaces/:policyID/taxes/settings/foreign-currency', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes/settings/foreign-currency` as const, + }, + WORKSPACE_TAXES_SETTINGS_CUSTOM_TAX_NAME: { + route: 'settings/workspaces/:policyID/taxes/settings/tax-name', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes/settings/tax-name` as const, + }, WORKSPACE_MEMBER_DETAILS: { route: 'settings/workspaces/:policyID/members/:accountID', getRoute: (policyID: string, accountID: number, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/members/${accountID}`, backTo), @@ -592,6 +608,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/members/:accountID/role-selection', getRoute: (policyID: string, accountID: number, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/members/${accountID}/role-selection`, backTo), }, + WORKSPACE_TAX_CREATE: { + route: 'settings/workspaces/:policyID/taxes/new', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes/new` as const, + }, WORKSPACE_DISTANCE_RATES: { route: 'settings/workspaces/:policyID/distance-rates', getRoute: (policyID: string) => `settings/workspaces/${policyID}/distance-rates` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 34c7212e7f31..2fbd122f9972 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -216,6 +216,11 @@ const SCREENS = { TAGS_SETTINGS: 'Tags_Settings', TAGS_EDIT: 'Tags_Edit', TAXES: 'Workspace_Taxes', + TAXES_SETTINGS: 'Workspace_Taxes_Settings', + TAXES_SETTINGS_CUSTOM_TAX_NAME: 'Workspace_Taxes_Settings_CustomTaxName', + TAXES_SETTINGS_WORKSPACE_CURRENCY_DEFAULT: 'Workspace_Taxes_Settings_WorkspaceCurrency', + TAXES_SETTINGS_FOREIGN_CURRENCY_DEFAULT: 'Workspace_Taxes_Settings_ForeignCurrency', + TAX_CREATE: 'Workspace_Tax_Create', TAG_CREATE: 'Tag_Create', TAG_SETTINGS: 'Tag_Settings', CURRENCY: 'Workspace_Profile_Currency', diff --git a/src/components/AmountForm.tsx b/src/components/AmountForm.tsx index 8ae8f0674012..48035dd884bd 100644 --- a/src/components/AmountForm.tsx +++ b/src/components/AmountForm.tsx @@ -12,8 +12,9 @@ import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; import CONST from '@src/CONST'; import BigNumberPad from './BigNumberPad'; import FormHelpMessage from './FormHelpMessage'; -import type {BaseTextInputRef} from './TextInput/BaseTextInput/types'; +import type {BaseTextInputProps, BaseTextInputRef} from './TextInput/BaseTextInput/types'; import TextInputWithCurrencySymbol from './TextInputWithCurrencySymbol'; +import type TextInputWithCurrencySymbolProps from './TextInputWithCurrencySymbol/types'; type AmountFormProps = { /** Amount supplied by the FormProvider */ @@ -36,7 +37,8 @@ type AmountFormProps = { /** Whether the currency symbol is pressable */ isCurrencyPressable?: boolean; -}; +} & Pick & + Pick; /** * Returns the new selection object based on the updated amount's length @@ -51,7 +53,7 @@ const NUM_PAD_CONTAINER_VIEW_ID = 'numPadContainerView'; const NUM_PAD_VIEW_ID = 'numPadView'; function AmountForm( - {value: amount, currency = CONST.CURRENCY.USD, extraDecimals = 0, errorText, onInputChange, onCurrencyButtonPress, isCurrencyPressable = true}: AmountFormProps, + {value: amount, currency = CONST.CURRENCY.USD, extraDecimals = 0, errorText, onInputChange, onCurrencyButtonPress, isCurrencyPressable = true, ...rest}: AmountFormProps, forwardedRef: ForwardedRef, ) { const styles = useThemeStyles(); @@ -214,6 +216,8 @@ function AmountForm( }} onKeyPress={textInputKeyPress} isCurrencyPressable={isCurrencyPressable} + // eslint-disable-next-line react/jsx-props-no-spreading + {...rest} /> {!!errorText && ( + + + + + + +