Skip to content

Commit

Permalink
Merge pull request #35928 from tienifr/fix/35309
Browse files Browse the repository at this point in the history
fix: Approving a report causes the workspace chat to “flash” in the LHN before disappearing
  • Loading branch information
iwiznia authored Mar 8, 2024
2 parents bcc1749 + c290a70 commit a2c24e1
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 53 deletions.
25 changes: 5 additions & 20 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import useWindowDimensions from '@hooks/useWindowDimensions';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import * as HeaderUtils from '@libs/HeaderUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -51,13 +50,9 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money
const {translate} = useLocalize();
const {windowWidth, isSmallScreenWidth} = useWindowDimensions();
const {reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(moneyRequestReport);
const isApproved = ReportUtils.isReportApproved(moneyRequestReport);
const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID);
const canAllowSettlement = ReportUtils.hasUpdatedTotal(moneyRequestReport);
const policyType = policy?.type;
const isAutoReimbursable = ReportUtils.canBeAutoReimbursed(moneyRequestReport, policy);
const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicy(moneyRequestReport);
const isManager = ReportUtils.isMoneyRequestReport(moneyRequestReport) && session?.accountID === moneyRequestReport.managerID;
const isPayer = ReportUtils.isPayer(session, moneyRequestReport);
const isDraft = ReportUtils.isDraftExpenseReport(moneyRequestReport);
const [isConfirmModalVisible, setIsConfirmModalVisible] = useState(false);
Expand All @@ -70,22 +65,12 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money
setIsConfirmModalVisible(false);
}, [moneyRequestReport, chatReport]);

const isOnInstantSubmitPolicy = PolicyUtils.isInstantSubmitEnabled(policy);
const isOnSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy);
const shouldShowPayButton = useMemo(
() => isPayer && !isDraft && !isSettled && !moneyRequestReport.isWaitingOnBankAccount && reimbursableSpend !== 0 && !ReportUtils.isArchivedRoom(chatReport) && !isAutoReimbursable,
[isPayer, isDraft, isSettled, moneyRequestReport, reimbursableSpend, chatReport, isAutoReimbursable],
);
const shouldShowApproveButton = useMemo(() => {
if (!isPaidGroupPolicy) {
return false;
}
if (isOnInstantSubmitPolicy && isOnSubmitAndClosePolicy) {
return false;
}
return isManager && !isDraft && !isApproved && !isSettled;
}, [isPaidGroupPolicy, isManager, isDraft, isApproved, isSettled, isOnInstantSubmitPolicy, isOnSubmitAndClosePolicy]);
const shouldShowPayButton = useMemo(() => IOU.canIOUBePaid(moneyRequestReport, chatReport, policy), [moneyRequestReport, chatReport, policy]);

const shouldShowApproveButton = useMemo(() => IOU.canApproveIOU(moneyRequestReport, chatReport, policy), [moneyRequestReport, chatReport, policy]);

const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton;

const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0;
const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE;
const shouldShowNextStep = !ReportUtils.isClosedExpenseReportWithNoExpenses(moneyRequestReport) && isFromPaidPolicy && !!nextStep?.message?.length;
Expand Down
34 changes: 5 additions & 29 deletions src/components/ReportActionItem/ReportPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import ControlSelection from '@libs/ControlSelection';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReceiptUtils from '@libs/ReceiptUtils';
import * as ReportActionUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
Expand All @@ -30,7 +29,7 @@ import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {Policy, Report, ReportAction, Session, Transaction, TransactionViolations} from '@src/types/onyx';
import type {Policy, Report, ReportAction, Transaction, TransactionViolations} from '@src/types/onyx';
import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';
import ReportActionItemImages from './ReportActionItemImages';

