Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat] Make it possible to submit expenses to any workspace, whether or not there is a workspace chat #49412

Merged
merged 15 commits into from
Oct 21, 2024
Merged
61 changes: 61 additions & 0 deletions src/libs/OptionsListUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import type * as OnyxCommon from '@src/types/onyx/OnyxCommon';
import type DeepValueOf from '@src/types/utils/DeepValueOf';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import times from '@src/utils/times';
import {createDraftReportForPolicyExpenseChat} from './actions/Report';
import Timing from './actions/Timing';
import * as ErrorUtils from './ErrorUtils';
import filterArrayByMatch from './filterArrayByMatch';
Expand All @@ -61,6 +62,7 @@ import * as UserUtils from './UserUtils';

type SearchOption<T> = ReportUtils.OptionData & {
item: T;
isOptimisticReportOption?: boolean;
};

type OptionList = {
Expand Down Expand Up @@ -179,6 +181,7 @@ type GetOptionsConfig = {
includeDomainEmail?: boolean;
action?: IOUAction;
shouldBoldTitleByDefault?: boolean;
includePoliciesWithoutExpenseChats?: boolean;
};

type GetUserToInviteConfig = {
Expand Down Expand Up @@ -239,6 +242,13 @@ Onyx.connect({
},
});

let allReportsDraft: OnyxCollection<Report>;
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT_DRAFT,
waitForCollectionCallback: true,
callback: (value) => (allReportsDraft = value),
});

