diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js
index 63181e4aea87..b75f4e2df845 100755
--- a/src/components/MoneyRequestConfirmationList.js
+++ b/src/components/MoneyRequestConfirmationList.js
@@ -267,6 +267,9 @@ function MoneyRequestConfirmationList(props) {
return (props.hasSmartScanFailed && TransactionUtils.hasMissingSmartscanFields(transaction)) || (didConfirmSplit && TransactionUtils.areRequiredFieldsEmpty(transaction));
}, [props.isEditingSplitBill, props.hasSmartScanFailed, transaction, didConfirmSplit]);
+ const isMerchantEmpty = !props.iouMerchant || props.iouMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT;
+ const shouldDisplayMerchantError = props.isPolicyExpenseChat && !props.isScanRequest && isMerchantEmpty;
+
useEffect(() => {
if (shouldDisplayFieldError && props.hasSmartScanFailed) {
setFormError('iou.receiptScanningFailed');
@@ -500,7 +503,7 @@ function MoneyRequestConfirmationList(props) {
}
const shouldShowSettlementButton = props.iouType === CONST.IOU.TYPE.SEND;
- const shouldDisableButton = selectedParticipants.length === 0;
+ const shouldDisableButton = selectedParticipants.length === 0 || shouldDisplayMerchantError;
const button = shouldShowSettlementButton ? (
)}
{shouldShowCategories && (
diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js
index 20012bc90ef0..dab34e324ffa 100755
--- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js
+++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js
@@ -97,6 +97,9 @@ const propTypes = {
/** Should the list be read only, and not editable? */
isReadOnly: PropTypes.bool,
+ /** Whether the money request is a scan request */
+ isScanRequest: PropTypes.bool,
+
/** Depending on expense report or personal IOU report, respective bank account route */
bankAccountRoute: PropTypes.string,
@@ -211,6 +214,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({
isEditingSplitBill,
isPolicyExpenseChat,
isReadOnly,
+ isScanRequest,
listStyles,
mileageRate,
onConfirm,
@@ -281,6 +285,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({
const [didConfirm, setDidConfirm] = useState(false);
const [didConfirmSplit, setDidConfirmSplit] = useState(false);
+ const [merchantError, setMerchantError] = useState(false);
+
const shouldDisplayFieldError = useMemo(() => {
if (!isEditingSplitBill) {
return false;
@@ -289,6 +295,21 @@ function MoneyTemporaryForRefactorRequestConfirmationList({
return (hasSmartScanFailed && TransactionUtils.hasMissingSmartscanFields(transaction)) || (didConfirmSplit && TransactionUtils.areRequiredFieldsEmpty(transaction));
}, [isEditingSplitBill, hasSmartScanFailed, transaction, didConfirmSplit]);
+ const isMerchantEmpty = !iouMerchant || iouMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT;
+ const isMerchantRequired = isPolicyExpenseChat && !isScanRequest && shouldShowMerchant;
+
+ useEffect(() => {
+ if ((!isMerchantRequired && isMerchantEmpty) || !merchantError) {
+ return;
+ }
+ if (!isMerchantEmpty && merchantError) {
+ setMerchantError(false);
+ if (formError === 'iou.error.invalidMerchant') {
+ setFormError('');
+ }
+ }
+ }, [formError, isMerchantEmpty, merchantError, isMerchantRequired]);
+
useEffect(() => {
if (shouldDisplayFieldError && hasSmartScanFailed) {
setFormError('iou.receiptScanningFailed');
@@ -298,9 +319,13 @@ function MoneyTemporaryForRefactorRequestConfirmationList({
setFormError('iou.error.genericSmartscanFailureMessage');
return;
}
+ if (merchantError) {
+ setFormError('iou.error.invalidMerchant');
+ return;
+ }
// reset the form error whenever the screen gains or loses focus
setFormError('');
- }, [isFocused, transaction, shouldDisplayFieldError, hasSmartScanFailed, didConfirmSplit]);
+ }, [isFocused, transaction, shouldDisplayFieldError, hasSmartScanFailed, didConfirmSplit, isMerchantRequired, merchantError]);
useEffect(() => {
if (!shouldCalculateDistanceAmount) {
@@ -470,6 +495,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({
if (_.isEmpty(selectedParticipants)) {
return;
}
+ if ((isMerchantRequired && isMerchantEmpty) || (shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction))) {
+ setMerchantError(true);
+ return;
+ }
if (iouType === CONST.IOU.TYPE.SEND) {
if (!paymentMethod) {
@@ -498,7 +527,21 @@ function MoneyTemporaryForRefactorRequestConfirmationList({
onConfirm(selectedParticipants);
}
},
- [selectedParticipants, onSendMoney, onConfirm, isEditingSplitBill, iouType, isDistanceRequest, isDistanceRequestWithoutRoute, iouCurrencyCode, iouAmount, transaction],
+ [
+ selectedParticipants,
+ isMerchantRequired,
+ isMerchantEmpty,
+ shouldDisplayFieldError,
+ transaction,
+ iouType,
+ onSendMoney,
+ iouCurrencyCode,
+ isDistanceRequest,
+ isDistanceRequestWithoutRoute,
+ iouAmount,
+ isEditingSplitBill,
+ onConfirm,
+ ],
);
const footerContent = useMemo(() => {
@@ -551,7 +594,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({
{button}
>
);
- }, [confirm, bankAccountRoute, iouCurrencyCode, iouType, isReadOnly, policyID, selectedParticipants, splitOrRequestOptions, translate, formError, styles.ph1, styles.mb2]);
+ }, [isReadOnly, iouType, selectedParticipants.length, confirm, bankAccountRoute, iouCurrencyCode, policyID, splitOrRequestOptions, formError, styles.ph1, styles.mb2, translate]);
const {image: receiptImage, thumbnail: receiptThumbnail} = receiptPath && receiptFilename ? ReceiptUtils.getThumbnailAndImageURIs(transaction, receiptPath, receiptFilename) : {};
return (
@@ -629,6 +672,26 @@ function MoneyTemporaryForRefactorRequestConfirmationList({
interactive={!isReadOnly}
numberOfLinesTitle={2}
/>
+ {isMerchantRequired && (
+ {
+ if (isEditingSplitBill) {
+ Navigation.navigate(ROUTES.EDIT_SPLIT_BILL.getRoute(reportID, reportActionID, CONST.EDIT_REQUEST_FIELD.MERCHANT));
+ return;
+ }
+ Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_MERCHANT.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams()));
+ }}
+ disabled={didConfirm}
+ interactive={!isReadOnly}
+ brickRoadIndicator={merchantError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''}
+ error={merchantError ? translate('common.error.fieldRequired') : ''}
+ />
+ )}
{!shouldShowAllFields && (
@@ -680,10 +743,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({
interactive={!isReadOnly}
/>
)}
- {shouldShowMerchant && (
+ {!isMerchantRequired && shouldShowMerchant && (
)}
{shouldShowCategories && (
diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js
index 47031bfc164c..514dc71ffe2c 100644
--- a/src/components/ReportActionItem/MoneyRequestView.js
+++ b/src/components/ReportActionItem/MoneyRequestView.js
@@ -240,7 +240,7 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate
`Started settling up, payment is held until ${submitterDisplayName} enables their Wallet`,
enableWallet: 'Enable Wallet',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 9b424fc48793..8f44c2a24274 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -613,6 +613,7 @@ export default {
genericSmartscanFailureMessage: 'La transacción tiene campos vacíos',
atLeastTwoDifferentWaypoints: 'Por favor introduce al menos dos direcciones diferentes',
splitBillMultipleParticipantsErrorMessage: 'Solo puedes dividir una cuenta entre un único espacio de trabajo o con usuarios individuales. Por favor actualiza tu selección.',
+ invalidMerchant: 'Por favor ingrese un comerciante correcto.',
},
waitingOnEnabledWallet: ({submitterDisplayName}: WaitingOnBankAccountParams) => `Inició el pago, pero no se procesará hasta que ${submitterDisplayName} active su Billetera`,
enableWallet: 'Habilitar Billetera',
diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts
index 6905a542fa5b..75815a2448e4 100644
--- a/src/libs/TransactionUtils.ts
+++ b/src/libs/TransactionUtils.ts
@@ -107,7 +107,7 @@ function buildOptimisticTransaction(
currency,
reportID,
comment: commentJSON,
- merchant: merchant || CONST.TRANSACTION.DEFAULT_MERCHANT,
+ merchant: merchant || CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT,
created: created || DateUtils.getDBTime(),
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
receipt,
diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js
index d43fefca20bc..3355597e8da6 100644
--- a/src/libs/actions/IOU.js
+++ b/src/libs/actions/IOU.js
@@ -160,6 +160,7 @@ function startMoneyRequest_temporaryForRefactor(reportID, isFromGlobalCreate, io
reportID,
transactionID: newTransactionID,
isFromGlobalCreate,
+ merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT,
});
}
@@ -278,7 +279,7 @@ function resetMoneyRequestInfo(id = '') {
currency: lodashGet(currentUserPersonalDetails, 'localCurrencyCode', CONST.CURRENCY.USD),
comment: '',
participants: [],
- merchant: CONST.TRANSACTION.DEFAULT_MERCHANT,
+ merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT,
category: '',
tag: '',
created,
diff --git a/src/pages/EditRequestMerchantPage.js b/src/pages/EditRequestMerchantPage.js
index 5fa14d850f45..c8766d9acc67 100644
--- a/src/pages/EditRequestMerchantPage.js
+++ b/src/pages/EditRequestMerchantPage.js
@@ -18,22 +18,27 @@ const propTypes = {
/** Callback to fire when the Save button is pressed */
onSubmit: PropTypes.func.isRequired,
+
+ /** Boolean to enable validation */
+ isPolicyExpenseChat: PropTypes.bool.isRequired,
};
-function EditRequestMerchantPage({defaultMerchant, onSubmit}) {
+function EditRequestMerchantPage({defaultMerchant, onSubmit, isPolicyExpenseChat}) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const merchantInputRef = useRef(null);
+ const isEmptyMerchant = defaultMerchant === '' || defaultMerchant === CONST.TRANSACTION.UNKNOWN_MERCHANT || defaultMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT;
- const validate = useCallback((value) => {
- const errors = {};
-
- if (_.isEmpty(value.merchant)) {
- errors.merchant = 'common.error.fieldRequired';
- }
-
- return errors;
- }, []);
+ const validate = useCallback(
+ (value) => {
+ const errors = {};
+ if (_.isEmpty(value.merchant) && value.merchant.trim() === '' && isPolicyExpenseChat) {
+ errors.merchant = 'common.error.fieldRequired';
+ }
+ return errors;
+ },
+ [isPolicyExpenseChat],
+ );
return (
{
// In case the merchant hasn't been changed, do not make the API request.
if (transactionChanges.merchant.trim() === transactionMerchant) {
Navigation.dismissModal();
return;
}
+ // This is possible only in case of IOU requests.
+ if (transactionChanges.merchant.trim() === '') {
+ editMoneyRequest({merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT});
+ return;
+ }
editMoneyRequest({merchant: transactionChanges.merchant.trim()});
}}
/>
diff --git a/src/pages/iou/MoneyRequestMerchantPage.js b/src/pages/iou/MoneyRequestMerchantPage.js
index bf799cd0957b..ce96a09446b9 100644
--- a/src/pages/iou/MoneyRequestMerchantPage.js
+++ b/src/pages/iou/MoneyRequestMerchantPage.js
@@ -53,6 +53,7 @@ function MoneyRequestMerchantPage({iou, route}) {
const {inputCallbackRef} = useAutoFocusInput();
const iouType = lodashGet(route, 'params.iouType', '');
const reportID = lodashGet(route, 'params.reportID', '');
+ const isEmptyMerchant = iou.merchant === '' || iou.merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT;
useEffect(() => {
const moneyRequestId = `${iouType}${reportID}`;
@@ -114,7 +115,7 @@ function MoneyRequestMerchantPage({iou, route}) {
InputComponent={TextInput}
inputID="moneyRequestMerchant"
name="moneyRequestMerchant"
- defaultValue={iou.merchant}
+ defaultValue={isEmptyMerchant ? '' : iou.merchant}
maxLength={CONST.MERCHANT_NAME_MAX_LENGTH}
label={translate('common.merchant')}
accessibilityLabel={translate('common.merchant')}
diff --git a/src/pages/iou/request/step/IOURequestStepMerchant.js b/src/pages/iou/request/step/IOURequestStepMerchant.js
index 3234b6046f31..355bb76b89b0 100644
--- a/src/pages/iou/request/step/IOURequestStepMerchant.js
+++ b/src/pages/iou/request/step/IOURequestStepMerchant.js
@@ -41,6 +41,7 @@ function IOURequestStepMerchant({
const styles = useThemeStyles();
const {translate} = useLocalize();
const {inputCallbackRef} = useAutoFocusInput();
+ const isEmptyMerchant = merchant === '' || merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT;
const navigateBack = () => {
Navigation.goBack(backTo || ROUTES.HOME);
@@ -89,7 +90,7 @@ function IOURequestStepMerchant({
InputComponent={TextInput}
inputID="moneyRequestMerchant"
name="moneyRequestMerchant"
- defaultValue={merchant}
+ defaultValue={isEmptyMerchant ? '' : merchant}
maxLength={CONST.MERCHANT_NAME_MAX_LENGTH}
label={translate('common.merchant')}
accessibilityLabel={translate('common.merchant')}
diff --git a/tests/actions/IOUTest.js b/tests/actions/IOUTest.js
index bb7a7c3424d2..4d9ce42a08ce 100644
--- a/tests/actions/IOUTest.js
+++ b/tests/actions/IOUTest.js
@@ -313,7 +313,7 @@ describe('actions/IOU', () => {
// The comment should be correct
expect(transaction.comment.comment).toBe(comment);
- expect(transaction.merchant).toBe(CONST.TRANSACTION.DEFAULT_MERCHANT);
+ expect(transaction.merchant).toBe(CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT);
// It should be pending
expect(transaction.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD);
@@ -497,7 +497,7 @@ describe('actions/IOU', () => {
expect(newTransaction.reportID).toBe(iouReportID);
expect(newTransaction.amount).toBe(amount);
expect(newTransaction.comment.comment).toBe(comment);
- expect(newTransaction.merchant).toBe(CONST.TRANSACTION.DEFAULT_MERCHANT);
+ expect(newTransaction.merchant).toBe(CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT);
expect(newTransaction.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD);
// The transactionID on the iou action should match the one from the transactions collection
@@ -642,7 +642,7 @@ describe('actions/IOU', () => {
expect(transaction.reportID).toBe(iouReportID);
expect(transaction.amount).toBe(amount);
expect(transaction.comment.comment).toBe(comment);
- expect(transaction.merchant).toBe(CONST.TRANSACTION.DEFAULT_MERCHANT);
+ expect(transaction.merchant).toBe(CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT);
expect(transaction.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD);
// The transactionID on the iou action should match the one from the transactions collection