Skip to content

Commit

Permalink
Merge branch 'main' into wave9/onboarding-flow-turn-on
Browse files Browse the repository at this point in the history
  • Loading branch information
filip-solecki committed Apr 21, 2024
2 parents c5d8bf6 + 8e8b173 commit c58d1a6
Show file tree
Hide file tree
Showing 87 changed files with 2,172 additions and 423 deletions.
64 changes: 64 additions & 0 deletions .github/scripts/detectRedirectCycle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {parse} from 'csv-parse';
import fs from 'fs';

const parser = parse();

const adjacencyList: Record<string, string[]> = {};
const visited: Map<string, boolean> = new Map<string, boolean>();
const backEdges: Map<string, boolean> = new Map<string, boolean>();

function addEdge(source: string, target: string) {
if (!adjacencyList[source]) {
adjacencyList[source] = [];
}
adjacencyList[source].push(target);
}

function isCyclic(currentNode: string): boolean {
visited.set(currentNode, true);
backEdges.set(currentNode, true);

// Do a depth first search for all the neighbours. If a node is found in backedge, a cycle is detected.
const neighbours = adjacencyList[currentNode];
if (neighbours) {
for (const node of neighbours) {
if (!visited.has(node)) {
if (isCyclic(node)) {
return true;
}
} else if (backEdges.has(node)) {
return true;
}
}
}

backEdges.delete(currentNode);

return false;
}

function detectCycle(): boolean {
for (const [node] of Object.entries(adjacencyList)) {
if (!visited.has(node)) {
if (isCyclic(node)) {
const cycle = Array.from(backEdges.keys());
console.log(`Infinite redirect found in the cycle: ${cycle.join(' -> ')} -> ${node}`);
return true;
}
}
}
return false;
}

fs.createReadStream(`${process.cwd()}/docs/redirects.csv`)
.pipe(parser)
.on('data', (row) => {
// Create a directed graph of sourceURL -> targetURL
addEdge(row[0], row[1]);
})
.on('end', () => {
if (detectCycle()) {
process.exit(1);
}
process.exit(0);
});
19 changes: 15 additions & 4 deletions .github/scripts/verifyRedirect.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,22 @@

declare -r REDIRECTS_FILE="docs/redirects.csv"

declare -r RED='\033[0;31m'
declare -r GREEN='\033[0;32m'
declare -r NC='\033[0m'

duplicates=$(awk -F, 'a[$1]++{print $1}' $REDIRECTS_FILE)
if [[ -n "$duplicates" ]]; then
echo "${RED}duplicate redirects are not allowed: $duplicates ${NC}"
exit 1
fi

if [[ -z "$duplicates" ]]; then
exit 0
npm run detectRedirectCycle
DETECT_CYCLE_EXIT_CODE=$?
if [[ DETECT_CYCLE_EXIT_CODE -eq 1 ]]; then
echo -e "${RED}The redirects.csv has a cycle. Please remove the redirect cycle because it will cause an infinite redirect loop ${NC}"
exit 1
fi

echo "duplicate redirects are not allowed: $duplicates"
exit 1
echo -e "${GREEN}The redirects.csv is valid!${NC}"
exit 0
4 changes: 2 additions & 2 deletions .github/workflows/deployExpensifyHelp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ jobs:

- name: Create docs routes file
run: ./.github/scripts/createDocsRoutes.sh
- name: Check duplicates in redirect.csv

- name: Check for duplicates and cycles in redirects.csv
run: ./.github/scripts/verifyRedirect.sh

- name: Build with Jekyll
Expand Down
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
versionCode 1001046307
versionName "1.4.63-7"
versionCode 1001046314
versionName "1.4.63-14"
// Supported language variants must be declared here to avoid from being removed during the compilation.
// This also helps us to not include unnecessary language variants in the APK.
resConfigs "en", "es"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
---
title: Enable and set up expense violations
description: Set up rules for expenses and enable violations
---
<div id="expensify-classic" markdown="1">

Expensify automatically detects expense errors or discrepancies as violations that must be corrected. You can also set rules for a workspace that will trigger a violation if the rule is not met. These rules can be set for categories, tags, and even for specific domain groups.

When reviewing submitted expense reports, approvers will see violations highlighted with an exclamation mark. There are two types of violations:
- **Yellow**: Automated highlights that require attention but may not require corrective action. For example, if a receipt was SmartScanned and then the amount was modified, a yellow violation will be added to call out the change for review.
- **Red**: Violations directly tied to your workspace settings. These violations must be addressed before the report can be submitted and reimbursed.

You can hover over the icon to see a brief description, and you can find more detailed information below the list of expenses.

{% include info.html %}
If your workspace has automations set to automatically submit reports for approval, the report that contains violations will not be submitted automatically until the violations are corrected. (However, if a comment is added to an expense, it will override the violation as the member is providing a reason for submission *unless* domain workspace rules are set to be strictly enforced, as detailed near the bottom of this article.)
{% include end-info.html %}

# Enable or disable expense violations

1. Hover over Settings, then click **Workspaces**.
2. Click the **Group** tab on the left.
3. Click the desired workspace name.
4. Click the **Expenses** tab on the left.
5. Click the “Enable violations” toggle.
6. If desired, enter the expense rules that will be used to create violations:
- **Max expense age (days)**: How old an expense can be
- **Max expense amount**: How much a single expense can cost
- **Receipt required amount**: How much a single expense can cost before a receipt is required

{% include info.html %}
Expensify includes certain system mandatory violations that can't be disabled, even if your policy has violations turned off.
{% include end-info.html %}

# Set category rules

Admins on a Control workspace can enable specific rules for each category, including setting expense caps for specific categories, requiring receipts, and more. These rules can allow you to have a default expense limit of $2,500 but to only allow a daily entertainment limit of $150 per person. You can also choose to not require receipts for mileage or per diem expenses.

To set up category rules,
1. Hover over Settings, then click **Workspaces**.
2. Click the **Group** tab on the left.
3. Click the desired workspace name.
4. Click the **Categories** tab on the left.
5. Click **Edit** to the right of the category.
6. Enter your category rules, as desired:
- **GL Code and Payroll Code**: You can add general ledger (GL) or payroll codes to the category for accounting. GL codes populate automatically if you have an accounting integration connected with Expensify.
- **Max Amount**: You can set specific expense caps for the expense category. Use the Limit Type dropdown to determine if the amount is set per individual expense or per day, then enter the maximum amount into this field.
- **Receipts**: You can determine whether receipts are required for the category. For example, many companies disable receipt requirements for toll expenses.
- **Description**: You can determine whether a description is required for expenses under this category.
- **Description Hint**: You can add a hint in the description field to prompt the expense creator on what they should enter into the description field for expenses under this category.
- **Approver**: You can set a specific approver for expenses labeled with this category.

If users are in violation of these rules, the violations will be shown in red on the report.

{% include info.html %}
If Scheduled Submit is enabled on a workspace, expenses with category violations will not be auto-submitted unless the expense has a comment added.
{% include end-info.html %}

# Make categories required

This means all expenses must be coded with a Category.

1. Hover over Settings, then click **Workspaces**.
2. Click the **Group** tab on the left.
3. Click the desired workspace name.
4. Click the **Categories** tab on the left.
5. Enable the “People must categorize expenses” toggle.

Each Workspace Member will now be required to select a category for their expense. If they do not select a category, the report will receive a violation, which can prevent submission if Scheduled Submit is enabled.

# Make tags required

1. Hover over Settings, then click **Workspaces**.
2. Click the **Group** tab on the left.
3. Click the desired workspace name.
4. Click the **Tags** tab on the left.
5. Enable the “People must tag expenses” toggle.

Each Workspace Member will now be required to select a tag for their expense before they’re able to submit it.

# Require strict compliance by domain group

You can require strict compliance to require members of a specific domain group to submit reports that meet **all** workspace rules before they can submit their expense report—even if they add a note. Every rule and regulation on the workspace must be met before a report can be submitted.

{% include info.html %}
This will prevent members from submitting any reports where a manager has granted them a manual exception for any of the workspace rules.
{% include end-info.html %}

To enable strict domain group compliance for reports,

1. Hover over Settings, then click **Domains**.
2. Click the **Groups** tab on the left.
3. Click **Edit** to the right of the desired workspace name.
4. Enable the “Strictly enforce expense workspace rules” toggle.

# FAQs

**Why can’t my employees see the categories on their expenses?**

The employee may have their default workspace set as their personal workspace. Look under the details section on top right of the report to ensure it is being reported under the correct workspace.

**Will the account numbers from our accounting system (QuickBooks Online, Sage Intacct, etc.) show in the category list for employees?**