let loginList: OnyxEntry<Login>;
Onyx.connect({
key: ONYXKEYS.LOGIN_LIST,
Expand Down Expand Up @@ -1516,6 +1526,7 @@ function isReportSelected(reportOption: ReportUtils.OptionData, selectedOptions:
function createOptionList(personalDetails: OnyxEntry<PersonalDetailsList>, reports?: OnyxCollection<Report>) {
const reportMapForAccountIDs: Record<number, Report> = {};
const allReportOptions: Array<SearchOption<Report>> = [];
const policyToReportForPolicyExpenseChats: Record<string, Report> = {};

if (reports) {
Object.values(reports).forEach((report) => {
Expand All @@ -1531,6 +1542,10 @@ function createOptionList(personalDetails: OnyxEntry<PersonalDetailsList>, repor
return;
}

if (ReportUtils.isPolicyExpenseChat(report) && report.policyID) {
policyToReportForPolicyExpenseChats[report.policyID] = report;
}

// Save the report in the map if this is a single participant so we can associate the reportID with the
// personal detail option later. Individuals should not be associated with single participant
// policyExpenseChats or chatRooms since those are not people.
Expand All @@ -1545,6 +1560,46 @@ function createOptionList(personalDetails: OnyxEntry<PersonalDetailsList>, repor
});
}

const policiesWithoutExpenseChats = Object.values(policies ?? {}).filter((policy) => {
if (policy?.type === CONST.POLICY.TYPE.PERSONAL || !policy?.isPolicyExpenseChatEnabled) {
ishpaul777 marked this conversation as resolved.
Show resolved Hide resolved
return false;
}
return !policyToReportForPolicyExpenseChats[policy?.id ?? ''];
});

// go through each policy and create a optimistic report option for it
if (policiesWithoutExpenseChats && policiesWithoutExpenseChats.length > 0) {
policiesWithoutExpenseChats.forEach((policy) => {
// check for draft report exist in allreportDrafts for the policy
let draftReport = Object.values(allReportsDraft ?? {})?.find((reportDraft) => reportDraft?.policyID === policy?.id);
if (!draftReport) {
draftReport = ReportUtils.buildOptimisticChatReport(
[currentUserAccountID ?? -1],
'',
CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT,
policy?.id,
currentUserAccountID,
true,
policy?.name,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
);
createDraftReportForPolicyExpenseChat({...draftReport, isOptimisticReport: true});
}
const accountIDs = ReportUtils.getParticipantsAccountIDsForDisplay(draftReport);
allReportOptions.push({
item: draftReport,
isOptimisticReportOption: true,
...createOption(accountIDs, personalDetails, draftReport, {}),
});
});
}
const allPersonalDetailsOptions = Object.values(personalDetails ?? {}).map((personalDetail) => ({
item: personalDetail,
...createOption([personalDetail?.accountID ?? -1], personalDetails, reportMapForAccountIDs[personalDetail?.accountID ?? -1], {}, {showPersonalDetails: true}),
Expand Down Expand Up @@ -1744,6 +1799,7 @@ function getOptions(
includeDomainEmail = false,
action,
shouldBoldTitleByDefault = true,
includePoliciesWithoutExpenseChats = false,
}: GetOptionsConfig,
): Options {
if (includeCategories) {
Expand Down Expand Up @@ -1808,6 +1864,9 @@ function getOptions(

// Filter out all the reports that shouldn't be displayed
const filteredReportOptions = options.reports.filter((option) => {
if (option.isOptimisticReportOption && !includePoliciesWithoutExpenseChats) {
return;
}
const report = option.item;
const doesReportHaveViolations = shouldShowViolations(report, transactionViolations);

Expand Down Expand Up @@ -2138,6 +2197,7 @@ function getFilteredOptions(
includeInvoiceRooms = false,
action: IOUAction | undefined = undefined,
sortByReportTypeInSearch = false,
includePoliciesWithoutExpenseChats = false,
) {
return getOptions(
{reports, personalDetails},
Expand Down Expand Up @@ -2167,6 +2227,7 @@ function getFilteredOptions(
includeInvoiceRooms,
action,
sortByReportTypeInSearch,
includePoliciesWithoutExpenseChats,
},
);
}
Expand Down
2 changes: 2 additions & 0 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@
| 'chatReportID'
| 'iouReportID'
| 'isOwnPolicyExpenseChat'
| 'isPolicyExpenseChat'
| 'isPinned'
| 'lastActorAccountID'
| 'lastMessageTranslationKey'
Expand Down Expand Up @@ -1536,7 +1537,7 @@
*/
function isExpenseRequest(report: OnyxInputOrEntry<Report>): boolean {
if (isThread(report)) {
const parentReportAction = ReportActionsUtils.getParentReportAction(report);

Check failure on line 1540 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'getParentReportAction' is deprecated. Use Onyx.connect() or withOnyx() instead
const parentReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`];
return isExpenseReport(parentReport) && !isEmptyObject(parentReportAction) && ReportActionsUtils.isTransactionThread(parentReportAction);
}
Expand All @@ -1549,7 +1550,7 @@
*/
function isIOURequest(report: OnyxInputOrEntry<Report>): boolean {
if (isThread(report)) {
const parentReportAction = ReportActionsUtils.getParentReportAction(report);

Check failure on line 1553 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'getParentReportAction' is deprecated. Use Onyx.connect() or withOnyx() instead
const parentReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`];
return isIOUReport(parentReport) && !isEmptyObject(parentReportAction) && ReportActionsUtils.isTransactionThread(parentReportAction);
}
Expand All @@ -1562,7 +1563,7 @@
*/
function isTrackExpenseReport(report: OnyxInputOrEntry<Report>): boolean {
if (isThread(report)) {
const parentReportAction = ReportActionsUtils.getParentReportAction(report);

Check failure on line 1566 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'getParentReportAction' is deprecated. Use Onyx.connect() or withOnyx() instead
return !isEmptyObject(parentReportAction) && ReportActionsUtils.isTrackExpenseAction(parentReportAction);
}
return false;
Expand Down Expand Up @@ -2211,7 +2212,7 @@
return [fallbackIcon];
}
if (isExpenseRequest(report)) {
const parentReportAction = ReportActionsUtils.getParentReportAction(report);

Check failure on line 2215 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'getParentReportAction' is deprecated. Use Onyx.connect() or withOnyx() instead
const workspaceIcon = getWorkspaceIcon(report, policy);
const memberIcon = {
source: personalDetails?.[parentReportAction?.actorAccountID ?? -1]?.avatar ?? FallbackAvatar,
Expand All @@ -2224,7 +2225,7 @@
return [memberIcon, workspaceIcon];
}
if (isChatThread(report)) {
const parentReportAction = ReportActionsUtils.getParentReportAction(report);

Check failure on line 2228 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'getParentReportAction' is deprecated. Use Onyx.connect() or withOnyx() instead

const actorAccountID = getReportActionActorAccountID(parentReportAction, report);
const actorDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(allPersonalDetails?.[actorAccountID ?? -1], '', false);
Expand Down Expand Up @@ -3105,7 +3106,7 @@
const transactionID = moneyRequestReport ? ReportActionsUtils.getOriginalMessage(reportAction)?.IOUTransactionID : 0;
const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] ?? ({} as Transaction);

const parentReportAction = ReportActionsUtils.getParentReportAction(moneyRequestReport);

Check failure on line 3109 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'getParentReportAction' is deprecated. Use Onyx.connect() or withOnyx() instead

const isRequestIOU = isIOUReport(moneyRequestReport);
const isHoldActionCreator = isHoldCreator(transaction, reportAction.childReportID ?? '-1');
Expand Down Expand Up @@ -3712,7 +3713,7 @@
}

let formattedName: string | undefined;
const parentReportAction = parentReportActionParam ?? ReportActionsUtils.getParentReportAction(report);

Check failure on line 3716 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'getParentReportAction' is deprecated. Use Onyx.connect() or withOnyx() instead
const parentReportActionMessage = ReportActionsUtils.getReportActionMessage(parentReportAction);

if (parentReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.SUBMITTED) {
Expand Down Expand Up @@ -4248,7 +4249,7 @@

// These parameters are not saved on the reportAction, but are used to display the task in the UI
// Added when we fetch the reportActions on a report
reportAction.reportAction.originalMessage = {

Check failure on line 4252 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'originalMessage' is deprecated. Used in old report actions before migration. Replaced by using getOriginalMessage function
html: ReportActionsUtils.getReportActionHtml(reportAction.reportAction),
taskReportID: ReportActionsUtils.getReportActionMessage(reportAction.reportAction)?.taskReportID,
whisperedTo: [],
Expand Down Expand Up @@ -5099,6 +5100,7 @@
chatType,
isOwnPolicyExpenseChat,
isPinned: isNewlyCreatedWorkspaceChat,
isPolicyExpenseChat: chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT,
lastActorAccountID: 0,
lastMessageTranslationKey: '',
lastMessageHtml: '',
Expand Down Expand Up @@ -5994,7 +5996,7 @@
// This can also happen for anyone accessing a public room or archived room for which they don't have access to the underlying policy.
// Optionally exclude reports that do not belong to currently active workspace

const parentReportAction = ReportActionsUtils.getParentReportAction(report);

Check failure on line 5999 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'getParentReportAction' is deprecated. Use Onyx.connect() or withOnyx() instead

if (
!report?.reportID ||
Expand Down
7 changes: 6 additions & 1 deletion src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ import processReportIDDeeplink from '@libs/processReportIDDeeplink';
import * as Pusher from '@libs/Pusher/pusher';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportConnection from '@libs/ReportConnection';
import type {OptimisticAddCommentReportAction} from '@libs/ReportUtils';
import type {OptimisticAddCommentReportAction, OptimisticChatReport} from '@libs/ReportUtils';
import * as ReportUtils from '@libs/ReportUtils';
import {doesReportBelongToWorkspace} from '@libs/ReportUtils';
import shouldSkipDeepLinkNavigation from '@libs/shouldSkipDeepLinkNavigation';
Expand Down Expand Up @@ -4097,6 +4097,10 @@ function markAsManuallyExported(reportID: string, connectionName: ConnectionName
API.write(WRITE_COMMANDS.MARK_AS_EXPORTED, params, {optimisticData, successData, failureData});
}

function createDraftReportForPolicyExpenseChat(draftReport: OptimisticChatReport) {
Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${draftReport.reportID}`, draftReport);
}

export type {Video};

export {
Expand Down Expand Up @@ -4185,4 +4189,5 @@ export {
exportToIntegration,
markAsManuallyExported,
handleReportChanged,
createDraftReportForPolicyExpenseChat,
};
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF
iouType === CONST.IOU.TYPE.INVOICE,
action,
isPaidGroupPolicy,
true,
);

return optionList;
Expand Down
Loading