Skip to content

Commit

Permalink
Merge pull request Expensify#37213 from Expensify/lucien/wave8-workfl…
Browse files Browse the repository at this point in the history
…ows-autoreporting

[Simplified Collect][Workflows] Create AutoReporting page and commands
  • Loading branch information
luacmartins authored Mar 1, 2024
2 parents 39a257d + 94c1824 commit a2d0cf2
Show file tree
Hide file tree
Showing 20 changed files with 520 additions and 45 deletions.
1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ const CONST = {
BETA_COMMENT_LINKING: 'commentLinking',
VIOLATIONS: 'violations',
REPORT_FIELDS: 'reportFields',
WORKFLOWS_DELAYED_SUBMISSION: 'workflowsDelayedSubmission',
},
BUTTON_STATES: {
DEFAULT: 'default',
Expand Down
8 changes: 8 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,14 @@ const ROUTES = {
route: 'workspace/:policyID/workflows',
getRoute: (policyID: string) => `workspace/${policyID}/workflows` as const,
},
WORKSPACE_WORKFLOWS_AUTOREPORTING_FREQUENCY: {
route: 'workspace/:policyID/settings/workflows/auto-reporting-frequency',
getRoute: (policyID: string) => `workspace/${policyID}/settings/workflows/auto-reporting-frequency` as const,
},
WORKSPACE_WORKFLOWS_AUTOREPORTING_MONTHLY_OFFSET: {
route: 'workspace/:policyID/settings/workflows/auto-reporting-frequency/monthly-offset',
getRoute: (policyID: string) => `workspace/${policyID}/settings/workflows/auto-reporting-frequency/monthly-offset` as const,
},
WORKSPACE_CARD: {
route: 'workspace/:policyID/card',
getRoute: (policyID: string) => `workspace/${policyID}/card` as const,
Expand Down
2 changes: 2 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ const SCREENS = {
CATEGORIES: 'Workspace_Categories',
CURRENCY: 'Workspace_Profile_Currency',
WORKFLOWS: 'Workspace_Workflows',
WORKFLOWS_AUTO_REPORTING_FREQUENCY: 'Workspace_Workflows_Auto_Reporting_Frequency',
WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET: 'Workspace_Workflows_Auto_Reporting_Monthly_Offset',
DESCRIPTION: 'Workspace_Profile_Description',
SHARE: 'Workspace_Profile_Share',
NAME: 'Workspace_Profile_Name',
Expand Down
9 changes: 8 additions & 1 deletion src/components/LocaleContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ type LocaleContextProps = {
/** Gets the locale digit corresponding to a standard digit */
toLocaleDigit: (digit: string) => string;

/** Formats a number into its localized ordinal representation */
toLocaleOrdinal: (number: number) => string;

/** Gets the standard digit corresponding to a locale digit */
fromLocaleDigit: (digit: string) => string;

Expand All @@ -65,6 +68,7 @@ const LocaleContext = createContext<LocaleContextProps>({
updateLocale: () => '',
formatPhoneNumber: () => '',
toLocaleDigit: () => '',
toLocaleOrdinal: () => '',
fromLocaleDigit: () => '',
preferredLocale: CONST.LOCALES.DEFAULT,
});
Expand Down Expand Up @@ -98,6 +102,8 @@ function LocaleContextProvider({preferredLocale, currentUserPersonalDetails = {}

const toLocaleDigit = useMemo<LocaleContextProps['toLocaleDigit']>(() => (digit) => LocaleDigitUtils.toLocaleDigit(locale, digit), [locale]);

const toLocaleOrdinal = useMemo<LocaleContextProps['toLocaleOrdinal']>(() => (number) => LocaleDigitUtils.toLocaleOrdinal(locale, number), [locale]);

const fromLocaleDigit = useMemo<LocaleContextProps['fromLocaleDigit']>(() => (localeDigit) => LocaleDigitUtils.fromLocaleDigit(locale, localeDigit), [locale]);

const contextValue = useMemo<LocaleContextProps>(
Expand All @@ -109,10 +115,11 @@ function LocaleContextProvider({preferredLocale, currentUserPersonalDetails = {}
updateLocale,
formatPhoneNumber,
toLocaleDigit,
toLocaleOrdinal,
fromLocaleDigit,
preferredLocale: locale,
}),
[translate, numberFormat, datetimeToRelative, datetimeToCalendarTime, updateLocale, formatPhoneNumber, toLocaleDigit, fromLocaleDigit, locale],
[translate, numberFormat, datetimeToRelative, datetimeToCalendarTime, updateLocale, formatPhoneNumber, toLocaleDigit, toLocaleOrdinal, fromLocaleDigit, locale],
);

return <LocaleContext.Provider value={contextValue}>{children}</LocaleContext.Provider>;
Expand Down
26 changes: 20 additions & 6 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1037,18 +1037,32 @@ export default {
delaySubmissionTitle: 'Delay submissions',
delaySubmissionDescription: 'Expenses are shared right away for better spend visibility. Set a slower cadence if needed.',
submissionFrequency: 'Submission frequency',
weeklyFrequency: 'Weekly',
monthlyFrequency: 'Monthly',
twiceAMonthFrequency: 'Twice a month',
byTripFrequency: 'By trip',
manuallyFrequency: 'Manually',
dailyFrequency: 'Daily',
submissionFrequencyDateOfMonth: 'Date of month',
addApprovalsTitle: 'Add approvals',
approver: 'Approver',
connectBankAccount: 'Connect bank account',
addApprovalsDescription: 'Require additional approval before authorizing a payment.',
makeOrTrackPaymentsTitle: 'Make or track payments',
makeOrTrackPaymentsDescription: 'Add an authorized payer for payments made in Expensify, or simply track payments made elsewhere.',
editor: {
submissionFrequency: 'Choose how long Expensify should wait before sharing error-free spend.',
},
frequencies: {
weekly: 'Weekly',
monthly: 'Monthly',
twiceAMonth: 'Twice a month',
byTrip: 'By trip',
manually: 'Manually',
daily: 'Daily',
lastDayOfMonth: 'Last day of the month',
lastBusinessDayOfMonth: 'Last business day of the month',
ordinals: {
one: 'st',
two: 'nd',
few: 'rd',
other: 'th',
},
},
},
reportFraudPage: {
title: 'Report virtual card fraud',
Expand Down
26 changes: 20 additions & 6 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1033,18 +1033,32 @@ export default {
delaySubmissionTitle: 'Retrasar envíos',
delaySubmissionDescription: 'Los gastos se comparten de inmediato para una mejor visibilidad del gasto. Establece una cadencia más lenta si es necesario.',
submissionFrequency: 'Frecuencia de envíos',
weeklyFrequency: 'Semanal',
monthlyFrequency: 'Mensual',
twiceAMonthFrequency: 'Dos veces al mes',
byTripFrequency: 'Por viaje',
manuallyFrequency: 'Manual',
dailyFrequency: 'Diaria',
submissionFrequencyDateOfMonth: 'Fecha del mes',
addApprovalsTitle: 'Requerir aprobaciones',
approver: 'Aprobador',
connectBankAccount: 'Conectar cuenta bancaria',
addApprovalsDescription: 'Requiere una aprobación adicional antes de autorizar un pago.',
makeOrTrackPaymentsTitle: 'Realizar o seguir pagos',
makeOrTrackPaymentsDescription: 'Añade un pagador autorizado para los pagos realizados en Expensify, o simplemente realiza un seguimiento de los pagos realizados en otro lugar.',
editor: {
submissionFrequency: 'Elige cuánto tiempo Expensify debe esperar antes de compartir los gastos sin errores.',
},
frequencies: {
weekly: 'Semanal',
monthly: 'Mensual',
twiceAMonth: 'Dos veces al mes',
byTrip: 'Por viaje',
manually: 'Manualmente',
daily: 'Diaria',
lastDayOfMonth: 'Último día del mes',
lastBusinessDayOfMonth: 'Último día hábil del mes',
ordinals: {
one: '.º',
two: '.º',
few: '.º',
other: '.º',
},
},
},
reportFraudPage: {
title: 'Reportar fraude con la tarjeta virtual',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type {ValueOf} from 'type-fest';
import type CONST from '@src/CONST';

type SetWorkspaceAutoReportingFrequencyParams = {
policyID: string;
frequency: ValueOf<typeof CONST.POLICY.AUTO_REPORTING_FREQUENCIES>;
};

export default SetWorkspaceAutoReportingFrequencyParams;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type SetWorkspaceAutoReportingMonthlyOffsetParams = {
policyID: string;
value: string;
};

export default SetWorkspaceAutoReportingMonthlyOffsetParams;
2 changes: 2 additions & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,5 +147,7 @@ export type {default as AcceptACHContractForBankAccount} from './AcceptACHContra
export type {default as UpdateWorkspaceDescriptionParams} from './UpdateWorkspaceDescriptionParams';
export type {default as SetWorkspaceRequiresCategoryParams} from './SetWorkspaceRequiresCategoryParams';
export type {default as SetWorkspaceAutoReportingParams} from './SetWorkspaceAutoReportingParams';
export type {default as SetWorkspaceAutoReportingFrequencyParams} from './SetWorkspaceAutoReportingFrequencyParams';
export type {default as SetWorkspaceAutoReportingMonthlyOffsetParams} from './SetWorkspaceAutoReportingMonthlyOffsetParams';
export type {default as SetWorkspaceApprovalModeParams} from './SetWorkspaceApprovalModeParams';
export type {default as SwitchToOldDotParams} from './SwitchToOldDotParams';
4 changes: 4 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ type ApiRequest = ValueOf<typeof CONST.API_REQUEST_TYPE>;

const WRITE_COMMANDS = {
SET_WORKSPACE_AUTO_REPORTING: 'SetWorkspaceAutoReporting',
SET_WORKSPACE_AUTO_REPORTING_FREQUENCY: 'SetWorkspaceAutoReportingFrequency',
SET_WORKSPACE_AUTO_REPORTING_MONTHLY_OFFSET: 'UpdatePolicy',
SET_WORKSPACE_APPROVAL_MODE: 'SetWorkspaceApprovalMode',
DISMISS_REFERRAL_BANNER: 'DismissReferralBanner',
UPDATE_PREFERRED_LOCALE: 'UpdatePreferredLocale',
Expand Down Expand Up @@ -300,6 +302,8 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.ACCEPT_ACH_CONTRACT_FOR_BANK_ACCOUNT]: Parameters.AcceptACHContractForBankAccount;
[WRITE_COMMANDS.UPDATE_WORKSPACE_DESCRIPTION]: Parameters.UpdateWorkspaceDescriptionParams;
[WRITE_COMMANDS.SET_WORKSPACE_AUTO_REPORTING]: Parameters.SetWorkspaceAutoReportingParams;
[WRITE_COMMANDS.SET_WORKSPACE_AUTO_REPORTING_FREQUENCY]: Parameters.SetWorkspaceAutoReportingFrequencyParams;
[WRITE_COMMANDS.SET_WORKSPACE_AUTO_REPORTING_MONTHLY_OFFSET]: Parameters.SetWorkspaceAutoReportingMonthlyOffsetParams;
[WRITE_COMMANDS.SET_WORKSPACE_APPROVAL_MODE]: Parameters.SetWorkspaceApprovalModeParams;
[WRITE_COMMANDS.SWITCH_TO_OLD_DOT]: Parameters.SwitchToOldDotParams;
};
Expand Down
6 changes: 5 additions & 1 deletion src/libs/HttpUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ Onyx.connect({
// We use the AbortController API to terminate pending request in `cancelPendingRequests`
let cancellationController = new AbortController();

// Some existing old commands (6+ years) exempted from the auth writes count check
const exemptedCommandsWithAuthWrites: string[] = ['SetWorkspaceAutoReportingFrequency'];

/**
* The API commands that require the skew calculation
*/
Expand Down Expand Up @@ -120,7 +123,8 @@ function processHTTPRequest(url: string, method: RequestType = 'get', body: Form
title: CONST.ERROR_TITLE.SOCKET,
});
}
if (response.jsonCode === CONST.JSON_CODE.MANY_WRITES_ERROR) {

if (response.jsonCode === CONST.JSON_CODE.MANY_WRITES_ERROR && !exemptedCommandsWithAuthWrites.includes(response.data?.phpCommandName ?? '')) {
if (response.data) {
const {phpCommandName, authWriteCommands} = response.data;
// eslint-disable-next-line max-len
Expand Down
30 changes: 29 additions & 1 deletion src/libs/LocaleDigitUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import _ from 'lodash';
import type {ValueOf} from 'type-fest';
import type CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import * as Localize from './Localize';
import * as NumberFormatUtils from './NumberFormatUtils';

type Locale = ValueOf<typeof CONST.LOCALES>;
Expand Down Expand Up @@ -66,4 +68,30 @@ function fromLocaleDigit(locale: Locale, localeDigit: string): string {
return STANDARD_DIGITS[index];
}

export {toLocaleDigit, fromLocaleDigit};
/**
* Formats a number into its localized ordinal representation i.e 1st, 2nd etc
*/
function toLocaleOrdinal(locale: Locale, number: number): string {
// Defaults to "other" suffix or "th" in English
let suffixKey = 'workflowsPage.frequencies.ordinals.other';

// Calculate last digit of the number to determine basic ordinality
const lastDigit = number % 10;

// Calculate last two digits to handle exceptions in the 11-13 range
const lastTwoDigits = number % 100;

if (lastDigit === 1 && lastTwoDigits !== 11) {
suffixKey = 'workflowsPage.frequencies.ordinals.one';
} else if (lastDigit === 2 && lastTwoDigits !== 12) {
suffixKey = 'workflowsPage.frequencies.ordinals.two';
} else if (lastDigit === 3 && lastTwoDigits !== 13) {
suffixKey = 'workflowsPage.frequencies.ordinals.few';
}

const suffix = Localize.translate(locale, suffixKey as TranslationPaths);

return `${number}${suffix}`;
}

export {toLocaleDigit, toLocaleOrdinal, fromLocaleDigit};
2 changes: 2 additions & 0 deletions src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,8 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.SETTINGS.EXIT_SURVEY.REASON]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyReasonPage').default as React.ComponentType,
[SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyResponsePage').default as React.ComponentType,
[SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyConfirmPage').default as React.ComponentType,
[SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: () => require('../../../pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage').default as React.ComponentType,
[SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: () => require('../../../pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage').default as React.ComponentType,
});

const EnablePaymentsStackNavigator = createModalStackNavigator<EnablePaymentsNavigatorParamList>({
Expand Down
6 changes: 6 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
[SCREENS.WORKSPACE.DESCRIPTION]: {
path: ROUTES.WORKSPACE_PROFILE_DESCRIPTION.route,
},
[SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: {
path: ROUTES.WORKSPACE_WORKFLOWS_AUTOREPORTING_FREQUENCY.route,
},
[SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: {
path: ROUTES.WORKSPACE_WORKFLOWS_AUTOREPORTING_MONTHLY_OFFSET.route,
},
[SCREENS.WORKSPACE.SHARE]: {
path: ROUTES.WORKSPACE_PROFILE_SHARE.route,
},
Expand Down
6 changes: 6 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ type CentralPaneNavigatorParamList = {
[SCREENS.WORKSPACE.WORKFLOWS]: {
policyID: string;
};
[SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: {
policyID: string;
};
[SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: {
policyID: string;
};
[SCREENS.WORKSPACE.REIMBURSE]: {
policyID: string;
};
Expand Down
5 changes: 5 additions & 0 deletions src/libs/Permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ function canUseViolations(betas: OnyxEntry<Beta[]>): boolean {
return !!betas?.includes(CONST.BETAS.VIOLATIONS) || canUseAllBetas(betas);
}

function canUseWorkflowsDelayedSubmission(betas: OnyxEntry<Beta[]>): boolean {
return !!betas?.includes(CONST.BETAS.WORKFLOWS_DELAYED_SUBMISSION) || canUseAllBetas(betas);
}

/**
* Link previews are temporarily disabled.
*/
Expand All @@ -40,4 +44,5 @@ export default {
canUseLinkPreviews,
canUseViolations,
canUseReportFields,
canUseWorkflowsDelayedSubmission,
};
Loading

0 comments on commit a2d0cf2

Please sign in to comment.