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

[Wave 8 - ECard Transactions] Update Expense Report Total Breakdown #29566

Merged
merged 8 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
10 changes: 5 additions & 5 deletions src/components/MoneyReportHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const defaultProps = {

function MoneyReportHeader({session, personalDetails, policy, chatReport, report: moneyRequestReport, isSmallScreenWidth}) {
const {translate} = useLocalize();
const reportTotal = ReportUtils.getMoneyRequestTotal(moneyRequestReport);
const reimbursableTotal = ReportUtils.getMoneyRequestReimbursableTotal(moneyRequestReport);
const isApproved = ReportUtils.isReportApproved(moneyRequestReport);
const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID);
const policyType = lodashGet(policy, 'type');
Expand All @@ -71,19 +71,19 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, report
const isPayer = policyType === CONST.POLICY.TYPE.CORPORATE ? isPolicyAdmin && isApproved : isPolicyAdmin || (ReportUtils.isMoneyRequestReport(moneyRequestReport) && isManager);
const isDraft = ReportUtils.isReportDraft(moneyRequestReport);
const shouldShowSettlementButton = useMemo(
() => isPayer && !isDraft && !isSettled && !moneyRequestReport.isWaitingOnBankAccount && reportTotal !== 0 && !ReportUtils.isArchivedRoom(chatReport),
[isPayer, isDraft, isSettled, moneyRequestReport, reportTotal, chatReport],
() => isPayer && !isDraft && !isSettled && !moneyRequestReport.isWaitingOnBankAccount && reimbursableTotal !== 0 && !ReportUtils.isArchivedRoom(chatReport),
[isPayer, isDraft, isSettled, moneyRequestReport, reimbursableTotal, chatReport],
);
const shouldShowApproveButton = useMemo(() => {
if (policyType !== CONST.POLICY.TYPE.CORPORATE) {
return false;
}
return isManager && !isDraft && !isApproved && !isSettled;
}, [policyType, isManager, isDraft, isApproved, isSettled]);
const shouldShowSubmitButton = isDraft && reportTotal !== 0;
const shouldShowSubmitButton = isDraft && reimbursableTotal !== 0;
const shouldShowAnyButton = shouldShowSettlementButton || shouldShowApproveButton || shouldShowSubmitButton;
const bankAccountRoute = ReportUtils.getBankAccountRoute(chatReport);
const formattedAmount = CurrencyUtils.convertToDisplayString(reportTotal, moneyRequestReport.currency);
const formattedAmount = CurrencyUtils.convertToDisplayString(reimbursableTotal, moneyRequestReport.currency);

return (
<View style={[styles.pt0]}>
Expand Down
70 changes: 64 additions & 6 deletions src/components/ReportActionItem/MoneyReportView.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import PropTypes from 'prop-types';
import reportPropTypes from '../../pages/reportPropTypes';
import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions';
Expand All @@ -8,7 +9,9 @@ import themeColors from '../../styles/themes/default';
import * as ReportUtils from '../../libs/ReportUtils';
import * as StyleUtils from '../../styles/StyleUtils';
import CONST from '../../CONST';
import compose from '../../libs/compose';
import Text from '../Text';
import ONYXKEYS from '../../ONYXKEYS';
import Icon from '../Icon';
import * as Expensicons from '../Icon/Expensicons';
import variables from '../../styles/variables';
Expand All @@ -28,19 +31,27 @@ const propTypes = {
};

function MoneyReportView(props) {
const formattedAmount = CurrencyUtils.convertToDisplayString(ReportUtils.getMoneyRequestTotal(props.report), props.report.currency);
const isSettled = ReportUtils.isSettled(props.report.reportID);
const {translate} = useLocalize();
const isSettled = ReportUtils.isSettled(props.report.reportID);

const {totalDisplaySpend, nonReimbursableSpend, reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(props.report);

const shouldShowBreakdown = nonReimbursableSpend && reimbursableSpend;
const formattedTotalAmount = CurrencyUtils.convertToDisplayString(totalDisplaySpend, props.report.currency);
const formattedOutOfPocketAmount = CurrencyUtils.convertToDisplayString(reimbursableSpend, props.report.currency);
const formattedCompanySpendAmount = CurrencyUtils.convertToDisplayString(nonReimbursableSpend, props.report.currency);

const subAmountTextStyles = [styles.taskTitleMenuItem, styles.alignSelfCenter, StyleUtils.getFontSizeStyle(variables.fontSizeh1), StyleUtils.getColorStyle(themeColors.textSupporting)];

return (
<View>
<View style={[StyleUtils.getReportWelcomeContainerStyle(props.isSmallScreenWidth), StyleUtils.getMinimumHeight(CONST.EMPTY_STATE_BACKGROUND.MONEY_REPORT.MIN_HEIGHT)]}>
<AnimatedEmptyStateBackground />
</View>
<View style={[styles.flexRow, styles.menuItemTextContainer, styles.pointerEventsNone, styles.containerWithSpaceBetween, styles.ph5, styles.pv2]}>
<View style={[styles.flexRow, styles.pointerEventsNone, styles.containerWithSpaceBetween, styles.ph5, styles.pv2]}>
grgia marked this conversation as resolved.
Show resolved Hide resolved
<View style={[styles.flex1, styles.justifyContentCenter]}>
<Text
style={[styles.textLabelSupporting, StyleUtils.getFontSizeStyle(variables.fontSizeNormal)]}
style={[styles.textLabelSupporting]}
numberOfLines={1}
>
{translate('common.total')}
Expand All @@ -59,10 +70,50 @@ function MoneyReportView(props) {
numberOfLines={1}
style={[styles.taskTitleMenuItem, styles.alignSelfCenter]}
>
{formattedAmount}
{formattedTotalAmount}
</Text>
</View>
</View>
{shouldShowBreakdown ? (
<>
<View style={[styles.flexRow, styles.pointerEventsNone, styles.containerWithSpaceBetween, styles.ph5, styles.pv1]}>
<View style={[styles.flex1, styles.justifyContentCenter]}>
<Text
style={[styles.textLabelSupporting]}
numberOfLines={1}
>
{translate('cardTransactions.outOfPocket')}
</Text>
</View>
<View style={[styles.flexRow, styles.justifyContentCenter]}>
<Text
numberOfLines={1}
style={subAmountTextStyles}
>
{formattedOutOfPocketAmount}
</Text>
</View>
</View>
<View style={[styles.flexRow, styles.pointerEventsNone, styles.containerWithSpaceBetween, styles.ph5, styles.pv1]}>
<View style={[styles.flex1, styles.justifyContentCenter]}>
<Text
style={[styles.textLabelSupporting, StyleUtils.getColorStyle(themeColors.textSupporting)]}
grgia marked this conversation as resolved.
Show resolved Hide resolved
numberOfLines={1}
>
{translate('cardTransactions.companySpend')}
</Text>
</View>
<View style={[styles.flexRow, styles.justifyContentCenter]}>
<Text
numberOfLines={1}
style={subAmountTextStyles}
>
{formattedCompanySpendAmount}
</Text>
</View>
</View>
</>
) : undefined}
<SpacerView
shouldShow={props.shouldShowHorizontalRule}
style={[props.shouldShowHorizontalRule ? styles.reportHorizontalRule : {}]}
Expand All @@ -74,4 +125,11 @@ function MoneyReportView(props) {
MoneyReportView.propTypes = propTypes;
MoneyReportView.displayName = 'MoneyReportView';

export default withWindowDimensions(MoneyReportView);
export default compose(
withWindowDimensions,
withOnyx({
report: {
key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`,
},
}),
grgia marked this conversation as resolved.
Show resolved Hide resolved
)(MoneyReportView);
10 changes: 5 additions & 5 deletions src/components/ReportActionItem/ReportPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ function ReportPreview(props) {

const managerID = props.iouReport.managerID || 0;
const isCurrentUserManager = managerID === lodashGet(props.session, 'accountID');
const reportTotal = ReportUtils.getMoneyRequestTotal(props.iouReport);
const {totalDisplaySpend, reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(props.iouReport);

const iouSettled = ReportUtils.isSettled(props.iouReportID);
const iouCanceled = ReportUtils.isArchivedRoom(props.chatReport);
Expand All @@ -136,11 +136,11 @@ function ReportPreview(props) {
scanningReceipts: numberOfScanningReceipts,
});

const shouldShowSubmitButton = isReportDraft && reportTotal !== 0;
const shouldShowSubmitButton = isReportDraft && reimbursableSpend !== 0;

const getDisplayAmount = () => {
if (reportTotal) {
return CurrencyUtils.convertToDisplayString(reportTotal, props.iouReport.currency);
if (totalDisplaySpend) {
return CurrencyUtils.convertToDisplayString(totalDisplaySpend, props.iouReport.currency);
}
if (isScanning) {
return props.translate('iou.receiptScanning');
Expand Down Expand Up @@ -172,7 +172,7 @@ function ReportPreview(props) {
const bankAccountRoute = ReportUtils.getBankAccountRoute(props.chatReport);
const shouldShowSettlementButton = ReportUtils.isControlPolicyExpenseChat(props.chatReport)
? props.policy.role === CONST.POLICY.ROLE.ADMIN && ReportUtils.isReportApproved(props.iouReport) && !iouSettled && !iouCanceled
: !_.isEmpty(props.iouReport) && isCurrentUserManager && !isReportDraft && !iouSettled && !iouCanceled && !props.iouReport.isWaitingOnBankAccount && reportTotal !== 0;
: !_.isEmpty(props.iouReport) && isCurrentUserManager && !isReportDraft && !iouSettled && !iouCanceled && !props.iouReport.isWaitingOnBankAccount && reimbursableSpend !== 0;

return (
<View style={[styles.chatItemMessage, ...props.containerStyles]}>
Expand Down
2 changes: 1 addition & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1835,7 +1835,7 @@ export default {
},
cardTransactions: {
notActivated: 'Not activated',
outOfPocketSpend: 'Out-of-pocket spend',
outOfPocket: 'Out of pocket',
companySpend: 'Company spend',
},
distance: {
Expand Down
2 changes: 1 addition & 1 deletion src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2320,7 +2320,7 @@ export default {
},
cardTransactions: {
notActivated: 'No activado',
outOfPocketSpend: 'Gastos por cuenta propia',
outOfPocket: 'Por cuenta propia',
companySpend: 'Gastos de empresa',
},
distance: {
Expand Down
2 changes: 1 addition & 1 deletion src/libs/OptionsListUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ function createOption(accountIDs, personalDetails, report, reportActions = {}, {
}

result.isIOUReportOwner = ReportUtils.isIOUOwnedByCurrentUser(result);
result.iouReportAmount = ReportUtils.getMoneyRequestTotal(result);
result.iouReportAmount = ReportUtils.getMoneyRequestReimbursableTotal(result);

if (!hasMultipleParticipants) {
result.login = personalDetail.login;
Expand Down
51 changes: 45 additions & 6 deletions src/libs/ReportUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -1272,7 +1272,7 @@ function isWaitingForTaskCompleteFromAssignee(report, parentReportAction = {}) {
* @param {Object} allReportsDict
* @returns {Number}
*/
function getMoneyRequestTotal(report, allReportsDict = null) {
function getMoneyRequestReimbursableTotal(report, allReportsDict = null) {
const allAvailableReports = allReportsDict || allReports;
let moneyRequestReport;
if (isMoneyRequestReport(report)) {
Expand All @@ -1283,7 +1283,6 @@ function getMoneyRequestTotal(report, allReportsDict = null) {
}
if (moneyRequestReport) {
const total = lodashGet(moneyRequestReport, 'total', 0);

if (total !== 0) {
// There is a possibility that if the Expense report has a negative total.
// This is because there are instances where you can get a credit back on your card,
Expand All @@ -1294,6 +1293,45 @@ function getMoneyRequestTotal(report, allReportsDict = null) {
return 0;
}

/**
* @param {Object} report
* @param {Object} allReportsDict
* @returns {Number}
grgia marked this conversation as resolved.
Show resolved Hide resolved
*/
function getMoneyRequestSpendBreakdown(report, allReportsDict = null) {
const allAvailableReports = allReportsDict || allReports;
let moneyRequestReport;
if (isMoneyRequestReport(report)) {
moneyRequestReport = report;
}
if (allAvailableReports && report.hasOutstandingIOU && report.iouReportID) {
moneyRequestReport = allAvailableReports[`${ONYXKEYS.COLLECTION.REPORT}${report.iouReportID}`];
}
if (moneyRequestReport) {
let nonReimbursableSpend = lodashGet(moneyRequestReport, 'nonReimbursableTotal', 0);
let reimbursableSpend = lodashGet(moneyRequestReport, 'total', 0);

if (nonReimbursableSpend + reimbursableSpend !== 0) {
nonReimbursableSpend = isExpenseReport(moneyRequestReport) ? nonReimbursableSpend * -1 : Math.abs(nonReimbursableSpend);
reimbursableSpend = isExpenseReport(moneyRequestReport) ? reimbursableSpend * -1 : Math.abs(reimbursableSpend);
const totalDisplaySpend = nonReimbursableSpend + reimbursableSpend;
// There is a possibility that if the Expense report has a negative total.
// This is because there are instances where you can get a credit back on your card,
// or you enter a negative expense to “offset” future expenses
grgia marked this conversation as resolved.
Show resolved Hide resolved
return {
nonReimbursableSpend,
reimbursableSpend,
totalDisplaySpend,
};
}
}
return {
nonReimbursableSpend: 0,
reimbursableSpend: 0,
totalDisplaySpend: 0,
};
}

/**
* Get the title for a policy expense chat which depends on the role of the policy member seeing this report
*
Expand Down Expand Up @@ -1333,7 +1371,7 @@ function getPolicyExpenseChatName(report, policy = undefined) {
* @returns {String}
*/
function getMoneyRequestReportName(report, policy = undefined) {
const formattedAmount = CurrencyUtils.convertToDisplayString(getMoneyRequestTotal(report), report.currency);
const formattedAmount = CurrencyUtils.convertToDisplayString(getMoneyRequestReimbursableTotal(report), report.currency);
const payerName = isExpenseReport(report) ? getPolicyName(report, false, policy) : getDisplayNameForParticipant(report.managerID);
const payerPaidAmountMesssage = Localize.translateLocal('iou.payerPaidAmount', {
payer: payerName,
Expand Down Expand Up @@ -1530,7 +1568,7 @@ function getReportPreviewMessage(report, reportAction = {}, shouldConsiderReceip
return Localize.translateLocal('iou.didSplitAmount', {formattedAmount, comment});
}

const totalAmount = getMoneyRequestTotal(report);
const totalAmount = getMoneyRequestReimbursableTotal(report);
const payerName = isExpenseReport(report) ? getPolicyName(report) : getDisplayNameForParticipant(report.managerID, true);
const formattedAmount = CurrencyUtils.convertToDisplayString(totalAmount, report.currency);

Expand Down Expand Up @@ -2198,7 +2236,7 @@ function buildOptimisticExpenseReport(chatReportID, policyID, payeeAccountID, to
function getIOUReportActionMessage(iouReportID, type, total, comment, currency, paymentType = '', isSettlingUp = false) {
const amount =
type === CONST.IOU.REPORT_ACTION_TYPE.PAY
? CurrencyUtils.convertToDisplayString(getMoneyRequestTotal(getReport(iouReportID)), currency)
? CurrencyUtils.convertToDisplayString(getMoneyRequestReimbursableTotal(getReport(iouReportID)), currency)
: CurrencyUtils.convertToDisplayString(total, currency);

let paymentMethodMessage;
Expand Down Expand Up @@ -3875,7 +3913,8 @@ export {
hasExpensifyGuidesEmails,
isWaitingForIOUActionFromCurrentUser,
isIOUOwnedByCurrentUser,
getMoneyRequestTotal,
getMoneyRequestReimbursableTotal,
getMoneyRequestSpendBreakdown,
canShowReportRecipientLocalTime,
formatReportLastMessageText,
chatIncludesConcierge,
Expand Down
4 changes: 2 additions & 2 deletions src/libs/SidebarUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ function getOrderedReportIDs(currentReportId, allReportsDict, betas, policies, p
report.displayName = ReportUtils.getReportName(report);

// eslint-disable-next-line no-param-reassign
report.iouReportAmount = ReportUtils.getMoneyRequestTotal(report, allReportsDict);
report.iouReportAmount = ReportUtils.getMoneyRequestReimbursableTotal(report, allReportsDict);
});

// The LHN is split into five distinct groups, and each group is sorted a little differently. The groups will ALWAYS be in this order:
Expand Down Expand Up @@ -384,7 +384,7 @@ function getOptionData(report, reportActions, personalDetails, preferredLocale,
}

result.isIOUReportOwner = ReportUtils.isIOUOwnedByCurrentUser(result);
result.iouReportAmount = ReportUtils.getMoneyRequestTotal(result);
result.iouReportAmount = ReportUtils.getMoneyRequestReimbursableTotal(result);

if (!hasMultipleParticipants) {
result.accountID = personalDetail.accountID;
Expand Down
Loading