Skip to content

Commit

Permalink
Merge branch 'main' into jasper-editDistanceRequests
Browse files Browse the repository at this point in the history
  • Loading branch information
tgolen committed Sep 27, 2023
2 parents 0869c9d + d7ad305 commit ad21933
Show file tree
Hide file tree
Showing 66 changed files with 1,043 additions and 680 deletions.
4 changes: 4 additions & 0 deletions .well-known/apple-app-site-association
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@
"/": "/workspace/*",
"comment": "Workspace Details"
},
{
"/": "/get-assistance/*",
"comment": "Get Assistance Pages"
},
{
"/": "/teachersunite/*",
"comment": "Teachers Unite!"
Expand Down
4 changes: 2 additions & 2 deletions docs/404.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
permalink: /404.html
---
<div class="centered-content with-lhn">
<img class="icon" src="/assets/images/hourglass.svg" />
<div class="centered-content">
<img class="icon" src="/assets/images/circle-hourglass.svg" />
<strong>Hmm it's not here...</strong>
<div>That page is nowhere to be found.</div>
</div>
29 changes: 6 additions & 23 deletions docs/_sass/_main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -641,30 +641,13 @@ button {
}

.centered-content {
height: 240px;
width: 100%;
height: calc(100vh - 56px);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
font-size: larger;
position: absolute;
top: calc((100vh - 240px) / 2);

width: 380px;
right: calc((100vw - 380px) / 2);
@include breakpoint($breakpoint-tablet) {
width: 500px;
right: calc((100vw - 500px) / 2);
}

&.with-lhn {
right: calc((100vw - 380px) / 2);

@include breakpoint($breakpoint-tablet) {
right: calc((100vw - 320px - 500px ) / 2);
}

@include breakpoint($breakpoint-desktop) {
right: calc((100vw - 420px - 500px) / 2);
}
}

div {
margin-top: 8px;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,124 @@
---
title: Create Expenses
description: Create Expenses
title: Create-Expenses.md
description: This is an article that shows you all the ways that you can create Expenses in Expensify!
---
## Resource Coming Soon!
<!-- The lines above are required by Jekyll to process the .md file -->

# 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 [email protected] 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 "Open") 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 an Open 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 [email protected] 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. **Unreported**: Unreported expenses are not yet part of a report (and therefore unsubmitted) and are not viewable by anyone but the expense creator/owner.
2. **Open**: Open expenses are on a report that's still in progress, and are unsubmitted. Your Policy Admin will be able to view them, making it 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:
**Red**: These indicate violations directly tied to your report's Policy settings. They are clear rule violations that must be addressed before submission.
**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.
You can continue adding more attendees or save the Expense.
To remove an attendee from an expense:
Open the expense.
Click or tap the **Attendees** field to display the list of attendees.
From the list, de-select the attendees you'd like to remove from the expense.

4 changes: 4 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,10 @@ const CONST = {
GOLD: 'GOLD',
SILVER: 'SILVER',
},
WEB_MESSAGE_TYPE: {
STATEMENT: 'STATEMENT_NAVIGATE',
CONCIERGE: 'CONCIERGE_NAVIGATE',
},
},

PLAID: {
Expand Down
1 change: 0 additions & 1 deletion src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,6 @@ export default {
IOU_SEND_ENABLE_PAYMENTS: 'send/new/enable-payments',

NEW_TASK: 'new/task',
NEW_TASK_WITH_REPORT_ID: 'new/task/:reportID?',
NEW_TASK_ASSIGNEE: 'new/task/assignee',
NEW_TASK_SHARE_DESTINATION: 'new/task/share-destination',
NEW_TASK_DETAILS: 'new/task/details',
Expand Down
4 changes: 3 additions & 1 deletion src/components/AttachmentModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import useWindowDimensions from '../hooks/useWindowDimensions';
import Navigation from '../libs/Navigation/Navigation';
import ROUTES from '../ROUTES';
import useNativeDriver from '../libs/useNativeDriver';
import useNetwork from '../hooks/useNetwork';

/**
* Modal render prop component that exposes modal launching triggers that can be used
Expand Down Expand Up @@ -121,6 +122,7 @@ function AttachmentModal(props) {
: undefined,
);
const {translate} = useLocalize();
const {isOffline} = useNetwork();

const onCarouselAttachmentChange = props.onCarouselAttachmentChange;

Expand Down Expand Up @@ -350,7 +352,7 @@ function AttachmentModal(props) {
<HeaderWithBackButton
title={props.headerTitle || translate(isAttachmentReceipt ? 'common.receipt' : 'common.attachment')}
shouldShowBorderBottom
shouldShowDownloadButton={props.allowDownload && shouldShowDownloadButton && !isAttachmentReceipt}
shouldShowDownloadButton={props.allowDownload && shouldShowDownloadButton && !isAttachmentReceipt && !isOffline}
onDownloadButtonPress={() => downloadAttachment(source)}
shouldShowCloseButton={!props.isSmallScreenWidth}
shouldShowBackButton={props.isSmallScreenWidth}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function BaseAutoCompleteSuggestions(props) {
});

const innerHeight = CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT * props.suggestions.length;
const animatedStyles = useAnimatedStyle(() => StyleUtils.getAutoCompleteSuggestionContainerStyle(rowHeight.value, props.shouldIncludeReportRecipientLocalTimeHeight));
const animatedStyles = useAnimatedStyle(() => StyleUtils.getAutoCompleteSuggestionContainerStyle(rowHeight.value));

useEffect(() => {
rowHeight.value = withTiming(measureHeightOfSuggestionRows(props.suggestions.length, props.isSuggestionPickerLarge), {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ const propTypes = {
* When this value is false, the suggester will have a height of 2.5 items. When this value is true, the height can be up to 5 items. */
isSuggestionPickerLarge: PropTypes.bool.isRequired,

/** Show that we should include ReportRecipientLocalTime view height */
shouldIncludeReportRecipientLocalTimeHeight: PropTypes.bool.isRequired,

/** create accessibility label for each item */
accessibilityLabelExtractor: PropTypes.func.isRequired,

Expand Down
17 changes: 8 additions & 9 deletions src/components/AutoCompleteSuggestions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import useWindowDimensions from '../../hooks/useWindowDimensions';
* On the native platform, tapping on auto-complete suggestions will not blur the main input.
*/

function AutoCompleteSuggestions({parentContainerRef, ...props}) {
function AutoCompleteSuggestions({measureParentContainer, ...props}) {
const containerRef = React.useRef(null);
const {windowHeight, windowWidth} = useWindowDimensions();
const [{width, left, bottom}, setContainerState] = React.useState({
Expand All @@ -37,11 +37,11 @@ function AutoCompleteSuggestions({parentContainerRef, ...props}) {
}, []);

React.useEffect(() => {
if (!parentContainerRef || !parentContainerRef.current) {
if (!measureParentContainer) {
return;
}
parentContainerRef.current.measureInWindow((x, y, w) => setContainerState({left: x, bottom: windowHeight - y, width: w}));
}, [parentContainerRef, windowHeight, windowWidth]);
measureParentContainer((x, y, w) => setContainerState({left: x, bottom: windowHeight - y, width: w}));
}, [measureParentContainer, windowHeight, windowWidth]);

const componentToRender = (
<BaseAutoCompleteSuggestions
Expand All @@ -51,11 +51,10 @@ function AutoCompleteSuggestions({parentContainerRef, ...props}) {
/>
);

if (!width) {
return componentToRender;
}

return ReactDOM.createPortal(<View style={StyleUtils.getBaseAutoCompleteSuggestionContainerStyle({left, width, bottom})}>{componentToRender}</View>, document.querySelector('body'));
return (
Boolean(width) &&
ReactDOM.createPortal(<View style={StyleUtils.getBaseAutoCompleteSuggestionContainerStyle({left, width, bottom})}>{componentToRender}</View>, document.querySelector('body'))
);
}

AutoCompleteSuggestions.propTypes = propTypes;
Expand Down
11 changes: 8 additions & 3 deletions src/components/AutoCompleteSuggestions/index.native.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import React from 'react';
import {Portal} from '@gorhom/portal';
import BaseAutoCompleteSuggestions from './BaseAutoCompleteSuggestions';
import {propTypes} from './autoCompleteSuggestionsPropTypes';

function AutoCompleteSuggestions({parentContainerRef, ...props}) {
// eslint-disable-next-line react/jsx-props-no-spreading
return <BaseAutoCompleteSuggestions {...props} />;
function AutoCompleteSuggestions({measureParentContainer, ...props}) {
return (
<Portal hostName="suggestions">
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<BaseAutoCompleteSuggestions {...props} />
</Portal>
);
}

AutoCompleteSuggestions.propTypes = propTypes;
Expand Down
4 changes: 0 additions & 4 deletions src/components/EmojiSuggestions.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ const propTypes = {
* 2.5 items. When this value is true, the height can be up to 5 items. */
isEmojiPickerLarge: PropTypes.bool.isRequired,

/** Show that we should include ReportRecipientLocalTime view height */
shouldIncludeReportRecipientLocalTimeHeight: PropTypes.bool.isRequired,

/** Stores user's preferred skin tone */
preferredSkinToneIndex: PropTypes.number.isRequired,

Expand Down Expand Up @@ -102,7 +99,6 @@ function EmojiSuggestions(props) {
highlightedSuggestionIndex={props.highlightedEmojiIndex}
onSelect={props.onSelect}
isSuggestionPickerLarge={props.isEmojiPickerLarge}
shouldIncludeReportRecipientLocalTimeHeight={props.shouldIncludeReportRecipientLocalTimeHeight}
accessibilityLabelExtractor={keyExtractor}
measureParentContainer={props.measureParentContainer}
/>
Expand Down
1 change: 1 addition & 0 deletions src/components/LHNOptionsList/OptionRowLHN.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ function OptionRowLHN(props) {
]}
accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON}
accessibilityLabel={translate('accessibilityHints.navigatesToChat')}
needsOffscreenAlphaCompositing={props.optionItem.icons.length >= 2}
>
<View style={sidebarInnerRowStyle}>
<View style={[styles.flexRow, styles.alignItemsCenter]}>
Expand Down
Loading

0 comments on commit ad21933

Please sign in to comment.