diff --git a/android/app/build.gradle b/android/app/build.gradle
index bcac489f6828..afe24fc37700 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -90,8 +90,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001037402
- versionName "1.3.74-2"
+ versionCode 1001037403
+ versionName "1.3.74-3"
}
flavorDimensions "default"
diff --git a/docs/articles/expensify-classic/get-paid-back/expenses/Merge-Expenses.md b/docs/articles/expensify-classic/get-paid-back/expenses/Merge-Expenses.md
index e7705a32f215..bfbc0773768c 100644
--- a/docs/articles/expensify-classic/get-paid-back/expenses/Merge-Expenses.md
+++ b/docs/articles/expensify-classic/get-paid-back/expenses/Merge-Expenses.md
@@ -1,5 +1,64 @@
---
title: Merge Expenses
-description: Merge Expenses
+description: This article shows you all the ways that you can merge your expenses in Expensify!
---
-## Resource Coming Soon!
+
+
+# About
+The merge expense function helps combine two separate expenses into one. This is useful when the same expense has been accidentally entered more than once, or if you have a connected credit card and an imported expense didn’t automatically merge with a manual entry.
+
+# How-to merge expenses
+It’s important to note that merging expenses doesn't add the two values together. Instead, merging them combines both expenses to create a single, consolidated expense.
+
+Keep in mind:
+1. Merging expenses cannot be undone.
+2. You can only merge two expenses at a time.
+3. You can merge a cash expense with a credit card expense, or two cash expenses - but not two credit card expenses.
+4. In order to merge, both expenses will need to be in an Open or Unreported state.
+
+# How to merge expenses on the web app
+To merge two expenses from the Expenses page:
+1. Sign into your Expensify account.
+2. Navigate to the Expenses page on the left-hand navigation.
+3. Click the checkboxes next to the two expenses you wish to merge.
+4. Click **Merge**.
+5. You'll be able to choose which aspect of each of the two expenses you would like to be used on the resulting expense, such as the receipt image, card, merchant, category, and more.
+
+To merge two expenses from the Reports page:
+1. Sign into your Expensify account.
+2. Navigate to the Reports page on the left-hand navigation.
+3. Click the Report that contains the expenses that you wish to merge.
+4. Click on the **Details** tab, then the Pencil icon.
+5. Select the two expenses that you wish to merge.
+6. You'll be able to choose which aspect of each of the two expenses you would like to be used on the resulting expense, such as the receipt image, card, merchant, category, and more.
+
+# How to merge expenses on the Expensify mobile app
+On the mobile app, merging is prompted when you see the message _"Potential duplicate expense detected"_. Simply tap **Resolve Now** to take a closer look, then hit **Merge Expense**, and you're done!
+
+If the expenses exist on two different reports, you will be asked which report you'd like the newly created single expense to be reported onto.
+
+# FAQ
+
+## Can you merge expenses across different reports?
+
+You cannot merge expenses across different reports. Expenses will only merge if they are on the same report. If you have expenses across different reports that you wish to merge, you’ll need to move both expenses onto the same report (and ensure they are in the Open status) in order to merge them.
+
+## Can you merge expenses across different accounts?
+
+You cannot merge expenses across two separate accounts. You will need to choose one submitter and transfer the expense information to that user's account in order to merge the expense.
+## Can you merge expenses with different currencies?
+
+Yes, you can merge expenses with different currencies. The conversion amount will be based on the daily exchange rate for the date of the transaction, as long as the converted rates are within +/- 5%. If the currencies are the same, then the amounts must be an exact match to merge.
+
+## Can Expensify automatically merge a cash expense with a credit card expense?
+
+Yes, Expensify can merge a cash expense with a credit card expense. A receipt will need to be SmartScanned via the app or forwarded to [receipts@expensify.com](mailto:receipts@expensify.com) in order to merge with a card expense. Note that the SmartScan must be fully completed and not stopped or edited, otherwise the two won’t merge.
+
+## It doesn’t look like my cash and card expenses merged properly. What are some troubleshooting tips?
+First, check the expense types - you can only merge a SmartScanned receipt (which will initially show with a cash icon) with a card transaction imported from a bank or via CSV.
+
+If the card expense in your Expensify account is older than the receipt you're trying to merge it with, they won't merge, and if the receipt is dated more than 7 days prior to the card expense, then they also will not merge.
+
+If you have any expenses that are more than 90 days old from the date they were incurred (not the date they were imported to Expensify), Expensify will not automatically merge them. This safeguard helps prevent the merging of very old expenses that might not align with recent transactions or receipts.
+
+Lastly, transactions imported with the Expensify API (via the Expense Importer) will not automatically merge with SmartScanned transactions.
diff --git a/docs/articles/expensify-classic/get-paid-back/reports/Create-A-Report.md b/docs/articles/expensify-classic/get-paid-back/reports/Create-A-Report.md
index fb4f756b2820..ea808695e7cd 100644
--- a/docs/articles/expensify-classic/get-paid-back/reports/Create-A-Report.md
+++ b/docs/articles/expensify-classic/get-paid-back/reports/Create-A-Report.md
@@ -1,5 +1,166 @@
---
title: Create a Report
-description: Create a Report
+description: Learn how to create and edit reports in Expensify
---
-## Resource Coming Soon!
+
+
+# Overview
+
+This article covers all the basics of creating, editing, deleting and managing your reports.
+
+# How to create a report
+
+_Using the web app:_
+
+To create a report on the Expensify website, click the New Report button on the **Reports** page.
+
+_Using the mobile app:_
+
+Tap the ☰ icon.
+Tap **Reports**.
+Tap the **+** icon.
+Choose your desired report type.
+
+# How to edit a report
+
+## Adding expenses to a report
+
+You can add expenses to the report by clicking **Add Expenses** at the top of the report.
+
+## Removing expenses from a report on the Expensify web app
+
+To remove expenses from the report on the web app, click the red ❌ next to the expense.
+
+## Removing expenses from a report on the Expensify mobile app
+
+To remove an expense on an Android device, hold the expense and tap **Delete**.
+
+To remove an expense on an iOS device, swipe the expense to the left and tap **Delete**.
+
+## Editing the report title
+
+To edit the report title, click the pencil icon next to the name. To save your changes, tap the enter key on your keyboard.
+
+**Note:** You may be unable to edit your reports' titles based on the settings.
+
+## Bulk-editing expenses on a report
+
+Click Details in the top-right of the report on the web app, then click the pencil icon to bring up the editing modal. You can click the pencil icon to the left of an expense to edit it, or you can edit multiple expenses at once by ticking the checkbox of the expenses you’d like to bulk-edit and then clicking **Edit Multiple** at the top of the modal.
+
+## Commenting on the report
+
+You can comment on the report by adding your comment to the **Report Comments** section at the bottom. Expensify will also log report actions here.
+
+## Attachments
+
+If you’d like to attach a photo or document to the report, follow the instructions below to add the attachment to your report comment section.
+
+_Using the web app:_
+
+1. Click the **Paperclip** icon in the comment box of the **Report Comments** section.
+2. Select the file to attach.
+3. Check the preview of the attachment and click Upload.
+
+_Using the mobile app:_
+
+1. Tap into the report.
+2. Scroll to the bottom of the report and tap the paper clip icon to attach a file.
+
+**Note:** Report comments support jpeg, jpg, png, gif, csv, and pdf files.
+
+## Changing the report's workspace
+
+To change the report's workspace, click **Details** in the top-right of the report on the web app, then select the correct workspace from the **Workspace** drop-down.
+
+## Changing the report type (Expense Report/Invoice)
+
+To change the report type, click **Details** in the top-right of the report on the web app, then select the correct report type from the **Type** drop-down.
+
+## Changing the layout of the report
+
+There are three ways you can change the report layout under the Details section of the report. To do this, select the desired layout from the relevant drop-down menu:
+
+ - **View** - Choose between a Basic or Detailed report view.
+ - **Group By** - Choose to group expenses on the report based on their Category or Tag.
+ - **Split By** - Split out the expenses based on their Reimbursable or Billable status.
+
+# How to submit a report
+
+1. Click **Submit** in the top-left of the report (or **Submit Report** at the top in the mobile app).
+2. Verify the approver and click **Submit** again.
+
+# How to retract your report (Undo Submit)
+
+As long as the report is still in a Processing state, you can retract this submission to put the report back to Draft status to make corrections and re-submit.
+
+To retract a **Processing** report on the web app, click the Undo Submit button at the upper left-hand corner of the report.
+
+To complete this from the mobile app, simply open the report from within your app and click the **Retract** button at the top of the report.
+
+# How to share a report
+
+Click Details in the top-right of the report on the web app to bring up the sharing settings. The following options are available:
+
+ - Click the **Printer** icon to print the report.
+ - Click the **Download** icon to download a PDF of the report
+ - Click the **Share** icon to share the report via email or SMS.
+
+# How to close a report
+
+You can close your report if you don't need it approved by your employer.
+
+_To close a report on the Expensify website:_
+
+1. Navigate to the report in question.
+2. Click **Mark as Closed** at the top of the report.
+3. You can re-open a report once it’s closed by clicking **Undo Close** at the top of the report.
+
+# How to delete a report
+
+_Deleting a report on the web app:_
+
+Click Details in the top-right of the report on the web app, then click the Trash icon to delete the report. Any expenses on the report will move to an Unreported state.
+
+_Deleting a report on the mobile app:_
+
+To delete a Draft report on an Android, press and hold the report name and tap **Delete**.
+
+To delete a Draft report on an iOS device, go to the **Reports** screen, swipe the report to the left, and tap **Delete**.
+
+_Deleting a report in the Processing, Approved, Reimbursed or Closed state:_
+
+If you want to delete a Processing or Closed report, please follow the How to undo your report submission instructions in this article to move the report back into an Draft status, then follow the steps above.
+
+If you want to delete an Approved or Reimbursed report, please speak to your Company Admin as this may not be possible.
+
+# How to move expenses between reports
+
+Navigate to your Expenses page.
+Tick the checkbox next to each expense you'd like to move.
+Click the Add To Report button in the top right corner.
+Select your desired report from the drop-down.
+
+# How to use Guided Review to clean up your report
+
+Open your report on the web app and click Review at the top. The system will walk you through each violation on the report.
+As you go through each violation, click View to look at the expense in more detail or resolve any violations.
+Click Next to move on to the next item.
+Click Finish to complete the review process when you’re done.
+
+# FAQ
+
+## Is there a difference between Expense Reports, Bills, and Invoices?
+
+**Expense Reports** are submitted by an employee to their employer. They contain either personally incurred expenses that the employee should be reimbursed for, or non-reimbursable expenses (such as company card expenses) incurred by the employee that require tracking for accounting purposes.
+
+**Invoices** are reports that a business or contractor will send to another business to charge them for goods or services the business received. Each invoice will have a matching **Bill** owned by the recipient so they may use it to pay the invoice sender.
+
+## Which report type should I use?
+
+If you bought something on a company card or need to be reimbursed by your employer, you’ll need an **Expense Report**.
+
+If someone external to the business sends you an invoice for their services, you’ll want a **Bill** (or even better - use our Bill Pay process)
+
+## When should I submit my report?
+
+Your Company Admin can answer this one, and they may have configured the workspace’s [Scheduled Submit] setting to enforce a regular cadence for you. If not, you can still set this up under your [Individual workspace].
diff --git a/docs/articles/expensify-classic/get-paid-back/reports/Reimbursements.md b/docs/articles/expensify-classic/get-paid-back/reports/Reimbursements.md
index c2cc25b32373..a31c0a582fd7 100644
--- a/docs/articles/expensify-classic/get-paid-back/reports/Reimbursements.md
+++ b/docs/articles/expensify-classic/get-paid-back/reports/Reimbursements.md
@@ -1,5 +1,42 @@
----
-title: Reimbursements
-description: Reimbursements
----
-## Resource Coming Soon!
+# Overview
+
+If you want to know more about how and when you’ll be reimbursed through Expensify, we’ve answered your questions below.
+
+# How to Get Reimbursed
+
+To get paid back after submitting a report for reimbursement, you’ll want to be sure to connect your bank account. You can do that under **Settings** > **Account** > **Payments** > **Add a Deposit Account**. Once your employer has approved your report, the reimbursement will be paid into the account you added.
+
+# Deep Dive
+
+## Reimbursement Timing
+
+### US Bank Accounts
+
+If your company uses Expensify's ACH reimbursement we'll first check to see if the report is eligible for Rapid Reimbursement (next business day). For a report to be eligible for Rapid Reimbursement it must fall under two limits:
+
+ - $100 per deposit bank account per day or less for the individuals being reimbursed or businesses receiving payments for bills.
+ - Less than $10,000 being disbursed in a 24-hour time period from the verified bank account being used to pay the reimbursement.
+
+If the request passes both checks, then you can expect to see funds deposited into your bank account on the next business day.
+
+If either limit has been reached, then you can expect to see funds deposited within your bank account within the typical ACH timeframe of 3-5 business days.
+
+### International Bank Accounts
+
+If receiving reimbursement to an international deposit account via Global Reimbursement, you should expect to see funds deposited in your bank account within 4 business days.
+
+## Bank Processing Timeframes
+
+Banks only process transactions and ACH activity on weekdays that are not bank holidays. These are considered business days. Additionally, the business day on which a transaction will be processed depends upon whether or not a request is created before or after the cutoff time, which is typically 3 pm PST.
+For example, if your reimbursement is initiated at 4 pm on Wednesday, this is past the bank's cutoff time, and it will not begin processing until the next business day.
+If that same reimbursement starts processing on Thursday, and it's estimated to take 3-5 business days, this will cover a weekend, and both days are not considered business days. So, assuming there are no bank holidays added into this mix, here is how that reimbursement timeline would play out:
+
+**Wednesday**: Reimbursement initiated after 3 pm PST; will be processed the next business day by your company’s bank.
+**Thursday**: Your company's bank will begin processing the withdrawal request
+**Friday**: Business day 1
+**Saturday**: Weekend
+**Sunday**: Weekend
+**Monday**: Business day 2
+**Tuesday**: Business day 3
+**Wednesday**: Business day 4
+**Thursday**: Business day 5
diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/Tax.md b/docs/articles/expensify-classic/policy-and-domain-settings/Tax.md
deleted file mode 100644
index 3ee1c8656b4b..000000000000
--- a/docs/articles/expensify-classic/policy-and-domain-settings/Tax.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Coming Soon
-description: Coming Soon
----
-## Resource Coming Soon!
diff --git a/docs/articles/expensify-classic/policy-and-domain-settings/tax-tracking.md b/docs/articles/expensify-classic/policy-and-domain-settings/tax-tracking.md
new file mode 100644
index 000000000000..7b859c5101b1
--- /dev/null
+++ b/docs/articles/expensify-classic/policy-and-domain-settings/tax-tracking.md
@@ -0,0 +1,19 @@
+---
+title: Tax
+description: How to track expense taxes
+---
+# Overview
+Expensify’s tax tracking feature allows you to:
+- Add tax names, rates, and codes whether you’re connected to an accounting system or not.
+- Enable/disable taxes you’d like to make available to users.
+- Set a default tax for Workspace currency expenses and, optionally, another default tax (including exempt) for foreign currency expenses which - will automatically apply to all new expenses.
+
+# How to Enable Tax Tracking
+Tax tracking can be enabled in the Tax section of the Workspace settings of any Workspace, whether group or individual.
+## If Connected to an Accounting Integration
+If your group Workspace is connected to Xero, QuickBooks Online, Sage Intacct, or NetSuite, make sure to first enable tax via the connection configuration page (Settings > Policies > Group > [Workspace Name] > Connections > Configure) and then sync the connection. Your tax rates will be imported from the accounting system and indicated by its logo.
+## Not Connected to an Accounting Integration
+If your Workspace is not connected to an accounting system, go to Settings > Policies > Group > [Workspace Name] > Tax to enable tax.
+
+# Tracking Tax by Expense Category
+To set a different tax rate for a specific expense type in the Workspace currency, go to Settings > Workspaces > Group > [Workspace Name] > Categories page. Click "Edit Rules" next to the desired category and set the "Category default tax". This will be applied to new expenses, overriding the default Workspace currency tax rate.
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index f41740a8bcb2..73e22053eda1 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -40,7 +40,7 @@
CFBundleVersion
- 1.3.74.2
+ 1.3.74.3ITSAppUsesNonExemptEncryptionLSApplicationQueriesSchemes
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index 95714ea2cc9f..5e7f02699579 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -19,6 +19,6 @@
CFBundleSignature????CFBundleVersion
- 1.3.74.2
+ 1.3.74.3
diff --git a/package-lock.json b/package-lock.json
index 64ee3cf6308f..8c63ba6ce9b3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "1.3.74-2",
+ "version": "1.3.74-3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "1.3.74-2",
+ "version": "1.3.74-3",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -90,7 +90,7 @@
"react-native-linear-gradient": "^2.8.1",
"react-native-localize": "^2.2.6",
"react-native-modal": "^13.0.0",
- "react-native-onyx": "1.0.87",
+ "react-native-onyx": "1.0.89",
"react-native-pager-view": "^6.2.0",
"react-native-pdf": "^6.7.1",
"react-native-performance": "^5.1.0",
@@ -41204,9 +41204,9 @@
}
},
"node_modules/react-native-onyx": {
- "version": "1.0.87",
- "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-1.0.87.tgz",
- "integrity": "sha512-6mIhobSwpClDDGnJm9XEdjnpEdWfFesJ18J8Ifsb4tL6AVi+uxos5bnlZcOoMbtlUk3UozrgSyTjMfFrkD/aZA==",
+ "version": "1.0.89",
+ "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-1.0.89.tgz",
+ "integrity": "sha512-bSC8YwVbMBJYm6BMtuhuYmZi6zMh13e1t8Kaxp7K5EDLcSoTWsWPkuWX4wBvewlkLfw+HgB1IdgnXpa6+jS+ag==",
"dependencies": {
"ascii-table": "0.0.9",
"fast-equals": "^4.0.3",
@@ -77269,9 +77269,9 @@
}
},
"react-native-onyx": {
- "version": "1.0.87",
- "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-1.0.87.tgz",
- "integrity": "sha512-6mIhobSwpClDDGnJm9XEdjnpEdWfFesJ18J8Ifsb4tL6AVi+uxos5bnlZcOoMbtlUk3UozrgSyTjMfFrkD/aZA==",
+ "version": "1.0.89",
+ "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-1.0.89.tgz",
+ "integrity": "sha512-bSC8YwVbMBJYm6BMtuhuYmZi6zMh13e1t8Kaxp7K5EDLcSoTWsWPkuWX4wBvewlkLfw+HgB1IdgnXpa6+jS+ag==",
"requires": {
"ascii-table": "0.0.9",
"fast-equals": "^4.0.3",
diff --git a/package.json b/package.json
index cd93f718679e..d013caa1c402 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.3.74-2",
+ "version": "1.3.74-3",
"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.",
@@ -133,7 +133,7 @@
"react-native-linear-gradient": "^2.8.1",
"react-native-localize": "^2.2.6",
"react-native-modal": "^13.0.0",
- "react-native-onyx": "1.0.87",
+ "react-native-onyx": "1.0.89",
"react-native-pager-view": "^6.2.0",
"react-native-pdf": "^6.7.1",
"react-native-performance": "^5.1.0",
diff --git a/src/CONST.ts b/src/CONST.ts
index e487514f150e..dbe47c6ed1a7 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -1232,6 +1232,7 @@ const CONST = {
EMOJI_NAME: /:[\w+-]+:/g,
EMOJI_SUGGESTIONS: /:[a-zA-Z0-9_+-]{1,40}$/,
AFTER_FIRST_LINE_BREAK: /\n.*/g,
+ LINE_BREAK: /\n/g,
CODE_2FA: /^\d{6}$/,
ATTACHMENT_ID: /chat-attachments\/(\d+)/,
HAS_COLON_ONLY_AT_THE_BEGINNING: /^:[^:]+$/,
@@ -1369,6 +1370,7 @@ const CONST = {
MERCHANT: 'merchant',
CATEGORY: 'category',
RECEIPT: 'receipt',
+ DISTANCE: 'distance',
TAG: 'tag',
},
FOOTER: {
diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index 6649a33fe15e..d2b3031220f1 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -242,6 +242,7 @@ const ONYXKEYS = {
POLICY_RECENTLY_USED_TAGS: 'policyRecentlyUsedTags_',
WORKSPACE_INVITE_MEMBERS_DRAFT: 'workspaceInviteMembersDraft_',
REPORT: 'report_',
+ REPORT_METADATA: 'reportMetadata_',
REPORT_ACTIONS: 'reportActions_',
REPORT_ACTIONS_DRAFTS: 'reportActionsDrafts_',
REPORT_ACTIONS_REACTIONS: 'reportActionsReactions_',
@@ -380,6 +381,7 @@ type OnyxValues = {
[ONYXKEYS.COLLECTION.DEPRECATED_POLICY_MEMBER_LIST]: OnyxTypes.PolicyMember;
[ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT]: Record;
[ONYXKEYS.COLLECTION.REPORT]: OnyxTypes.Report;
+ [ONYXKEYS.COLLECTION.REPORT_METADATA]: OnyxTypes.ReportMetadata;
[ONYXKEYS.COLLECTION.REPORT_ACTIONS]: OnyxTypes.ReportAction;
[ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS]: string;
[ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS]: OnyxTypes.ReportActionReactions;
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 78d5f4d54888..00f3a4012664 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -5,19 +5,18 @@ import CONST from './CONST';
* This is a file containing constants for all of the routes we want to be able to go to
*/
-// prettier-ignore
export default {
HOME: '',
/** This is a utility route used to go to the user's concierge chat, or the sign-in page if the user's not authenticated */
CONCIERGE: 'concierge',
FLAG_COMMENT: {
route: 'flag/:reportID/:reportActionID',
- getRoute: (reportID: string, reportActionID: string) => `flag/${reportID}/${reportActionID}`
+ getRoute: (reportID: string, reportActionID: string) => `flag/${reportID}/${reportActionID}`,
},
SEARCH: 'search',
DETAILS: {
route: 'details',
- getRoute: (login: string) => `details?login=${encodeURIComponent(login)}`
+ getRoute: (login: string) => `details?login=${encodeURIComponent(login)}`,
},
PROFILE: {
route: 'a/:accountID',
@@ -31,7 +30,7 @@ export default {
VALIDATE_LOGIN: 'v/:accountID/:validateCode',
GET_ASSISTANCE: {
route: 'get-assistance/:taskID',
- getRoute: (taskID: string) => `get-assistance/${taskID}`
+ getRoute: (taskID: string) => `get-assistance/${taskID}`,
},
UNLINK_LOGIN: 'u/:accountID/:validateCode',
APPLE_SIGN_IN: 'sign-in-with-apple',
@@ -102,11 +101,11 @@ export default {
REPORT: 'r',
REPORT_WITH_ID: {
route: 'r/:reportID?/:reportActionID?',
- getRoute: (reportID: string) => `r/${reportID}`
+ getRoute: (reportID: string) => `r/${reportID}`,
},
EDIT_REQUEST: {
route: 'r/:threadReportID/edit/:field',
- getRoute: (threadReportID: string, field: ValueOf) => `r/${threadReportID}/edit/${field}`
+ getRoute: (threadReportID: string, field: ValueOf) => `r/${threadReportID}/edit/${field}`,
},
EDIT_CURRENCY_REQUEST: {
route: 'r/:threadReportID/edit/currency',
@@ -114,89 +113,89 @@ export default {
},
REPORT_WITH_ID_DETAILS_SHARE_CODE: {
route: 'r/:reportID/details/shareCode',
- getRoute: (reportID: string) => `r/${reportID}/details/shareCode`
+ getRoute: (reportID: string) => `r/${reportID}/details/shareCode`,
},
REPORT_ATTACHMENTS: {
route: 'r/:reportID/attachment',
- getRoute: (reportID: string, source: string) => `r/${reportID}/attachment?source=${encodeURI(source)}`
+ getRoute: (reportID: string, source: string) => `r/${reportID}/attachment?source=${encodeURI(source)}`,
},
REPORT_PARTICIPANTS: {
route: 'r/:reportID/participants',
- getRoute: (reportID: string) => `r/${reportID}/participants`
+ getRoute: (reportID: string) => `r/${reportID}/participants`,
},
REPORT_WITH_ID_DETAILS: {
route: 'r/:reportID/details',
- getRoute: (reportID: string) => `r/${reportID}/details`
+ getRoute: (reportID: string) => `r/${reportID}/details`,
},
REPORT_SETTINGS: {
route: 'r/:reportID/settings',
- getRoute: (reportID: string) => `r/${reportID}/settings`
+ getRoute: (reportID: string) => `r/${reportID}/settings`,
},
REPORT_SETTINGS_ROOM_NAME: {
route: 'r/:reportID/settings/room-name',
- getRoute: (reportID: string) => `r/${reportID}/settings/room-name`
+ getRoute: (reportID: string) => `r/${reportID}/settings/room-name`,
},
REPORT_SETTINGS_NOTIFICATION_PREFERENCES: {
route: 'r/:reportID/settings/notification-preferences',
- getRoute: (reportID: string) => `r/${reportID}/settings/notification-preferences`
+ getRoute: (reportID: string) => `r/${reportID}/settings/notification-preferences`,
},
REPORT_SETTINGS_WRITE_CAPABILITY: {
route: 'r/:reportID/settings/who-can-post',
- getRoute: (reportID: string) => `r/${reportID}/settings/who-can-post`
+ getRoute: (reportID: string) => `r/${reportID}/settings/who-can-post`,
},
REPORT_WELCOME_MESSAGE: {
route: 'r/:reportID/welcomeMessage',
- getRoute: (reportID: string) => `r/${reportID}/welcomeMessage`
+ getRoute: (reportID: string) => `r/${reportID}/welcomeMessage`,
},
SPLIT_BILL_DETAILS: {
route: 'r/:reportID/split/:reportActionID',
- getRoute: (reportID: string, reportActionID: string) => `r/${reportID}/split/${reportActionID}`
+ getRoute: (reportID: string, reportActionID: string) => `r/${reportID}/split/${reportActionID}`,
},
TASK_TITLE: {
route: 'r/:reportID/title',
- getRoute: (reportID: string) => `r/${reportID}/title`
+ getRoute: (reportID: string) => `r/${reportID}/title`,
},
TASK_DESCRIPTION: {
route: 'r/:reportID/description',
- getRoute: (reportID: string) => `r/${reportID}/description`
+ getRoute: (reportID: string) => `r/${reportID}/description`,
},
TASK_ASSIGNEE: {
route: 'r/:reportID/assignee',
- getRoute: (reportID: string) => `r/${reportID}/assignee`
+ getRoute: (reportID: string) => `r/${reportID}/assignee`,
},
PRIVATE_NOTES_VIEW: {
route: 'r/:reportID/notes/:accountID',
- getRoute: (reportID: string, accountID: string | number) => `r/${reportID}/notes/${accountID}`
+ getRoute: (reportID: string, accountID: string | number) => `r/${reportID}/notes/${accountID}`,
},
PRIVATE_NOTES_LIST: {
route: 'r/:reportID/notes',
- getRoute: (reportID: string) => `r/${reportID}/notes`
+ getRoute: (reportID: string) => `r/${reportID}/notes`,
},
PRIVATE_NOTES_EDIT: {
route: 'r/:reportID/notes/:accountID/edit',
- getRoute: (reportID: string, accountID: string | number) => `r/${reportID}/notes/${accountID}/edit`
+ getRoute: (reportID: string, accountID: string | number) => `r/${reportID}/notes/${accountID}/edit`,
},
// To see the available iouType, please refer to CONST.IOU.MONEY_REQUEST_TYPE
MONEY_REQUEST: {
route: ':iouType/new/:reportID?',
- getRoute: (iouType: string, reportID = '') => `${iouType}/new/${reportID}`
+ getRoute: (iouType: string, reportID = '') => `${iouType}/new/${reportID}`,
},
MONEY_REQUEST_AMOUNT: {
route: ':iouType/new/amount/:reportID?',
- getRoute: (iouType: string, reportID = '') => `${iouType}/new/amount/${reportID}`
+ getRoute: (iouType: string, reportID = '') => `${iouType}/new/amount/${reportID}`,
},
MONEY_REQUEST_PARTICIPANTS: {
route: ':iouType/new/participants/:reportID?',
- getRoute: (iouType: string, reportID = '') => `${iouType}/new/participants/${reportID}`
+ getRoute: (iouType: string, reportID = '') => `${iouType}/new/participants/${reportID}`,
},
MONEY_REQUEST_CONFIRMATION: {
route: ':iouType/new/confirmation/:reportID?',
- getRoute: (iouType: string, reportID = '') => `${iouType}/new/confirmation/${reportID}`
+ getRoute: (iouType: string, reportID = '') => `${iouType}/new/confirmation/${reportID}`,
},
MONEY_REQUEST_DATE: {
route: ':iouType/new/date/:reportID?',
- getRoute: (iouType: string, reportID = '') => `${iouType}/new/date/${reportID}`
+ getRoute: (iouType: string, reportID = '') => `${iouType}/new/date/${reportID}`,
},
MONEY_REQUEST_CURRENCY: {
route: ':iouType/new/currency/:reportID?',
@@ -204,35 +203,39 @@ export default {
},
MONEY_REQUEST_DESCRIPTION: {
route: ':iouType/new/description/:reportID?',
- getRoute: (iouType: string, reportID = '') => `${iouType}/new/description/${reportID}`
+ getRoute: (iouType: string, reportID = '') => `${iouType}/new/description/${reportID}`,
},
MONEY_REQUEST_CATEGORY: {
route: ':iouType/new/category/:reportID?',
- getRoute: (iouType: string, reportID = '') => `${iouType}/new/category/${reportID}`
+ getRoute: (iouType: string, reportID = '') => `${iouType}/new/category/${reportID}`,
},
MONEY_REQUEST_TAG: {
route: ':iouType/new/tag/:reportID?',
- getRoute: (iouType: string, reportID = '') => `${iouType}/new/tag/${reportID}`
+ getRoute: (iouType: string, reportID = '') => `${iouType}/new/tag/${reportID}`,
},
MONEY_REQUEST_MERCHANT: {
route: ':iouType/new/merchant/:reportID?',
- getRoute: (iouType: string, reportID = '') => `${iouType}/new/merchant/${reportID}`
+ getRoute: (iouType: string, reportID = '') => `${iouType}/new/merchant/${reportID}`,
},
MONEY_REQUEST_WAYPOINT: {
route: ':iouType/new/waypoint/:waypointIndex',
- getRoute: (iouType: string, waypointIndex: number) => `${iouType}/new/waypoint/${waypointIndex}`
+ getRoute: (iouType: string, waypointIndex: number) => `${iouType}/new/waypoint/${waypointIndex}`,
},
MONEY_REQUEST_RECEIPT: {
route: ':iouType/new/receipt/:reportID?',
- getRoute: (iouType: string, reportID = '') => `${iouType}/new/receipt/${reportID}`
+ getRoute: (iouType: string, reportID = '') => `${iouType}/new/receipt/${reportID}`,
},
- MONEY_REQUEST_ADDRESS: {
+ MONEY_REQUEST_DISTANCE: {
route: ':iouType/new/address/:reportID?',
- getRoute: (iouType: string, reportID = '') => `${iouType}/new/address/${reportID}`
+ getRoute: (iouType: string, reportID = '') => `${iouType}/new/address/${reportID}`,
+ },
+ MONEY_REQUEST_EDIT_WAYPOINT: {
+ route: 'r/:threadReportID/edit/distance/:transactionID/waypoint/:waypointIndex',
+ getRoute: (threadReportID: number, transactionID: string, waypointIndex: number) => `r/${threadReportID}/edit/distance/${transactionID}/waypoint/${waypointIndex}`,
},
MONEY_REQUEST_DISTANCE_TAB: {
route: ':iouType/new/:reportID?/distance',
- getRoute: (iouType: string, reportID = '') => `${iouType}/new/${reportID}/distance`
+ getRoute: (iouType: string, reportID = '') => `${iouType}/new/${reportID}/distance`,
},
MONEY_REQUEST_MANUAL_TAB: ':iouType/new/:reportID?/manual',
MONEY_REQUEST_SCAN_TAB: ':iouType/new/:reportID?/scan',
@@ -259,47 +262,47 @@ export default {
WORKSPACE_NEW_ROOM: 'workspace/new-room',
WORKSPACE_INITIAL: {
route: 'workspace/:policyID',
- getRoute: (policyID: string) => `workspace/${policyID}`
+ getRoute: (policyID: string) => `workspace/${policyID}`,
},
WORKSPACE_INVITE: {
route: 'workspace/:policyID/invite',
- getRoute: (policyID: string) => `workspace/${policyID}/invite`
+ getRoute: (policyID: string) => `workspace/${policyID}/invite`,
},
WORKSPACE_INVITE_MESSAGE: {
route: 'workspace/:policyID/invite-message',
- getRoute: (policyID: string) => `workspace/${policyID}/invite-message`
+ getRoute: (policyID: string) => `workspace/${policyID}/invite-message`,
},
WORKSPACE_SETTINGS: {
route: 'workspace/:policyID/settings',
- getRoute: (policyID: string) => `workspace/${policyID}/settings`
+ getRoute: (policyID: string) => `workspace/${policyID}/settings`,
},
WORKSPACE_CARD: {
route: 'workspace/:policyID/card',
- getRoute: (policyID: string) => `workspace/${policyID}/card`
+ getRoute: (policyID: string) => `workspace/${policyID}/card`,
},
WORKSPACE_REIMBURSE: {
route: 'workspace/:policyID/reimburse',
- getRoute: (policyID: string) => `workspace/${policyID}/reimburse`
+ getRoute: (policyID: string) => `workspace/${policyID}/reimburse`,
},
WORKSPACE_RATE_AND_UNIT: {
route: 'workspace/:policyID/rateandunit',
- getRoute: (policyID: string) => `workspace/${policyID}/rateandunit`
+ getRoute: (policyID: string) => `workspace/${policyID}/rateandunit`,
},
WORKSPACE_BILLS: {
route: 'workspace/:policyID/bills',
- getRoute: (policyID: string) => `workspace/${policyID}/bills`
+ getRoute: (policyID: string) => `workspace/${policyID}/bills`,
},
WORKSPACE_INVOICES: {
route: 'workspace/:policyID/invoices',
- getRoute: (policyID: string) => `workspace/${policyID}/invoices`
+ getRoute: (policyID: string) => `workspace/${policyID}/invoices`,
},
WORKSPACE_TRAVEL: {
route: 'workspace/:policyID/travel',
- getRoute: (policyID: string) => `workspace/${policyID}/travel`
+ getRoute: (policyID: string) => `workspace/${policyID}/travel`,
},
WORKSPACE_MEMBERS: {
route: 'workspace/:policyID/members',
- getRoute: (policyID: string) => `workspace/${policyID}/members`
+ getRoute: (policyID: string) => `workspace/${policyID}/members`,
},
// These are some on-off routes that will be removed once they're no longer needed (see GH issues for details)
diff --git a/src/components/DistanceRequest.js b/src/components/DistanceRequest.js
index 5e9b73f2eb3a..aa7878337f21 100644
--- a/src/components/DistanceRequest.js
+++ b/src/components/DistanceRequest.js
@@ -5,39 +5,31 @@ import lodashGet from 'lodash/get';
import lodashIsNil from 'lodash/isNil';
import PropTypes from 'prop-types';
import _ from 'underscore';
-
import CONST from '../CONST';
import ROUTES from '../ROUTES';
import ONYXKEYS from '../ONYXKEYS';
-
import styles from '../styles/styles';
import variables from '../styles/variables';
-import theme from '../styles/themes/default';
-
-import transactionPropTypes from './transactionPropTypes';
-
+import LinearGradient from './LinearGradient';
+import * as MapboxToken from '../libs/actions/MapboxToken';
import useNetwork from '../hooks/useNetwork';
-import usePrevious from '../hooks/usePrevious';
import useLocalize from '../hooks/useLocalize';
-
-import * as ErrorUtils from '../libs/ErrorUtils';
import Navigation from '../libs/Navigation/Navigation';
-import * as MapboxToken from '../libs/actions/MapboxToken';
+import reportPropTypes from '../pages/reportPropTypes';
+import DotIndicatorMessage from './DotIndicatorMessage';
+import * as ErrorUtils from '../libs/ErrorUtils';
+import usePrevious from '../hooks/usePrevious';
+import theme from '../styles/themes/default';
import * as Transaction from '../libs/actions/Transaction';
import * as TransactionUtils from '../libs/TransactionUtils';
import * as IOUUtils from '../libs/IOUUtils';
-
import Button from './Button';
import DistanceMapView from './DistanceMapView';
-import LinearGradient from './LinearGradient';
import * as Expensicons from './Icon/Expensicons';
import PendingMapView from './MapView/PendingMapView';
-import DotIndicatorMessage from './DotIndicatorMessage';
import MenuItemWithTopDescription from './MenuItemWithTopDescription';
-import {iouPropTypes} from '../pages/iou/propTypes';
-import reportPropTypes from '../pages/reportPropTypes';
-import * as IOU from '../libs/actions/IOU';
import * as StyleUtils from '../styles/StyleUtils';
+import transactionPropTypes from './transactionPropTypes';
import ScreenWrapper from './ScreenWrapper';
import FullPageNotFoundView from './BlockingViews/FullPageNotFoundView';
import HeaderWithBackButton from './HeaderWithBackButton';
@@ -46,18 +38,12 @@ const MAX_WAYPOINTS = 25;
const MAX_WAYPOINTS_TO_DISPLAY = 4;
const propTypes = {
- /** Holds data related to Money Request view state, rather than the underlying Money Request data. */
- iou: iouPropTypes,
-
- /** Type of money request (i.e. IOU) */
- iouType: PropTypes.oneOf(_.values(CONST.IOU.MONEY_REQUEST_TYPE)),
+ /** The transactionID of this request */
+ transactionID: PropTypes.string,
/** The report to which the distance request is associated */
report: reportPropTypes,
- /** The optimistic transaction for this request */
- transaction: transactionPropTypes,
-
/** Data about Mapbox token for calling Mapbox API */
mapboxAccessToken: PropTypes.shape({
/** Temporary token for Mapbox API */
@@ -67,6 +53,15 @@ const propTypes = {
expiration: PropTypes.string,
}),
+ /** Are we editing an existing distance request, or creating a new one? */
+ isEditingRequest: PropTypes.bool,
+
+ /** Called on submit of this page */
+ onSubmit: PropTypes.func.isRequired,
+
+ /* Onyx Props */
+ transaction: transactionPropTypes,
+
/** React Navigation route */
route: PropTypes.shape({
/** Params from the route */
@@ -81,16 +76,16 @@ const propTypes = {
};
const defaultProps = {
- iou: {},
- iouType: '',
+ transactionID: '',
report: {},
- transaction: {},
+ isEditingRequest: false,
mapboxAccessToken: {
token: '',
},
+ transaction: {},
};
-function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken, route}) {
+function DistanceRequest({transactionID, report, transaction, mapboxAccessToken, route, isEditingRequest, onSubmit}) {
const [shouldShowGradient, setShouldShowGradient] = useState(false);
const [scrollContainerHeight, setScrollContainerHeight] = useState(0);
const [scrollContentHeight, setScrollContentHeight] = useState(0);
@@ -99,6 +94,7 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken,
const isEditing = lodashGet(route, 'path', '').includes('address');
const reportID = lodashGet(report, 'reportID', '');
+ const iouType = lodashGet(route, 'params.iouType', '');
const waypoints = useMemo(() => lodashGet(transaction, 'comment.waypoints', {}), [transaction]);
const previousWaypoints = usePrevious(waypoints);
const numberOfWaypoints = _.size(waypoints);
@@ -107,6 +103,7 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken,
const lastWaypointIndex = numberOfWaypoints - 1;
const isLoadingRoute = lodashGet(transaction, 'comment.isLoading', false);
+ const isLoading = lodashGet(transaction, 'isLoading', false);
const hasRouteError = !!lodashGet(transaction, 'errorFields.route');
const hasRoute = TransactionUtils.hasRoute(transaction);
const validatedWaypoints = TransactionUtils.getValidWaypoints(waypoints);
@@ -159,12 +156,12 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken,
}, []);
useEffect(() => {
- if (!iou.transactionID || !_.isEmpty(waypoints)) {
+ if (!transactionID || !_.isEmpty(waypoints)) {
return;
}
// Create the initial start and stop waypoints
- Transaction.createInitialWaypoints(iou.transactionID);
- }, [iou.transactionID, waypoints]);
+ Transaction.createInitialWaypoints(transactionID);
+ }, [transactionID, waypoints]);
const updateGradientVisibility = (event = {}) => {
// If a waypoint extends past the bottom of the visible area show the gradient, else hide it.
@@ -176,8 +173,8 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken,
return;
}
- Transaction.getRoute(iou.transactionID, validatedWaypoints);
- }, [shouldFetchRoute, iou.transactionID, validatedWaypoints, isOffline]);
+ Transaction.getRoute(transactionID, validatedWaypoints);
+ }, [shouldFetchRoute, transactionID, validatedWaypoints, isOffline]);
useEffect(() => {
if (numberOfWaypoints <= numberOfPreviousWaypoints) {
@@ -192,13 +189,12 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken,
Navigation.goBack(isEditing ? ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID) : ROUTES.HOME);
};
- const navigateToNextPage = () => {
- if (isEditing) {
- Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID));
- return;
- }
-
- IOU.navigateToNextPage(iou, iouType, reportID, report);
+ /**
+ * Takes the user to the page for editing a specific waypoint
+ * @param {Number} index of the waypoint to edit
+ */
+ const navigateToWaypointEditPage = (index) => {
+ Navigation.navigate(isEditingRequest ? ROUTES.MONEY_REQUEST_EDIT_WAYPOINT.getRoute(report.reportID, transactionID, index) : ROUTES.MONEY_REQUEST_WAYPOINT.getRoute('request', index));
};
const content = (
@@ -237,7 +233,7 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken,
secondaryIcon={waypointIcon}
secondaryIconFill={theme.icon}
shouldShowRightIcon
- onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_WAYPOINT.getRoute('request', index))}
+ onPress={() => navigateToWaypointEditPage(index)}
key={key}
/>
);
@@ -261,10 +257,7 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken,