Skip to content

Commit

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

[Wave 8 - ECard Transactions] Update Money Request View Fields to support Ecard transactions
  • Loading branch information
grgia authored Oct 13, 2023
2 parents 88b1885 + ca92928 commit 1e68e9e
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 29 deletions.
17 changes: 16 additions & 1 deletion src/components/MoneyRequestHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction,
}, [parentReportAction, setIsDeleteModalVisible]);

const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction);
const isPending = TransactionUtils.isPending(transaction);

const canModifyRequest = isActionOwner && !isSettled && !isApproved && !ReportActionsUtils.isDeletedAction(parentReportAction);

Expand Down Expand Up @@ -112,6 +113,7 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction,
<>
<View style={[styles.pl0]}>
<HeaderWithBackButton
shouldShowBorderBottom={!isScanning && !isPending}
shouldShowAvatarWithDisplay
shouldShowPinButton={false}
shouldShowThreeDotsButton
Expand All @@ -127,7 +129,20 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction,
shouldShowBackButton={isSmallScreenWidth}
onBackButtonPress={() => Navigation.goBack(ROUTES.HOME, false, true)}
/>
{isScanning && <MoneyRequestHeaderStatusBar />}
{isPending && (
<MoneyRequestHeaderStatusBar
title={translate('iou.pending')}
description={translate('iou.transactionPendingText')}
shouldShowBorderBottom={!isScanning}
/>
)}
{isScanning && (
<MoneyRequestHeaderStatusBar
title={translate('iou.receiptStatusTitle')}
description={translate('iou.receiptStatusText')}
shouldShowBorderBottom
/>
)}
</View>
<ConfirmModal
title={translate('iou.deleteRequest')}
Expand Down
23 changes: 17 additions & 6 deletions src/components/MoneyRequestHeaderStatusBar.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
import React from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import styles from '../styles/styles';
import useLocalize from '../hooks/useLocalize';
import Text from './Text';

function MoneyRequestHeaderStatusBar() {
const {translate} = useLocalize();
const propTypes = {
/** Title displayed in badge */
title: PropTypes.string.isRequired,

/** Banner Description */
description: PropTypes.string.isRequired,

/** Whether we show the border bottom */
shouldShowBorderBottom: PropTypes.bool.isRequired,
};

function MoneyRequestHeaderStatusBar({title, description, shouldShowBorderBottom}) {
const borderBottomStyle = shouldShowBorderBottom ? styles.borderBottom : {};
return (
<View style={[styles.dFlex, styles.flexRow, styles.alignItemsCenter, styles.overflowHidden, styles.ph5, styles.pb3, styles.borderBottom, styles.w100]}>
<View style={[styles.dFlex, styles.flexRow, styles.alignItemsCenter, styles.flexGrow1, styles.overflowHidden, styles.ph5, styles.pb3, borderBottomStyle]}>
<View style={[styles.moneyRequestHeaderStatusBarBadge]}>
<Text style={[styles.textStrong, styles.textLabel]}>{translate('iou.receiptStatusTitle')}</Text>
<Text style={[styles.textStrong, styles.textLabel]}>{title}</Text>
</View>
<View style={[styles.flexShrink1]}>
<Text style={[styles.textLabelSupporting]}>{translate('iou.receiptStatusText')}</Text>
<Text style={[styles.textLabelSupporting]}>{description}</Text>
</View>
</View>
);
}

MoneyRequestHeaderStatusBar.displayName = 'MoneyRequestHeaderStatusBar';
MoneyRequestHeaderStatusBar.propTypes = propTypes;

export default MoneyRequestHeaderStatusBar;
71 changes: 49 additions & 22 deletions src/components/ReportActionItem/MoneyRequestView.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import * as OptionsListUtils from '../../libs/OptionsListUtils';
import * as ReportActionsUtils from '../../libs/ReportActionsUtils';
import * as StyleUtils from '../../styles/StyleUtils';
import * as PolicyUtils from '../../libs/PolicyUtils';
import * as CardUtils from '../../libs/CardUtils';
import CONST from '../../CONST';
import * as Expensicons from '../Icon/Expensicons';
import iouReportPropTypes from '../../pages/iouReportPropTypes';
Expand Down Expand Up @@ -90,6 +91,9 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should
billable: transactionBillable,
category: transactionCategory,
tag: transactionTag,
originalAmount: transactionOriginalAmount,
originalCurrency: transactionOriginalCurrency,
cardID: transactionCardID,
} = ReportUtils.getTransactionDetails(transaction);
const isEmptyMerchant =
transactionMerchant === '' || transactionMerchant === CONST.TRANSACTION.UNKNOWN_MERCHANT || transactionMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT;
Expand All @@ -98,9 +102,12 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should
if (isDistanceRequest && !formattedTransactionAmount) {
formattedTransactionAmount = translate('common.tbd');
}
const formattedOriginalAmount = transactionOriginalAmount && transactionOriginalCurrency && CurrencyUtils.convertToDisplayString(transactionOriginalAmount, transactionOriginalCurrency);
const isExpensifyCardTransaction = TransactionUtils.isExpensifyCardTransaction(transaction);
const cardProgramName = isExpensifyCardTransaction ? CardUtils.getCardDescription(transactionCardID) : '';

