Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions assets/images/integrationicons/gusto-icon-square.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 33 additions & 1 deletion src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2704,6 +2704,14 @@ const CONST = {
},
},

GUSTO: {
APPROVAL_MODE: {
BASIC: 'basic',
MANAGER: 'manager',
CUSTOM: 'custom',
},
},

QUICKBOOKS_REIMBURSABLE_ACCOUNT_TYPE: {
VENDOR_BILL: 'bill',
CHECK: 'check',
Expand Down Expand Up @@ -3542,6 +3550,7 @@ const CONST = {
IS_TRAVEL_ENABLED: 'isTravelEnabled',
REQUIRE_COMPANY_CARDS_ENABLED: 'requireCompanyCardsEnabled',
IS_TIME_TRACKING_ENABLED: 'isTimeTrackingEnabled',
IS_HR_ENABLED: 'isHREnabled',
},
DEFAULT_CATEGORIES: {
ADVERTISING: 'Advertising',
Expand Down Expand Up @@ -3618,6 +3627,7 @@ const CONST = {
NETSUITE: 'netsuite',
SAGE_INTACCT: 'intacct',
CERTINIA: 'certinia',
GUSTO: 'gusto',
},
SUPPORTED_ONLY_ON_OLDDOT: {
FINANCIALFORCE: 'financialForce',
Expand All @@ -3632,13 +3642,15 @@ const CONST = {
SAGE_INTACCT: 'sage-intacct',
QBD: 'quickbooks-desktop',
CERTINIA: 'certinia',
GUSTO: 'gusto',
},
NAME_USER_FRIENDLY: {
netsuite: 'NetSuite',
quickbooksOnline: 'QuickBooks Online',
quickbooksDesktop: 'QuickBooks Desktop',
xero: 'Xero',
intacct: 'Sage Intacct',
gusto: 'Gusto',
financialForce: 'FinancialForce',
certinia: 'Certinia',
billCom: 'Bill.com',
Expand All @@ -3648,8 +3660,14 @@ const CONST = {
microsoftDynamics: 'Microsoft Dynamics',
other: 'Other',
},
get ACCOUNTING_CONNECTION_NAMES() {
return [this.NAME.QBO, this.NAME.QBD, this.NAME.XERO, this.NAME.NETSUITE, this.NAME.SAGE_INTACCT, this.NAME.CERTINIA] as const;
},
get HR_CONNECTION_NAMES() {
return [this.NAME.GUSTO] as const;
},
get EXPORTED_TO_INTEGRATION_DISPLAY_NAMES(): string[] {
return Object.values(this.NAME).map((name) => this.NAME_USER_FRIENDLY[name as keyof typeof this.NAME_USER_FRIENDLY]);
return this.ACCOUNTING_CONNECTION_NAMES.map((name) => this.NAME_USER_FRIENDLY[name as keyof typeof this.NAME_USER_FRIENDLY]);
},
CORPORATE: ['quickbooksDesktop', 'netsuite', 'intacct', 'oracle', 'sap', 'microsoftDynamics', 'other'],
AUTH_HELP_LINKS: {
Expand All @@ -3662,6 +3680,7 @@ const CONST = {
STARTING_IMPORT_QBO: 'startingImportQBO',
STARTING_IMPORT_XERO: 'startingImportXero',
STARTING_IMPORT_QBD: 'startingImportQBD',
STARTING_IMPORT_GUSTO: 'startingImportGusto',
QBO_IMPORT_MAIN: 'quickbooksOnlineImportMain',
QBO_IMPORT_CUSTOMERS: 'quickbooksOnlineImportCustomers',
QBO_IMPORT_EMPLOYEES: 'quickbooksOnlineImportEmployees',
Expand Down Expand Up @@ -3727,6 +3746,10 @@ const CONST = {
SAGE_INTACCT_SYNC_IMPORT_EMPLOYEES: 'intacctImportEmployees',
SAGE_INTACCT_SYNC_IMPORT_DIMENSIONS: 'intacctImportDimensions',
SAGE_INTACCT_SYNC_IMPORT_SYNC_REIMBURSED_REPORTS: 'intacctImportSyncBillPayments',
GUSTO_SYNC_LOAD_COMPANY: 'gustoSyncLoadCompany',
GUSTO_SYNC_IMPORT_EMPLOYEES: 'gustoSyncImportEmployees',
GUSTO_SYNC_BUILD_APPROVAL_CHAINS: 'gustoSyncBuildApprovalChains',
GUSTO_SYNC_FINALIZE: 'gustoSyncFinalize',
},
SYNC_STAGE_TIMEOUT_MINUTES: 20,
},
Expand Down Expand Up @@ -8218,6 +8241,14 @@ const CONST = {
description: 'workspace.upgrade.companyCards.description' as const,
icon: 'CompanyCard',
},
hr: {
id: 'hr' as const,
alias: 'hr',
name: 'HR' as const,
title: 'workspace.upgrade.hr.title' as const,
description: 'workspace.upgrade.hr.description' as const,
icon: 'Users',
},
rules: {
id: 'rules' as const,
alias: 'rules',
Expand Down Expand Up @@ -9113,6 +9144,7 @@ const CONST = {
MEMBERS: 'WorkspaceInitial-Members',
REPORTS: 'WorkspaceInitial-Reports',
ACCOUNTING: 'WorkspaceInitial-Accounting',
HR: 'WorkspaceInitial-HR',
RECEIPT_PARTNERS: 'WorkspaceInitial-ReceiptPartners',
CATEGORIES: 'WorkspaceInitial-Categories',
TAGS: 'WorkspaceInitial-Tags',
Expand Down
17 changes: 17 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2290,6 +2290,23 @@ const ROUTES = {
return `workspaces/${policyID}/more-features` as const;
},
},
WORKSPACE_HR: {
route: 'workspaces/:policyID/hr',
getRoute: (policyID: string | undefined) => {
if (!policyID) {
Log.warn('Invalid policyID is used to build the WORKSPACE_HR route');
}
return `workspaces/${policyID}/hr` as const;
},
},
WORKSPACE_HR_GUSTO_APPROVAL_MODE: {
route: 'workspaces/:policyID/hr/gusto/approval-mode',
getRoute: (policyID: string | undefined) => `workspaces/${policyID}/hr/gusto/approval-mode` as const,
},
WORKSPACE_HR_GUSTO_FINAL_APPROVER: {
route: 'workspaces/:policyID/hr/gusto/final-approver',
getRoute: (policyID: string | undefined) => `workspaces/${policyID}/hr/gusto/final-approver` as const,
},
WORKSPACE_TAGS: {
route: 'workspaces/:policyID/tags',
getRoute: (policyID: string | undefined) => {
Expand Down
3 changes: 3 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,9 @@ const SCREENS = {
},
INITIAL: 'Workspace_Initial',
PROFILE: 'Workspace_Overview',
HR: 'Workspace_HR',
HR_GUSTO_APPROVAL_MODE: 'Workspace_HR_Gusto_Approval_Mode',
HR_GUSTO_FINAL_APPROVER: 'Workspace_HR_Gusto_Final_Approver',
COMPANY_CARDS: 'Workspace_CompanyCards',
COMPANY_CARDS_BROKEN_CARD_FEED_CONNECTION: 'Workspace_CompanyCards_BrokenCardFeedConnection',
COMPANY_CARDS_REFRESH_CARD_FEED_CONNECTION: 'Workspace_CompanyCards_RefreshCardFeedConnection',
Expand Down
17 changes: 17 additions & 0 deletions src/components/ConnectToGustoFlow/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {useEffect} from 'react';
import useEnvironment from '@hooks/useEnvironment';
import {connectPolicyToGusto} from '@libs/actions/connections/Gusto';
import type {ConnectToGustoFlowProps} from './types';

function ConnectToGustoFlow({policyID}: ConnectToGustoFlowProps) {
const {environmentURL} = useEnvironment();

useEffect(() => {
connectPolicyToGusto(policyID, environmentURL);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return null;
}

export default ConnectToGustoFlow;
5 changes: 5 additions & 0 deletions src/components/ConnectToGustoFlow/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type ConnectToGustoFlowProps = {
policyID: string;
};

export type {ConnectToGustoFlowProps};

Check failure on line 5 in src/components/ConnectToGustoFlow/types.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Prefer default export on a file with single export

Check failure on line 5 in src/components/ConnectToGustoFlow/types.ts

View workflow job for this annotation

GitHub Actions / ESLint check

Prefer default export on a file with single export
87 changes: 87 additions & 0 deletions src/components/GustoSyncResultsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React from 'react';
import {View} from 'react-native';
import ConfirmModal from '@components/ConfirmModal';

Check warning on line 3 in src/components/GustoSyncResultsModal.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Unexpected subpath import via alias '@components/ConfirmModal'. Use './ConfirmModal' instead

Check warning on line 3 in src/components/GustoSyncResultsModal.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

Unexpected subpath import via alias '@components/ConfirmModal'. Use './ConfirmModal' instead
import Text from '@components/Text';

Check warning on line 4 in src/components/GustoSyncResultsModal.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Unexpected subpath import via alias '@components/Text'. Use './Text' instead

Check warning on line 4 in src/components/GustoSyncResultsModal.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

Unexpected subpath import via alias '@components/Text'. Use './Text' instead
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import type {GustoSyncResult} from '@libs/API/types';

type GustoSyncResultsModalProps = {
isVisible: boolean;
onClose: () => void;
result?: GustoSyncResult | null;
};

function GustoSyncResultsModal({isVisible, onClose, result}: GustoSyncResultsModalProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();

const getSkippedReasonLabel = (reason: string) => {
switch (reason) {
case 'missingManager':
return translate('workspace.hr.gusto.syncResults.skippedReasons.missingManager');
case 'missingFinalApprover':
return translate('workspace.hr.gusto.syncResults.skippedReasons.missingFinalApprover');
case 'alreadyExists':
return translate('workspace.hr.gusto.syncResults.skippedReasons.alreadyExists');
default:
return reason;
}
};

const renderUsers = (users?: Array<{email: string; displayName?: string}>) => {
if (!users?.length) {
return <Text style={[styles.textSupporting]}>{translate('workspace.hr.gusto.syncResults.empty')}</Text>;
}

return users.map((user) => (
<Text
key={user.email}
style={[styles.mt1]}
>
{user.displayName ?? user.email}
</Text>
));
};

return (
<ConfirmModal
title={translate('workspace.hr.gusto.syncResults.title')}
isVisible={isVisible}
onConfirm={onClose}
onCancel={onClose}
confirmText={translate('workspace.hr.gusto.syncResults.confirmText')}
shouldShowCancelButton={false}
prompt={
<View style={[styles.gap4]}>
<View>
<Text style={[styles.textLabelSupporting]}>{translate('workspace.hr.gusto.syncResults.added')}</Text>
{renderUsers(result?.added)}
</View>
<View>
<Text style={[styles.textLabelSupporting]}>{translate('workspace.hr.gusto.syncResults.removed')}</Text>
{renderUsers(result?.removed)}
</View>
<View>
<Text style={[styles.textLabelSupporting]}>{translate('workspace.hr.gusto.syncResults.skipped')}</Text>
{result?.skipped?.length ? (
result.skipped.map((user) => (
<View
key={`${user.email}-${user.reason}`}
style={[styles.mt1]}
>
<Text>{user.displayName ?? user.email}</Text>
<Text style={[styles.textSupporting]}>{getSkippedReasonLabel(user.reason)}</Text>
</View>
))
) : (
<Text style={[styles.textSupporting]}>{translate('workspace.hr.gusto.syncResults.empty')}</Text>
)}
</View>
</View>
}
/>
);
}

export default GustoSyncResultsModal;
2 changes: 2 additions & 0 deletions src/components/Icon/chunks/expensify-icons.chunk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ import Info from '@assets/images/info.svg';
import BillComSquare from '@assets/images/integrationicons/bill-com-icon-square.svg';
import CertiniaSquare from '@assets/images/integrationicons/certinia-icon-square.svg';
import CircleSlash from '@assets/images/integrationicons/circle-slash.svg';
import GustoSquare from '@assets/images/integrationicons/gusto-icon-square.svg';
import NetSuiteExport from '@assets/images/integrationicons/export/netsuite-icon.svg';
import QBOExport from '@assets/images/integrationicons/export/qbo-icon.svg';
import SageIntacctExport from '@assets/images/integrationicons/export/sage-intacct-icon.svg';
Expand Down Expand Up @@ -358,6 +359,7 @@ const Expensicons = {
GalleryPlus,
Gear,
Globe,
GustoSquare,
GoogleLogo,
Hashtag,
Heart,
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useExportedToFilterOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default function useExportedToFilterOptions(): UseExportedToFilterDataRes
}

const combinedUniqueExportTemplates = Array.from(uniqueExportTemplatesByName.values());
const integrationConnectionNamesSet = new Set<string>(Object.values(CONST.POLICY.CONNECTIONS.NAME));
const integrationConnectionNamesSet = new Set<string>(CONST.POLICY.CONNECTIONS.ACCOUNTING_CONNECTION_NAMES);

const standardAndCustomExportTemplates: string[] = [];
for (const template of combinedUniqueExportTemplates) {
Expand Down
Loading
Loading