The general ledger (GL) account numbers are visible only for Workspace Admins in the workspace settings when they are part of a control workspace. This information is not visible to other members of the workspace. However, if you wish to have this information available to your employees when they are categorizing their expenses, you can edit the account name in your accounting software to include the GL number (for example, Accounts Payable - 12345).

**What causes a category violation?**

- An expense is categorized with a category that is not included in the workspace's categories. This may happen if the employee creates an expense under the wrong workspace, which will cause a "category out of workspace" violation.
- If the workspace categories are being imported from an accounting integration and they’ve been updated in the accounting system but not in Expensify, this can cause an old category to still be in use on an open report which would throw a violation on submission. Simply reselect a proper category to clear violation.

</div>
4 changes: 3 additions & 1 deletion docs/redirects.csv
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ https://help.expensify.com/articles/new-expensify/payments/Request-Money,https:/
https://help.expensify.com/articles/expensify-classic/workspace-and-domain-settings/tax-tracking,https://help.expensify.com/articles/expensify-classic/expenses/expenses/Apply-Tax
https://help.expensify.com/articles/expensify-classic/copilots-and-delegates/User-Roles.html,https://help.expensify.com/expensify-classic/hubs/copilots-and-delegates/
https://help.expensify.com/articles/expensify-classic/expensify-billing/Billing-Owner,https://help.expensify.com/articles/expensify-classic/workspaces/Assign-billing-owner-and-payment-account
https://help.expensify.com/articles/expensify-classic/expensify-billing/Billing-Owner.html,https://help.expensify.com/articles/expensify-classic/workspaces/Assign-billing-owner-and-payment-account
https://help.expensify.com/articles/expensify-classic/insights-and-custom-reporting/Other-Export-Options.html,https://help.expensify.com/articles/expensify-classic/spending-insights/Other-Export-Options
https://help.expensify.com/articles/expensify-classic/bank-accounts-and-credit-cards/business-bank-accounts/Business-Bank-Accounts-AUD,https://help.expensify.com/articles/expensify-classic/connect-credit-cards/Global-Reimbursements
https://help.expensify.com/expensify-classic/hubs/bank-accounts-and-credit-cards,https://help.expensify.com/expensify-classic/hubs/
Expand Down Expand Up @@ -123,7 +124,6 @@ https://help.expensify.com/articles/expensify-classic/billing-and-subscriptions/
https://help.expensify.com/articles/expensify-classic/billing-and-subscriptions/Tax-Exempt,https://help.expensify.com/articles/expensify-classic/expensify-billing/Tax-Exempt
https://help.expensify.com/articles/expensify-classic/copilots-and-delegates/Approving-Reports,https://help.expensify.com/expensify-classic/hubs/reports/
https://help.expensify.com/articles/expensify-classic/copilots-and-delegates/Invite-Members,https://help.expensify.com/articles/expensify-classic/workspaces/Invite-members-and-assign-roles
https://help.expensify.com/articles/expensify-classic/copilots-and-delegates/Removing-Members,https://help.expensify.com/articles/expensify-classic/copilots-and-delegates/Removing-Members
https://help.expensify.com/articles/expensify-classic/expense-and-report-features/Attendee-Tracking,https://help.expensify.com/articles/expensify-classic/expenses/Track-group-expenses
https://help.expensify.com/articles/expensify-classic/expense-and-report-features/Currency,https://help.expensify.com/articles/expensify-classic/workspaces/Currency
https://help.expensify.com/articles/expensify-classic/expense-and-report-features/Expense-Rules,https://help.expensify.com/articles/expensify-classic/expenses/Expense-Rules
Expand Down Expand Up @@ -158,3 +158,5 @@ https://help.expensify.com/articles/expensify-classic/workspaces/Budgets,https:/
https://help.expensify.com/articles/expensify-classic/workspaces/Categories,https://help.expensify.com/articles/expensify-classic/workspaces/Create-categories
https://help.expensify.com/articles/expensify-classic/workspaces/Tags,https://help.expensify.com/articles/expensify-classic/workspaces/Create-tags
https://help.expensify.com/expensify-classic/hubs/manage-employees-and-report-approvals,https://help.expensify.com/articles/expensify-classic/copilots-and-delegates/Approval-Workflows
https://help.expensify.com/articles/expensify-classic/reports/Report-Audit-Log-and-Comments,https://help.expensify.com/articles/expensify-classic/reports/Print-or-download-a-report
https://help.expensify.com/articles/expensify-classic/reports/The-Reports-Page,https://help.expensify.com/articles/expensify-classic/reports/Report-statuses
2 changes: 1 addition & 1 deletion docs/sitemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ layout:
{% assign pages = site.html_pages | where_exp:'doc','doc.sitemap != false' | where_exp:'doc','doc.url != "/404.html"' %}
{% for page in pages %}
<url>
<loc>{{ page.url | replace:'/index.html','/' | absolute_url | xml_escape }}</loc>
<loc>{{ page.url | replace:'/index.html','/' | absolute_url | xml_escape | replace:'.html','' }}</loc>
</url>
{% endfor %}
</urlset>
2 changes: 1 addition & 1 deletion ios/NewExpensify/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>1.4.63.7</string>
<string>1.4.63.14</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
Expand Down
2 changes: 1 addition & 1 deletion ios/NewExpensifyTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.4.63.7</string>
<string>1.4.63.14</string>
</dict>
</plist>
2 changes: 1 addition & 1 deletion ios/NotificationServiceExtension/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<key>CFBundleShortVersionString</key>
<string>1.4.63</string>
<key>CFBundleVersion</key>
<string>1.4.63.7</string>
<string>1.4.63.14</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
Expand Down
11 changes: 9 additions & 2 deletions package-lock.json

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

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
"version": "1.4.63-7",
"version": "1.4.63-14",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
Expand Down Expand Up @@ -28,6 +28,7 @@
"desktop-build": "scripts/build-desktop.sh production",
"desktop-build-staging": "scripts/build-desktop.sh staging",
"createDocsRoutes": "ts-node .github/scripts/createDocsRoutes.ts",
"detectRedirectCycle": "ts-node .github/scripts/detectRedirectCycle.ts",
"desktop-build-adhoc": "scripts/build-desktop.sh adhoc",
"ios-build": "fastlane ios build",
"android-build": "fastlane android build",
Expand Down Expand Up @@ -252,6 +253,7 @@
"concurrently": "^8.2.2",
"copy-webpack-plugin": "^10.1.0",
"css-loader": "^6.7.2",
"csv-parse": "^5.5.5",
"diff-so-fancy": "^1.3.0",
"dotenv": "^16.0.3",
"electron": "^29.2.0",
Expand Down
23 changes: 23 additions & 0 deletions patches/react-native+0.73.4+014+fix-inverted-flatlist.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
diff --git a/node_modules/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewShadowNode.cpp b/node_modules/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewShadowNode.cpp
index a8ecce5..6ad790e 100644
--- a/node_modules/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewShadowNode.cpp
+++ b/node_modules/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewShadowNode.cpp
@@ -66,7 +66,17 @@ void ScrollViewShadowNode::layout(LayoutContext layoutContext) {
Point ScrollViewShadowNode::getContentOriginOffset() const {
auto stateData = getStateData();
auto contentOffset = stateData.contentOffset;
- return {-contentOffset.x, -contentOffset.y + stateData.scrollAwayPaddingTop};
+ auto props = getConcreteProps();
+
+ float productX = 1.0f;
+ float productY = 1.0f;
+
+ for (const auto& operation : props.transform.operations) {
+ productX *= operation.x;
+ productY *= operation.y;
+ }
+
+ return {-contentOffset.x * productX, (-contentOffset.y + stateData.scrollAwayPaddingTop) * productY};
}

} // namespace facebook::react
12 changes: 12 additions & 0 deletions patches/react-native-screens+3.30.1+001+fix-screen-type.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
diff --git a/node_modules/react-native-screens/src/components/Screen.tsx b/node_modules/react-native-screens/src/components/Screen.tsx
index 3f9a1cb..45767f7 100644
--- a/node_modules/react-native-screens/src/components/Screen.tsx
+++ b/node_modules/react-native-screens/src/components/Screen.tsx
@@ -79,6 +79,7 @@ export class InnerScreen extends React.Component<ScreenProps> {
// Due to how Yoga resolves layout, we need to have different components for modal nad non-modal screens
const AnimatedScreen =
Platform.OS === 'android' ||
+ stackPresentation === undefined ||
stackPresentation === 'push' ||
stackPresentation === 'containedModal' ||
stackPresentation === 'containedTransparentModal'
Loading

0 comments on commit c58d1a6

Please sign in to comment.