Expand All @@ -44,9 +43,6 @@ type ReportPreviewOnyxProps = {
/** Active IOU Report for current report */
iouReport: OnyxEntry<Report>;

/** Session info for the currently logged in user. */
session: OnyxEntry<Session>;

/** All the transactions, used to update ReportPreview label and status */
transactions: OnyxCollection<Transaction>;

Expand Down Expand Up @@ -85,7 +81,6 @@ type ReportPreviewProps = ReportPreviewOnyxProps & {

function ReportPreview({
iouReport,
session,
policy,
iouReportID,
policyID,
Expand Down Expand Up @@ -118,12 +113,9 @@ function ReportPreview({
);

const managerID = iouReport?.managerID ?? 0;
const isCurrentUserManager = managerID === session?.accountID;
const {totalDisplaySpend, reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(iouReport);
const isAutoReimbursable = ReportUtils.canBeAutoReimbursed(iouReport, policy);

const iouSettled = ReportUtils.isSettled(iouReportID);
const iouCanceled = ReportUtils.isArchivedRoom(chatReport);
const numberOfRequests = ReportActionUtils.getNumberOfMoneyRequests(action);
const moneyRequestComment = action?.childLastMoneyRequestComment ?? '';
const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(chatReport);
Expand Down Expand Up @@ -208,23 +200,10 @@ function ReportPreview({

const bankAccountRoute = ReportUtils.getBankAccountRoute(chatReport);

const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport);
const isPayer = ReportUtils.isPayer(session, iouReport);
const isOnInstantSubmitPolicy = PolicyUtils.isInstantSubmitEnabled(policy);
const isOnSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy);
const shouldShowPayButton = useMemo(
() => isPayer && !isDraftExpenseReport && !iouSettled && !iouReport?.isWaitingOnBankAccount && reimbursableSpend !== 0 && !iouCanceled && !isAutoReimbursable,
[isPayer, isDraftExpenseReport, iouSettled, reimbursableSpend, iouCanceled, isAutoReimbursable, iouReport],
);
const shouldShowApproveButton = useMemo(() => {
if (!isPaidGroupPolicy) {
return false;
}
if (isOnInstantSubmitPolicy && isOnSubmitAndClosePolicy) {
return false;
}
return isCurrentUserManager && !isDraftExpenseReport && !isApproved && !iouSettled;
}, [isPaidGroupPolicy, isCurrentUserManager, isDraftExpenseReport, isApproved, isOnInstantSubmitPolicy, isOnSubmitAndClosePolicy, iouSettled]);
const shouldShowPayButton = useMemo(() => IOU.canIOUBePaid(iouReport, chatReport, policy), [iouReport, chatReport, policy]);

const shouldShowApproveButton = useMemo(() => IOU.canApproveIOU(iouReport, chatReport, policy), [iouReport, chatReport, policy]);

const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton;

/*
Expand Down Expand Up @@ -353,9 +332,6 @@ export default withOnyx<ReportPreviewProps, ReportPreviewOnyxProps>({
iouReport: {
key: ({iouReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`,
},
session: {
key: ONYXKEYS.SESSION,
},
transactions: {
key: ONYXKEYS.COLLECTION.TRANSACTION,
},
Expand Down
2 changes: 1 addition & 1 deletion src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ function isInstantSubmitEnabled(policy: OnyxEntry<Policy> | EmptyObject): boolea
/**
* Checks if policy's approval mode is "optional", a.k.a. "Submit & Close"
*/
function isSubmitAndClose(policy: OnyxEntry<Policy>): boolean {
function isSubmitAndClose(policy: OnyxEntry<Policy> | EmptyObject): boolean {
return policy?.approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL;
}

Expand Down
4 changes: 2 additions & 2 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5171,8 +5171,8 @@ function getAllAncestorReportActionIDs(report: Report | null | undefined): Ances
return allAncestorIDs;
}

function canBeAutoReimbursed(report: OnyxEntry<Report>, policy: OnyxEntry<Policy> = null): boolean {
if (!policy) {
function canBeAutoReimbursed(report: OnyxEntry<Report>, policy: OnyxEntry<Policy> | EmptyObject): boolean {
if (isEmptyObject(policy)) {
return false;
}
type CurrencyType = (typeof CONST.DIRECT_REIMBURSEMENT_CURRENCIES)[number];
Expand Down
83 changes: 82 additions & 1 deletion src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3677,10 +3677,73 @@ function sendMoneyWithWallet(report: OnyxTypes.Report, amount: number, currency:
Report.notifyNewAction(params.chatReportID, managerID);
}

function canIOUBePaid(iouReport: OnyxEntry<OnyxTypes.Report> | EmptyObject, chatReport: OnyxEntry<OnyxTypes.Report> | EmptyObject, policy: OnyxEntry<OnyxTypes.Policy> | EmptyObject) {
const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(chatReport);
const iouCanceled = ReportUtils.isArchivedRoom(chatReport);

if (isEmptyObject(iouReport)) {
return false;
}

const isPayer = ReportUtils.isPayer(
{
email: currentUserEmail,
accountID: userAccountID,
},
iouReport,
);

const isDraftExpenseReport = isPolicyExpenseChat && ReportUtils.isDraftExpenseReport(iouReport);
const iouSettled = ReportUtils.isSettled(iouReport?.reportID);

const {reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(iouReport);
const isAutoReimbursable = ReportUtils.canBeAutoReimbursed(iouReport, policy);
return isPayer && !isDraftExpenseReport && !iouSettled && !iouReport?.isWaitingOnBankAccount && reimbursableSpend !== 0 && !iouCanceled && !isAutoReimbursable;
}

function canApproveIOU(iouReport: OnyxEntry<OnyxTypes.Report> | EmptyObject, chatReport: OnyxEntry<OnyxTypes.Report> | EmptyObject, policy: OnyxEntry<OnyxTypes.Policy> | EmptyObject) {
if (isEmptyObject(chatReport)) {
return false;
}
const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport);
if (!isPaidGroupPolicy) {
return false;
}

const isOnInstantSubmitPolicy = PolicyUtils.isInstantSubmitEnabled(policy);
const isOnSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy);
if (isOnInstantSubmitPolicy && isOnSubmitAndClosePolicy) {
return false;
}

const managerID = iouReport?.managerID ?? 0;
const isCurrentUserManager = managerID === userAccountID;
const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(chatReport);

const isDraftExpenseReport = isPolicyExpenseChat && ReportUtils.isDraftExpenseReport(iouReport);
const isApproved = ReportUtils.isReportApproved(iouReport);
const iouSettled = ReportUtils.isSettled(iouReport?.reportID);

return isCurrentUserManager && !isDraftExpenseReport && !isApproved && !iouSettled;
}

function hasIOUToApproveOrPay(chatReport: OnyxEntry<OnyxTypes.Report> | EmptyObject, excludedIOUReportID: string): boolean {
const chatReportActions = ReportActionsUtils.getAllReportActions(chatReport?.reportID ?? '');

return Object.values(chatReportActions).some((action) => {
const iouReport = ReportUtils.getReport(action.childReportID ?? '');
const policy = ReportUtils.getPolicy(iouReport?.policyID);

const shouldShowSettlementButton = canIOUBePaid(iouReport, chatReport, policy) || canApproveIOU(iouReport, chatReport, policy);
return action.childReportID?.toString() !== excludedIOUReportID && action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && shouldShowSettlementButton;
});
}

function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject) {
const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`] ?? null;
const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(expenseReport.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID);
const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, CONST.REPORT.STATUS_NUM.APPROVED);
const chatReport = ReportUtils.getReport(expenseReport.chatReportID);

const optimisticReportActionsData: OnyxUpdate = {
onyxMethod: Onyx.METHOD.MERGE,
Expand All @@ -3703,12 +3766,21 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject) {
statusNum: CONST.REPORT.STATUS_NUM.APPROVED,
},
};

const optimisticChatReportData: OnyxUpdate = {
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport?.chatReportID}`,
value: {
hasOutstandingChildRequest: hasIOUToApproveOrPay(chatReport, expenseReport?.reportID ?? ''),
},
};

const optimisticNextStepData: OnyxUpdate = {
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`,
value: optimisticNextStep,
};
const optimisticData: OnyxUpdate[] = [optimisticIOUReportData, optimisticReportActionsData, optimisticNextStepData];
const optimisticData: OnyxUpdate[] = [optimisticIOUReportData, optimisticReportActionsData, optimisticNextStepData, optimisticChatReportData];

const successData: OnyxUpdate[] = [
{
Expand All @@ -3732,6 +3804,13 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject) {
},
},
},
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.chatReportID}`,
value: {
hasOutstandingChildRequest: chatReport?.hasOutstandingChildRequest,
},
},
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`,
Expand Down Expand Up @@ -4331,4 +4410,6 @@ export {
cancelPayment,
navigateToStartStepIfScanFileCannotBeRead,
savePreferredPaymentMethod,
canIOUBePaid,
canApproveIOU,
};

0 comments on commit a2c24e1

Please sign in to comment.