Skip to content

Commit

Permalink
Merge pull request #29566 from Expensify/georgia-cardTransactions-Mon…
Browse files Browse the repository at this point in the history
…eyReportView

[Wave 8 - ECard Transactions] Update Expense Report Total Breakdown
  • Loading branch information
grgia authored Oct 17, 2023
2 parents 91e2120 + b1e0461 commit be44c93
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 26 deletions.
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
58 changes: 53 additions & 5 deletions src/components/ReportActionItem/MoneyReportView.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,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]}>
<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 +67,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]}
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 Down
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 @@ -176,7 +176,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 @@ -1852,7 +1852,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 @@ -2337,7 +2337,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 @@ -522,7 +522,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 @@ -1283,7 +1283,7 @@ function hasNonReimbursableTransactions(iouReportID) {
* @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 @@ -1294,7 +1294,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 @@ -1305,6 +1304,45 @@ function getMoneyRequestTotal(report, allReportsDict = null) {
return 0;
}

/**
* @param {Object} report
* @param {Object} allReportsDict
* @returns {Object}
*/
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) {
// 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
nonReimbursableSpend = isExpenseReport(moneyRequestReport) ? nonReimbursableSpend * -1 : Math.abs(nonReimbursableSpend);
reimbursableSpend = isExpenseReport(moneyRequestReport) ? reimbursableSpend * -1 : Math.abs(reimbursableSpend);
const totalDisplaySpend = nonReimbursableSpend + reimbursableSpend;
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 @@ -1344,7 +1382,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 @@ -1550,7 +1588,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 @@ -2219,7 +2257,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 @@ -3912,7 +3950,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
1 change: 1 addition & 0 deletions src/pages/home/report/ReportActionItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,7 @@ export default compose(
prevProps.report.managerEmail === nextProps.report.managerEmail &&
prevProps.shouldHideThreadDividerLine === nextProps.shouldHideThreadDividerLine &&
lodashGet(prevProps.report, 'total', 0) === lodashGet(nextProps.report, 'total', 0) &&
lodashGet(prevProps.report, 'nonReimbursableTotal', 0) === lodashGet(nextProps.report, 'nonReimbursableTotal', 0) &&
prevProps.linkedReportActionID === nextProps.linkedReportActionID,
),
);
4 changes: 4 additions & 0 deletions src/pages/home/report/ReportActionsView.js
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,10 @@ function arePropsEqual(oldProps, newProps) {
return false;
}

if (lodashGet(newProps, 'report.nonReimbursableTotal') !== lodashGet(oldProps, 'report.nonReimbursableTotal')) {
return false;
}

if (lodashGet(newProps, 'report.writeCapability') !== lodashGet(oldProps, 'report.writeCapability')) {
return false;
}
Expand Down
1 change: 1 addition & 0 deletions src/pages/home/sidebar/SidebarLinksData.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ const chatReportSelector = (report) =>
lastVisibleActionCreated: report.lastVisibleActionCreated,
iouReportID: report.iouReportID,
total: report.total,
nonReimbursableTotal: report.nonReimbursableTotal,
hasOutstandingIOU: report.hasOutstandingIOU,
isWaitingOnBankAccount: report.isWaitingOnBankAccount,
statusNum: report.statusNum,
Expand Down

0 comments on commit be44c93

Please sign in to comment.