diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 3e54975433f6..cdb95bd66779 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -5,7 +5,7 @@ on: pull_request: types: [opened, synchronize] branches-ignore: [staging, production] - paths: ['**.ts', '**.tsx', 'package.json', 'package-lock.json', 'tsconfig.json'] + paths: ['**.js', '**.ts', '**.tsx', 'package.json', 'package-lock.json', 'tsconfig.json'] jobs: typecheck: @@ -20,3 +20,12 @@ jobs: run: npm run typecheck env: CI: true + + - name: Check for new JavaScript files + run: | + git fetch origin main --no-tags --depth=1 + count_new_js=$(git diff --name-only --diff-filter=A origin/main HEAD -- 'src/libs/*.js' 'src/hooks/*.js' 'src/styles/*.js' 'src/languages/*.js' | wc -l) + if [ "$count_new_js" -gt "0" ]; then + echo "ERROR: Found new JavaScript files in the /src/libs, /src/hooks, /src/styles, or /src/languages directories; use TypeScript instead." + exit 1 + fi diff --git a/android/app/build.gradle b/android/app/build.gradle index af7f43adad0d..6827448c5053 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001039900 - versionName "1.3.99-0" + versionCode 1001040000 + versionName "1.4.0-0" } flavorDimensions "default" diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Connect-Company-Cards.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Connect-Company-Cards.md index 112c3b9617c9..f2ff837d7638 100644 --- a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Connect-Company-Cards.md +++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Connect-Company-Cards.md @@ -1,5 +1,91 @@ --- -title: Connect Company Cards -description: Connect Company Cards +title: Company-Card-Settings.md +description: Company card settings --- -## Resource Coming Soon! +# Overview +Once you’ve imported your company cards via commercial card feed, direct bank feed, or CSV import, the next step is to configure the cards’ settings. + +As a Domain Admin, you can access the company card settings by navigating to Settings > Domains> Domain Name > Company Card > Settings. + +If you cannot access Domains, you will need to request Domain Admin access my the Domain Admin. + +# How to configure company card settings +You can manage company cards and set and adjust the settings from the Domains page by navigating to Settings > Domains > [Domain name] > Settings + +## Reimbursable preference + +You can control how your employees' company card expenses are flagged for reimbursement: + +Force Yes: All expenses will be marked as reimbursable, and employees cannot change this setting. +Force No: All expenses will be marked as non-reimbursable, and employees cannot change this setting. +Do Not Force: Expenses will default to either reimbursable or non-reimbursable (your choice), but employees can adjust if necessary. + +## Liability type + +Choose the liability type that suits your needs: + +Corporate Liability: Users cannot delete company card expenses. +Personal Liability: Users are allowed to delete company card expenses. + +If you update the settings on an existing company card feed, the changes will apply to expenses imported after the date the setting is saved. The update will not affect previously imported expenses. + +## Preferred policy + +Setting a preferred policy for a company card feed will ensure that the imported transactions are added to a report on the policy you set. This setting is useful when members are on multiple policies and need to ensure their company card expenses are reported to a particular policy. + +# How to use Scheduled Submit with company cards +All expenses must be placed on a report if they need to be approved; with Scheduled Submit, you no longer need to worry about the arduous task of employees creating their expenses, adding them to a report, and submitting them manually. All they need to do is SmartScan their receipts and Concierge will take care of the rest, on a variety of schedules that you can set according to your preferences! + +Concierge won't automatically submit expenses on reports that have Expense Violations. Instead, these expenses will be moved to a new report, creating an additional report for the current reporting period. + +An employee can add comments in the Expense Comment field or at the bottom of the report to clarify any details. + +## Enable Scheduled Submit +Scheduled Submit is enabled in the Group Policy by navigating to Settings > Policies > Group > Policy Name > Reports > Scheduled Submit +Use the toggle to enable Scheduled Submit +Choose your desired frequency + +If Scheduled Submit is disabled on the group policy level (or set to a manual frequency), and you have noticed expense reports are still automatically submitted to the group policy, it's likely Scheduled Submit is enabled on the user’s Individual Policy settings. + +# How to connect company cards to an accounting integration + +If you're using a connected accounting system such as NetSuite, Xero, Intacct, Quickbooks Desktop, or QuickBooks Online, you can also connect the card to export to a specific credit card GL account. First, connect the card itself, and once completed, follow the steps below: +Go to Settings > Domains > Domain name > Company Cards +Click Edit Exports on the right-hand side of the card table and select the GL account you want to export expenses to +You're all done. After the account is set, exported expenses will be mapped to the specific account selected when exported by a Domain Admin. + +# How to export company card expenses to a connected accounting integration + +## Pooled GL account + +To export credit card expenses to a pooled GL account: +Go to Settings > Policies > Group > Policy Name > Connections > Accounting Integrations > Configure +Select Credit Card / Charge Card / Bank Transaction as your Non-reimbursable export option. +Please review the Export Settings page for exporting Expense Reports to NetSuite +Select the Vendor/liability account you want to export all non-reimbursable expenses to. + +## Individual GL account + +Go to Settings > Domain > Domain name > Company Cards +Click the Export Settings cog to the right-hand side of the card and select the GL account you want to export expenses to. +You're all done! After the account is set, exported expenses will be mapped to the specific account selected. + +# Deep Dive +## Identifying company card transactions +When you link your credit cards to Expensify, the transactions will appear in each user's account on the Expenses page as soon as they're posted. You can identify transactions from centrally managed cards by seeing the locked card icon next to them. That icon indicates that they’re company card expenses: +[add image here] + +## Importing historical transactions + +After a card is connected via direct connection or via Approved! banks, Expensify will import 30-90 days' worth of historical transactions to your account (the timeframe is based on your bank's discretion). Any historical expenses beyond that date range can be imported using the CSV spreadsheet import method. + +## Using eReceipts +Expensify eReceipts serve as digital substitutes for paper receipts in your purchase transactions, eliminating the necessity to retain physical receipts or utilize SmartScanning receipts. In the case of Expensify Card transactions, eReceipts are automatically generated for all amounts. For other card programs, eReceipts are specifically generated for purchases amounting to $75 or less, provided the transactions are in USD. +To ensure seamless automatic importation, it's essential to maintain your transactions in US Dollars. Additionally, eReceipts can be directly imported from your bank account. Please be aware that CSV/OFX imported files of bank transactions do not support eReceipts. +It's important to note that eReceipts are not generated for lodging expenses. Moreover, due to incomplete or inaccurate category information from certain banks, there may be instances of invalid eReceipts being generated for hotel purchases. If you choose to re-categorize expenses, a similar situation may arise. It's crucial to remember that our Expensify eReceipt Guarantee excludes coverage for hotel and motel expenses. + +# FAQ +## What plan/subscription is required in order to manage corporate cards? +Group Policy (Collect or Control plan only) +## When do my company card transactions import to Expensify? +Credit card transactions are imported to Expensify once they’re posted to the bank account. This usually takes 1-3 business days between the point of purchase and when the transactions populate in your account. diff --git a/docs/articles/expensify-classic/expense-and-report-features/The-Expenses-Page.md b/docs/articles/expensify-classic/expense-and-report-features/The-Expenses-Page.md index 42a8a914e5bc..5431355dd790 100644 --- a/docs/articles/expensify-classic/expense-and-report-features/The-Expenses-Page.md +++ b/docs/articles/expensify-classic/expense-and-report-features/The-Expenses-Page.md @@ -66,7 +66,7 @@ A Workspace admin can see Processing, Approved, and Reimbursed expenses as long If employees submit expense reports on a workspace where you are not an admin, you will not have visibility into those expenses. Additionally, if an expense is left unreported, a workspace admin will not be able to see that expense until it’s been added to a report. A Workspace admin can edit the tags and categories on an expense, but if they want to edit the amount, date, or merchant name, the expense will need to be in a Processing state or rejected back to the submitter for changes. -We have more about company card expense reconciliation in this support article. +We have more about company card expense reconciliation in this [support article](https://help.expensify.com/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/Reconciliation). ## Can I edit multiple expenses at once? Yes! Select the expenses you want to edit and click **Edit Multiple**. diff --git a/docs/articles/new-expensify/getting-started/Expensify-Lounge.md b/docs/articles/new-expensify/getting-started/Expensify-Lounge.md deleted file mode 100644 index bdccbe927769..000000000000 --- a/docs/articles/new-expensify/getting-started/Expensify-Lounge.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: Welcome to the Expensify Lounge! -description: How to get the most out of the Expensify Lounge. -redirect_from: articles/other/Expensify-Lounge/ ---- - - -# What is the Expensify Lounge? -The Expensify Lounge is a place where people go to Get Shit Done. It's a beautiful environment with great coffee and a group of people to collaborate with. Check out this guide on how to best utilize the Expensify Lounge! - -# The Two Rules -### Rule #1 - Get Shit Done - -The Lounge is a space for people to get work done. It is optimized to be the perfect environment for you to focus on your work, collaborate with others, and advance your most wild and creative ideas. To make this a reality, we ask our members to keep the following in mind: - -- **#focus** - Use the space for how it was designed and do not distract from others' focus. The space is beautiful, social, and collaborative, but it was created to help our members work effectively. -- **#urgency** - Working remotely is great, but there's nothing like real-time collaboration with your colleagues. Use the lounge to meet with co-workers IRL to continue the progress on whatever it is you're working on. -- **#results** - Don't mistake time for effort or effort for output. Upon arrival, visualize what you want to accomplish, and don't leave until it's done. - -## Rule #2 - Don’t Ruin it for Everyone Else - -We want this place to be incredible, innovative, and always elvoving. To achieve that, we have some general guidelines: - -- **#writeitdown** - If you can help others learn from you, do so. Write a blog post, a document, or a post in Expensify Chat to share with others. This includes making the Expensify Lounge a better space. Feel free to write down any improvements so we can make it better. -- **#showup** - If you are in the lounge, be fully present. Meet others, and collaborate in social rooms. The point is to build a community of people who are focused on getting shit done; you’ll get out what you put in. -- **#oneteam** - Providing an inclusive community is our priority, and we do not tolerate any form of discrimination. Aim to go out of your way to include people who want to be included. -- **#nocreeps** - Do not make people feel uncomfortable with your words or actions. If you are made to feel uncomfortable or notice this happening to someone else, you can use the escalation process outlined in the FAQ section. - -# How to Use the Expensify Lounge -Keeping those two rules in mind, below is a guide on how our members can get the most out of the lounge. - -### Rule #1 - Getting Shit Done -- **Order drinks from Concierge** - [Write Concierge here](https://new.expensify.com/concierge) to ask lounge questions or order beverages. Concierge will bring your order directly to you! -- **Using an office** - Offices are first come, first serve. If an office is open, feel free to use it! Please keep office use to under an hour. We currently do not allow reserving offices. -- **Lounge hours** - The lounge will be open from 8am-6pm PT, Monday through Friday and closed on some major holidays. You can review our Google Maps profile to check our holiday hours. -- **Make the lounge better** - Make any suggestions to improve the lounge experience in [#announce - Expensify Lounge](https://new.expensify.com/r/8292963527436014). - -## Rule #2 - Not Ruining it for Everyone Else -- **Offices are for calls** - Please do not occupy an office unless you have a call or collaborative meeting happening, and don't stay in an office for longer than an hour. -- **Respect other people** - Please do not be too loud or distracting while others are trying to work. While collaborating in Expensify Chat, be respectful of others’ viewpoints and keep a positive environment. -- **Stay home if you’re sick** - If you feel sick, please do not visit the lounge, or consider wearing a mask in public areas. -- **If you see something, say something** - If you are made to feel uncomfortable or witness others being made uncomfortable, let Concierge know. If this is happening in Expensify Chat, use our moderation tools (outlined below in the FAQ) to apply the applicable level of moderation. - -We’re so happy you are here to live rich, have fun, and save the world with us. Now, go enjoy the Expensify Lounge, and let's Get Shit Done! - -# FAQs - -#### What is Concierge? - -Concierge is our automated system that answers member questions in real-time. Questions regarding the local lounge will be routed directly to the lounge's Concierge. You can send Concierge a message if you have a drink request or general questions. They’ll take care of everything for you! - -#### Who is invited to the Expensify Lounge? - -Everyone is invited to the Expensify Lounge! Whether you're an existing customer, or you're someone looking for a great space to Get Shit Done, we'd love to have you. - -#### How do I escalate something that's making me or someone else uncomfortable? - -If you see something in Expensify Chat that should be escalated, you can use the escalation feature to mark a chat as: -- **Spam or Inconsiderate**: This will send a whisper to the sender of the message warning them of the violation, and the message will have a flag applied to it which will be visible to all users. Concierge will not review these flags. -- **Intimidating or Bullying**: The message will be immediately hidden, and the content will be reviewed by our team. After reviewing the message, and it's confirmed intimidation or bullying, the message will be permanently hidden and we'll communicate the violation to the sender of the message. -- **Harassment or Assault**: The message will be immediately hidden and reviewed by our team. The user will be sent a message to warning them of the violation, and Concierge can block the user if that's deemed necessary. - -If you witness something in-person, please write to Concierge referencing which lounge you are in, and they will escalate the issue appropriately. - -#### Where are other Expensify Lounge locations? - -Right now, we only have the San Francisco Lounge, but be on the lookout for more coming soon! diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index d40d36701731..0de3f7cb2671 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3.99 + 1.4.0 CFBundleSignature ???? CFBundleURLTypes @@ -40,7 +40,7 @@ CFBundleVersion - 1.3.99.0 + 1.4.0.0 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 4e3ca3ebce6d..cd8b9bd630c8 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.3.99 + 1.4.0 CFBundleSignature ???? CFBundleVersion - 1.3.99.0 + 1.4.0.0 diff --git a/package-lock.json b/package-lock.json index b34cde433719..fae3dbb10ed7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.3.99-0", + "version": "1.4.0-0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.3.99-0", + "version": "1.4.0-0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index e68f10940d6a..b7e2f5ad8515 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.3.99-0", + "version": "1.4.0-0", "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.", diff --git a/src/components/DisplayNames/DisplayNamesTooltipItem.tsx b/src/components/DisplayNames/DisplayNamesTooltipItem.tsx index 8f215fefd71b..45cdb340cc98 100644 --- a/src/components/DisplayNames/DisplayNamesTooltipItem.tsx +++ b/src/components/DisplayNames/DisplayNamesTooltipItem.tsx @@ -8,7 +8,7 @@ import styles from '@styles/styles'; type DisplayNamesTooltipItemProps = { index?: number; - /** The full title of the DisplayNames component (not split up) */ + /** The function to get a distance to shift the tooltip horizontally */ getTooltipShiftX?: (index: number) => number | undefined; /** The Account ID for the tooltip */ diff --git a/src/components/withLocalize.js b/src/components/withLocalize.tsx similarity index 53% rename from src/components/withLocalize.js rename to src/components/withLocalize.tsx index 346d402829bd..040ec509135e 100755 --- a/src/components/withLocalize.js +++ b/src/components/withLocalize.tsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; -import React, {forwardRef} from 'react'; +import React, {ComponentType, ForwardedRef, forwardRef, ReactElement, RefAttributes} from 'react'; import getComponentDisplayName from '@libs/getComponentDisplayName'; -import {LocaleContext} from './LocaleContextProvider'; +import {LocaleContext, LocaleContextProps} from './LocaleContextProvider'; const withLocalizePropTypes = { /** Returns translated string for given locale and phrase */ @@ -30,24 +30,30 @@ const withLocalizePropTypes = { toLocaleDigit: PropTypes.func.isRequired, }; -export default function withLocalize(WrappedComponent) { - const WithLocalize = forwardRef((props, ref) => ( - - {(translateUtils) => ( - - )} - - )); +type WithLocalizeProps = LocaleContextProps; + +export default function withLocalize( + WrappedComponent: ComponentType>, +): (props: Omit & React.RefAttributes) => ReactElement | null { + function WithLocalize(props: Omit, ref: ForwardedRef) { + return ( + + {(translateUtils) => ( + + )} + + ); + } WithLocalize.displayName = `withLocalize(${getComponentDisplayName(WrappedComponent)})`; - - return WithLocalize; + return forwardRef(WithLocalize); } export {withLocalizePropTypes}; +export type {WithLocalizeProps}; diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 973ed1f0dfd1..6a34611838c2 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -4196,6 +4196,15 @@ function getRoom(type, policyID) { const room = _.find(allReports, (report) => report && report.policyID === policyID && report.chatType === type && !isThread(report)); return room; } +/** + * We only want policy owners and admins to be able to modify the welcome message, but not in thread chat. + * @param {Object} report + * @param {Object} policy + * @return {Boolean} + */ +function shouldDisableWelcomeMessage(report, policy) { + return isMoneyRequestReport(report) || isArchivedRoom(report) || !isChatRoom(report) || isChatThread(report) || !PolicyUtils.isPolicyAdmin(policy); +} export { getReportParticipantsTitle, @@ -4358,4 +4367,5 @@ export { getReimbursementQueuedActionMessage, getPersonalDetailsForAccountID, getRoom, + shouldDisableWelcomeMessage, }; diff --git a/src/pages/ReportWelcomeMessagePage.js b/src/pages/ReportWelcomeMessagePage.js index c748e36c98e6..c9bd65a11318 100644 --- a/src/pages/ReportWelcomeMessagePage.js +++ b/src/pages/ReportWelcomeMessagePage.js @@ -13,7 +13,7 @@ import TextInput from '@components/TextInput'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; -import * as PolicyUtils from '@libs/PolicyUtils'; +import * as ReportUtils from '@libs/ReportUtils'; import updateMultilineInputRange from '@libs/UpdateMultilineInputRange'; import styles from '@styles/styles'; import * as Report from '@userActions/Report'; @@ -80,7 +80,7 @@ function ReportWelcomeMessagePage(props) { includeSafeAreaPaddingBottom={false} testID={ReportWelcomeMessagePage.displayName} > - + Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(props.report.reportID))} diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js index c89e08c8c7b3..cea9296055c7 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -62,9 +62,8 @@ function ReportSettingsPage(props) { const shouldDisableRename = useMemo(() => ReportUtils.shouldDisableRename(report, linkedWorkspace), [report, linkedWorkspace]); const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); - // We only want policy owners and admins to be able to modify the welcome message. - const shouldDisableWelcomeMessage = - isMoneyRequestReport || ReportUtils.isArchivedRoom(report) || !ReportUtils.isChatRoom(report) || _.isEmpty(linkedWorkspace) || linkedWorkspace.role !== CONST.POLICY.ROLE.ADMIN; + // We only want policy owners and admins to be able to modify the welcome message, but not in thread chat + const shouldDisableWelcomeMessage = ReportUtils.shouldDisableWelcomeMessage(report, linkedWorkspace); const shouldDisableSettings = _.isEmpty(report) || ReportUtils.isArchivedRoom(report); const shouldShowRoomName = !ReportUtils.isPolicyExpenseChat(report) && !ReportUtils.isChatThread(report);