This guide covers everything you need to know about creating and managing expense reports in New Expensify.
Creating a New Report
Automatic Report Creation
Reports can be created automatically based on your workspace’s submission frequency settings. When enabled, expenses are automatically grouped into reports according to the configured schedule.
// From src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx
const optionItems : ToggleSettingOptionRowProps [] = useMemo (() => {
return [
{
title: translate ( 'workflowsPage.submissionFrequency' ),
subtitle: translate ( 'workflowsPage.submissionFrequencyDescription' ),
switchAccessibilityLabel: translate ( 'workflowsPage.submissionFrequencyDescription' ),
onToggle : ( isEnabled : boolean ) => ( policy ? setWorkspaceAutoHarvesting ( policy , isEnabled ) : undefined ),
subMenuItems : (
< MenuItemWithTopDescription
title = { getAutoReportingFrequencyDisplayNames ( translate )[ getCorrectedAutoReportingFrequency ( policy ) ?? CONST.POLICY.AUTO_REPORTING_FREQUENCIES.WEEKLY]}
titleStyle = {styles. textNormalThemeText }
descriptionTextStyle = {styles. textLabelSupportingNormal }
onPress = { onPressAutoReportingFrequency }
description = { translate ( 'common.frequency' )}
shouldShowRightIcon
/>
),
isActive: ( policy ?. autoReporting && ! hasDelayedSubmissionError ) ?? false ,
},
];
}, [ /* dependencies */ ]);
Manual Report Creation
When automatic submission is disabled, you can create reports manually by grouping expenses together.
Report Lifecycle
Building Optimistic Report Data
When creating a new report, New Expensify builds optimistic data for immediate UI updates:
// From src/libs/actions/Report/index.ts
function buildNewReportOptimisticData (
policy : OnyxEntry < Policy >,
reportID : string ,
reportActionID : string ,
ownerPersonalDetails : CurrentUserPersonalDetails ,
reportPreviewReportActionID : string ,
hasViolationsParam : boolean ,
isASAPSubmitBetaEnabled : boolean ,
betas : OnyxEntry < Beta []>,
) {
const { accountID , login , email } = ownerPersonalDetails ;
const timeOfCreation = DateUtils . getDBTime ();
const parentReport = getPolicyExpenseChat ( accountID , policy ?. id );
const optimisticReportData = buildOptimisticEmptyReport ( reportID , accountID , parentReport , reportPreviewReportActionID , policy , timeOfCreation , betas );
const optimisticNextStep = buildOptimisticNextStep ({
report: optimisticReportData ,
predictedNextStatus: CONST . REPORT . STATUS_NUM . OPEN ,
policy ,
currentUserAccountIDParam: accountID ,
currentUserEmailParam: email ?? '' ,
hasViolations: hasViolationsParam ,
isASAPSubmitBetaEnabled ,
});
if ( optimisticNextStep ) {
optimisticReportData . nextStep = optimisticNextStep ;
}
return optimisticReportData ;
}
Editing Report Details
Updating Report Name
You can customize the report name to make it more descriptive:
// From src/libs/actions/Report/index.ts
function updateChatName ( reportID : string , oldReportName : string | undefined , reportName : string , type : typeof CONST . REPORT . CHAT_TYPE . GROUP | typeof CONST . REPORT . CHAT_TYPE . TRIP_ROOM ) {
const optimisticData : Array < OnyxUpdate < typeof ONYXKEYS . COLLECTION . REPORT >> = [
{
onyxMethod: Onyx . METHOD . MERGE ,
key: ` ${ ONYXKEYS . COLLECTION . REPORT }${ reportID } ` ,
value: {
reportName ,
pendingFields: {
reportName: CONST . RED_BRICK_ROAD_PENDING_ACTION . UPDATE ,
},
errorFields: {
reportName: null ,
},
},
},
];
const successData : Array < OnyxUpdate < typeof ONYXKEYS . COLLECTION . REPORT >> = [
{
onyxMethod: Onyx . METHOD . MERGE ,
key: ` ${ ONYXKEYS . COLLECTION . REPORT }${ reportID } ` ,
value: {
pendingFields: {
reportName: null ,
},
},
},
];
const failureData : Array < OnyxUpdate < typeof ONYXKEYS . COLLECTION . REPORT >> = [
{
onyxMethod: Onyx . METHOD . MERGE ,
key: ` ${ ONYXKEYS . COLLECTION . REPORT }${ reportID } ` ,
value: {
reportName: oldReportName ?? null ,
pendingFields: {
reportName: null ,
},
},
},
];
const command = type === CONST . REPORT . CHAT_TYPE . GROUP ? WRITE_COMMANDS . UPDATE_GROUP_CHAT_NAME : WRITE_COMMANDS . UPDATE_TRIP_ROOM_NAME ;
const parameters : UpdateChatNameParams = { reportName , reportID };
API . write ( command , parameters , { optimisticData , successData , failureData });
}
Updating Report Avatar
For group reports and workspace rooms, you can customize the report avatar:
// From src/libs/actions/Report/index.ts
function updateGroupChatAvatar ( reportID : string , oldAvatarUrl : string | undefined , file ?: File | CustomRNImageManipulatorResult ) {
const { optimisticData , successData , failureData } = buildUpdateReportAvatarOnyxData ( reportID , oldAvatarUrl , file );
const parameters : UpdateGroupChatAvatarParams = { file , reportID };
API . write ( WRITE_COMMANDS . UPDATE_GROUP_CHAT_AVATAR , parameters , { optimisticData , failureData , successData });
}
Add comments to provide context or communicate with approvers:
// From src/libs/actions/Report/index.ts
/** Add a single comment to a report */
function addComment ({
report ,
notifyReportID ,
ancestors ,
text ,
timezoneParam ,
currentUserAccountID ,
shouldPlaySound ,
isInSidePanel ,
pregeneratedResponseParams ,
reportActionID ,
} : AddCommentParams ) {
if ( shouldPlaySound ) {
playSound ( SOUNDS . DONE );
}
addActions ({ report , notifyReportID , ancestors , timezoneParam , currentUserAccountID , text , isInSidePanel , pregeneratedResponseParams , reportActionID });
}
Comments are visible to all participants on the report and create a permanent audit trail.
Report Actions
Available Actions
Depending on the report type and state, different actions are available:
Open Reports
Submitted Reports
Approved Reports
Closed Reports
Add expenses
Edit report details
Add comments
Submit for approval
Delete report
Add comments
Recall report
View approval status
View details
Add comments
Track reimbursement
View details (read-only)
Export report
The Report Details page provides various menu options:
// From src/pages/ReportDetailsPage.tsx
const menuItems : ReportDetailsPageMenuItem [] = useMemo (() => {
const items : ReportDetailsPageMenuItem [] = [];
if ( isSelfDM ) {
return [];
}
if ( isArchivedRoom ) {
return items ;
}
// Members option
if ( isGroupChat || ( ! isUserCreatedPolicyRoom && participants . length ) || ( isUserCreatedPolicyRoom && isPolicyEmployee )) {
items . push ({
key: CONST . REPORT_DETAILS_MENU_ITEM . MEMBERS ,
translationKey: 'common.members' ,
icon: expensifyIcons . Users ,
subtitle: activeChatMembers . length ,
isAnonymousAction: false ,
shouldShowRightIcon: true ,
action : () => {
if ( shouldOpenRoomMembersPage ) {
Navigation . navigate ( ROUTES . ROOM_MEMBERS . getRoute ( report ?. reportID , backTo ));
} else {
Navigation . navigate ( ROUTES . REPORT_PARTICIPANTS . getRoute ( report ?. reportID , backTo ));
}
},
});
}
// Settings option
if ( shouldShowMenuItem ) {
items . push ({
key: CONST . REPORT_DETAILS_MENU_ITEM . SETTINGS ,
translationKey: 'common.settings' ,
icon: expensifyIcons . Gear ,
isAnonymousAction: false ,
shouldShowRightIcon: true ,
action : () => {
Navigation . navigate ( ROUTES . REPORT_SETTINGS . getRoute ( report ?. reportID , backTo ));
},
});
}
return items ;
}, [ /* dependencies */ ]);
Report Validation
Checking Report State
Before performing actions, validate the report state:
// From src/libs/ReportUtils.ts
function isOpenReport ( report : OnyxEntry < Report >) : boolean {
return report ?. stateNum === CONST . REPORT . STATE_NUM . OPEN && report ?. statusNum === CONST . REPORT . STATUS_NUM . OPEN ;
}
function isProcessingReport ( report : OnyxEntry < Report >) : boolean {
return report ?. stateNum === CONST . REPORT . STATE_NUM . SUBMITTED && report ?. statusNum === CONST . REPORT . STATUS_NUM . SUBMITTED ;
}
function isOpenOrProcessingReport ( report : OnyxEntry < Report >) : boolean {
return isOpenReport ( report ) || isProcessingReport ( report );
}
Best Practices
Give your reports clear, descriptive names that indicate the purpose or time period (e.g., “Q1 2024 Travel Expenses” or “Client Meeting - January 2024”).
Add comments and context to your report early in the process, before submission. This helps approvers understand the expenses and speeds up the approval process.
Check your open reports regularly to ensure all expenses are properly categorized and receipts are attached.
Submit reports according to your company’s policy to ensure timely reimbursement and accurate financial reporting.
Common Issues
Report Creation Failed If report creation fails, check:
Network connectivity
Workspace permissions
Policy settings
Required field validation
Optimistic Updates New Expensify uses optimistic updates to provide instant feedback. If you see a pending indicator, the action is being synced with the server.
Reports Overview Learn about expense reports
Approval Workflows Understand the approval process
Report Fields Add custom fields to reports