const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID);
const canEdit = ReportUtils.canEditMoneyRequest(parentReportAction);
const canEdit = ReportUtils.canEditMoneyRequest(parentReportAction) && !isExpensifyCardTransaction;

// A flag for verifying that the current report is a sub-report of a workspace chat
const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)), [report]);
Expand All @@ -114,14 +121,24 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should
const shouldShowTag = isPolicyExpenseChat && Permissions.canUseTags(betas) && (transactionTag || OptionsListUtils.hasEnabledOptions(lodashValues(policyTagsList)));
const shouldShowBillable = isPolicyExpenseChat && Permissions.canUseTags(betas) && (transactionBillable || !lodashGet(policy, 'disabledFields.defaultBillable', true));

let description = `${translate('iou.amount')}`;
if (!isDistanceRequest) {
description += ` • ${translate('iou.cash')}`;
}
if (isSettled) {
description += ` • ${translate('iou.settledExpensify')}`;
} else if (report.isWaitingOnBankAccount) {
description += ` • ${translate('iou.pending')}`;
let amountDescription = `${translate('iou.amount')}`;

if (isExpensifyCardTransaction) {
if (formattedOriginalAmount) {
amountDescription += ` • ${translate('iou.original')} ${formattedOriginalAmount}`;
}
if (TransactionUtils.isPending(transaction)) {
amountDescription += ` • ${translate('iou.pending')}`;
}
} else {
if (!isDistanceRequest) {
amountDescription += ` • ${translate('iou.cash')}`;
}
if (isSettled) {
amountDescription += ` • ${translate('iou.settledExpensify')}`;
} else if (report.isWaitingOnBankAccount) {
amountDescription += ` • ${translate('iou.pending')}`;
}
}

// A temporary solution to hide the transaction detail
Expand Down Expand Up @@ -163,7 +180,7 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should
title={formattedTransactionAmount ? formattedTransactionAmount.toString() : ''}
shouldShowTitleIcon={isSettled}
titleIcon={Expensicons.Checkmark}
description={description}
description={amountDescription}
titleStyle={styles.newKansasLarge}
interactive={canEdit}
shouldShowRightIcon={canEdit}
Expand All @@ -185,18 +202,6 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should
numberOfLinesTitle={0}
/>
</OfflineWithFeedback>
<OfflineWithFeedback pendingAction={getPendingFieldAction('pendingFields.created')}>
<MenuItemWithTopDescription
description={translate('common.date')}
title={transactionDate}
interactive={canEdit}
shouldShowRightIcon={canEdit}
titleStyle={styles.flex1}
onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DATE))}
brickRoadIndicator={hasErrors && transactionDate === '' ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''}
error={hasErrors && transactionDate === '' ? translate('common.error.enterDate') : ''}
/>
</OfflineWithFeedback>
{isDistanceRequest ? (
<OfflineWithFeedback pendingAction={lodashGet(transaction, 'pendingFields.waypoints') || lodashGet(transaction, 'pendingAction')}>
<MenuItemWithTopDescription
Expand All @@ -222,6 +227,18 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should
/>
</OfflineWithFeedback>
)}
<OfflineWithFeedback pendingAction={getPendingFieldAction('pendingFields.created')}>
<MenuItemWithTopDescription
description={translate('common.date')}
title={transactionDate}
interactive={canEdit}
shouldShowRightIcon={canEdit}
titleStyle={styles.flex1}
onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.DATE))}
brickRoadIndicator={hasErrors && transactionDate === '' ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''}
error={hasErrors && transactionDate === '' ? translate('common.error.enterDate') : ''}
/>
</OfflineWithFeedback>
{shouldShowCategory && (
<OfflineWithFeedback pendingAction={lodashGet(transaction, 'pendingFields.category') || lodashGet(transaction, 'pendingAction')}>
<MenuItemWithTopDescription
Expand All @@ -246,6 +263,16 @@ function MoneyRequestView({report, betas, parentReport, policyCategories, should
/>
</OfflineWithFeedback>
)}
{isExpensifyCardTransaction ? (
<OfflineWithFeedback pendingAction={getPendingFieldAction('pendingFields.cardID')}>
<MenuItemWithTopDescription
description={translate('iou.card')}
title={cardProgramName}
titleStyle={styles.flex1}
interactive={canEdit}
/>
</OfflineWithFeedback>
) : null}
{shouldShowBillable && (
<View style={[styles.flexRow, styles.mb4, styles.justifyContentBetween, styles.alignItemsCenter, styles.ml5, styles.mr8]}>
<Text color={!transactionBillable ? themeColors.textSupporting : undefined}>{translate('common.billable')}</Text>
Expand Down
2 changes: 2 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ export default {
approved: 'Approved',
cash: 'Cash',
card: 'Card',
original: 'Original',
split: 'Split',
addToSplit: 'Add to split',
splitBill: 'Split bill',
Expand All @@ -535,6 +536,7 @@ export default {
receiptStatusTitle: 'Scanning…',
receiptStatusText: "Only you can see this receipt when it's scanning. Check back later or enter the details now.",
receiptScanningFailed: 'Receipt scanning failed. Enter the details manually.',
transactionPendingText: 'It takes a few days from the date the card was used for the transaction to post.',
requestCount: ({count, scanningReceipts = 0}: RequestCountParams) => `${count} requests${scanningReceipts > 0 ? `, ${scanningReceipts} scanning` : ''}`,
deleteRequest: 'Delete request',
deleteConfirmation: 'Are you sure that you want to delete this request?',
Expand Down
2 changes: 2 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@ export default {
approved: 'Aprobado',
cash: 'Efectivo',
card: 'Tarjeta',
original: 'Original',
split: 'Dividir',
addToSplit: 'Añadir para dividir',
splitBill: 'Dividir factura',
Expand All @@ -527,6 +528,7 @@ export default {
receiptStatusTitle: 'Escaneando…',
receiptStatusText: 'Solo tú puedes ver este recibo cuando se está escaneando. Vuelve más tarde o introduce los detalles ahora.',
receiptScanningFailed: 'El escaneo de recibo ha fallado. Introduce los detalles manualmente.',
transactionPendingText: 'La transacción tarda unos días en contabilizarse desde la fecha en que se utilizó la tarjeta.',
requestCount: ({count, scanningReceipts = 0}: RequestCountParams) => `${count} solicitudes${scanningReceipts > 0 ? `, ${scanningReceipts} escaneando` : ''}`,
deleteRequest: 'Eliminar pedido',
deleteConfirmation: '¿Estás seguro de que quieres eliminar este pedido?',
Expand Down
2 changes: 2 additions & 0 deletions src/libs/ReportUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -1373,6 +1373,8 @@ function getTransactionDetails(transaction, createdDateFormat = CONST.DATE.FNS_F
tag: TransactionUtils.getTag(transaction),
mccGroup: TransactionUtils.getMCCGroup(transaction),
cardID: TransactionUtils.getCardID(transaction),
originalAmount: TransactionUtils.getOriginalAmount(transaction),
originalCurrency: TransactionUtils.getOriginalCurrency(transaction),
};
}

Expand Down
26 changes: 26 additions & 0 deletions src/libs/TransactionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,21 @@ function getCurrency(transaction: Transaction): string {
return transaction?.currency ?? CONST.CURRENCY.USD;
}

/**
* Return the original currency field from the transaction.
*/
function getOriginalCurrency(transaction: Transaction): string {
return transaction?.originalCurrency ?? '';
}

/**
* Return the absolute value of the original amount field from the transaction.
*/
function getOriginalAmount(transaction: Transaction): number {
const amount = transaction?.originalAmount ?? 0;
return Math.abs(amount);
}

/**
* Return the merchant field from the transaction, return the modifiedMerchant if present.
*/
Expand Down Expand Up @@ -293,20 +308,29 @@ function isDistanceRequest(transaction: Transaction): boolean {
return type === CONST.TRANSACTION.TYPE.CUSTOM_UNIT && customUnitName === CONST.CUSTOM_UNITS.NAME_DISTANCE;
}

/**
* Determine whether a transaction is made with an Expensify card.
*/
function isExpensifyCardTransaction(transaction: Transaction): boolean {
if (!transaction.cardID) {
return false;
}
return isExpensifyCard(transaction.cardID);
}

/**
* Check if the transaction status is set to Pending.
*/
function isPending(transaction: Transaction): boolean {
if (!transaction.status) {
return false;
}
return transaction.status === CONST.TRANSACTION.STATUS.PENDING;
}

/**
* Check if the transaction status is set to Posted.
*/
function isPosted(transaction: Transaction): boolean {
if (!transaction.status) {
return false;
Expand Down Expand Up @@ -428,6 +452,8 @@ export {
getAmount,
getCurrency,
getCardID,
getOriginalCurrency,
getOriginalAmount,
getMerchant,
getMCCGroup,
getCreated,
Expand Down

0 comments on commit 1e68e9e

Please sign in to comment.