diff --git a/src/libs/ValidationUtils.js b/src/libs/ValidationUtils.js index a15fd74a8815..81b91e2101be 100644 --- a/src/libs/ValidationUtils.js +++ b/src/libs/ValidationUtils.js @@ -95,6 +95,24 @@ function isRequiredFulfilled(value) { return Boolean(value); } +/** + * Used to add requiredField error to the fields passed. + * + * @param {Object} values + * @param {Array} requiredFields + * @returns {Object} + */ +function getFieldRequiredErrors(values, requiredFields) { + const errors = {}; + _.each(requiredFields, (fieldKey) => { + if (isRequiredFulfilled(values[fieldKey])) { + return; + } + errors[fieldKey] = 'common.error.fieldRequired'; + }); + return errors; +} + /** * Validates that this is a valid expiration date. Supports the following formats: * 1. MM/YY @@ -451,6 +469,7 @@ export { isValidIndustryCode, isValidZipCode, isRequiredFulfilled, + getFieldRequiredErrors, isValidUSPhone, isValidWebsite, validateIdentity, diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.js b/src/pages/EnablePayments/AdditionalDetailsStep.js index cdf2ad16ac1b..9804edd63fcd 100644 --- a/src/pages/EnablePayments/AdditionalDetailsStep.js +++ b/src/pages/EnablePayments/AdditionalDetailsStep.js @@ -18,7 +18,6 @@ import TextLink from '../../components/TextLink'; import TextInput from '../../components/TextInput'; import * as Wallet from '../../libs/actions/Wallet'; import * as ValidationUtils from '../../libs/ValidationUtils'; -import * as ErrorUtils from '../../libs/ErrorUtils'; import AddressForm from '../ReimbursementAccount/AddressForm'; import DatePicker from '../../components/DatePicker'; import Form from '../../components/Form'; @@ -70,29 +69,6 @@ const defaultProps = { ...withCurrentUserPersonalDetailsDefaultProps, }; -const INPUT_IDS = { - LEGAL_FIRST_NAME: 'legalFirstName', - LEGAL_LAST_NAME: 'legalLastName', - PHONE_NUMBER: 'phoneNumber', - DOB: 'dob', - SSN: 'ssn', - ADDRESS: { - street: 'addressStreet', - city: 'addressCity', - state: 'addressState', - zipCode: 'addressZip', - }, -}; -const errorTranslationKeys = { - legalFirstName: 'bankAccount.error.firstName', - legalLastName: 'bankAccount.error.lastName', - phoneNumber: 'bankAccount.error.phoneNumber', - dob: 'bankAccount.error.dob', - age: 'bankAccount.error.age', - ssn: 'bankAccount.error.ssnLast4', - ssnFull9: 'additionalDetailsStep.ssnFull9Error', -}; - const fieldNameTranslationKeys = { legalFirstName: 'additionalDetailsStep.legalFirstNameLabel', legalLastName: 'additionalDetailsStep.legalLastNameLabel', @@ -113,50 +89,37 @@ function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserP * @returns {Object} */ const validate = (values) => { - const errors = {}; - - if (_.isEmpty(values[INPUT_IDS.LEGAL_FIRST_NAME])) { - errors[INPUT_IDS.LEGAL_FIRST_NAME] = errorTranslationKeys.legalFirstName; - } - - if (_.isEmpty(values[INPUT_IDS.LEGAL_LAST_NAME])) { - errors[INPUT_IDS.LEGAL_LAST_NAME] = errorTranslationKeys.legalLastName; - } - - if (!ValidationUtils.isValidPastDate(values[INPUT_IDS.DOB]) || !ValidationUtils.meetsMaximumAgeRequirement(values[INPUT_IDS.DOB])) { - ErrorUtils.addErrorMessage(errors, INPUT_IDS.DOB, errorTranslationKeys.dob); - } else if (!ValidationUtils.meetsMinimumAgeRequirement(values[INPUT_IDS.DOB])) { - ErrorUtils.addErrorMessage(errors, INPUT_IDS.DOB, errorTranslationKeys.age); - } + const requiredFields = ['legalFirstName', 'legalLastName', 'addressStreet', 'addressCity', 'addressZipCode', 'phoneNumber', 'dob', 'ssn', 'addressState']; + const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields); - if (!ValidationUtils.isValidAddress(values[INPUT_IDS.ADDRESS.street]) || _.isEmpty(values[INPUT_IDS.ADDRESS.street])) { - errors[INPUT_IDS.ADDRESS.street] = 'bankAccount.error.addressStreet'; - } - - if (_.isEmpty(values[INPUT_IDS.ADDRESS.city])) { - errors[INPUT_IDS.ADDRESS.city] = 'bankAccount.error.addressCity'; + if (values.dob) { + if (!ValidationUtils.isValidPastDate(values.dob) || !ValidationUtils.meetsMaximumAgeRequirement(values.dob)) { + errors.dob = 'bankAccount.error.dob'; + } else if (!ValidationUtils.meetsMinimumAgeRequirement(values.dob)) { + errors.dob = 'bankAccount.error.age'; + } } - if (_.isEmpty(values[INPUT_IDS.ADDRESS.state])) { - errors[INPUT_IDS.ADDRESS.state] = 'bankAccount.error.addressState'; + if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { + errors.addressStreet = 'bankAccount.error.addressStreet'; } - if (!ValidationUtils.isValidZipCode(values[INPUT_IDS.ADDRESS.zipCode])) { - errors[INPUT_IDS.ADDRESS.zipCode] = 'bankAccount.error.zipCode'; + if (values.addressZipCode && !ValidationUtils.isValidZipCode(values.addressZipCode)) { + errors.addressZipCode = 'bankAccount.error.zipCode'; } - if (!ValidationUtils.isValidUSPhone(values[INPUT_IDS.PHONE_NUMBER], true)) { - errors[INPUT_IDS.PHONE_NUMBER] = errorTranslationKeys.phoneNumber; + if (values.phoneNumber && !ValidationUtils.isValidUSPhone(values.phoneNumber, true)) { + errors.phoneNumber = 'bankAccount.error.phoneNumber'; } // walletAdditionalDetails stores errors returned by the server. If the server returns an SSN error // then the user needs to provide the full 9 digit SSN. if (walletAdditionalDetails.errorCode === CONST.WALLET.ERROR.SSN) { - if (!ValidationUtils.isValidSSNFullNine(values[INPUT_IDS.SSN])) { - errors[INPUT_IDS.SSN] = errorTranslationKeys.ssnFull9; + if (values.ssn && !ValidationUtils.isValidSSNFullNine(values.ssn)) { + errors.ssn = 'additionalDetailsStep.ssnFull9Error'; } - } else if (!ValidationUtils.isValidSSNLastFour(values[INPUT_IDS.SSN])) { - errors[INPUT_IDS.SSN] = errorTranslationKeys.ssn; + } else if (values.ssn && !ValidationUtils.isValidSSNLastFour(values.ssn)) { + errors.ssn = 'bankAccount.error.ssnLast4'; } return errors; @@ -167,17 +130,16 @@ function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserP */ const activateWallet = (values) => { const personalDetails = { - phoneNumber: parsePhoneNumber(values[INPUT_IDS.PHONE_NUMBER], {regionCode: CONST.COUNTRY.US}).number.significant, - legalFirstName: values[INPUT_IDS.LEGAL_FIRST_NAME], - legalLastName: values[INPUT_IDS.LEGAL_LAST_NAME], - addressStreet: values[INPUT_IDS.ADDRESS.street], - addressCity: values[INPUT_IDS.ADDRESS.city], - addressState: values[INPUT_IDS.ADDRESS.state], - addressZip: values[INPUT_IDS.ADDRESS.zipCode], - dob: values[INPUT_IDS.DOB], - ssn: values[INPUT_IDS.SSN], + phoneNumber: parsePhoneNumber(values.phoneNumber, {regionCode: CONST.COUNTRY.US}).number.significant, + legalFirstName: values.legalFirstName, + legalLastName: values.legalLastName, + addressStreet: values.addressStreet, + addressCity: values.addressCity, + addressState: values.addressState, + addressZip: values.addressZipCode, + dob: values.dob, + ssn: values.ssn, }; - // Attempt to set the personal details Wallet.updatePersonalDetails(personalDetails); }; @@ -222,7 +184,7 @@ function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserP style={[styles.mh5, styles.flexGrow1]} > { - const errorFields = {}; + const requiredFields = ['routingNumber', 'accountNumber']; + const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields); const routingNumber = values.routingNumber && values.routingNumber.trim(); if ( - !values.accountNumber || - (!CONST.BANK_ACCOUNT.REGEX.US_ACCOUNT_NUMBER.test(values.accountNumber.trim()) && !CONST.BANK_ACCOUNT.REGEX.MASKED_US_ACCOUNT_NUMBER.test(values.accountNumber.trim())) + values.accountNumber && + !CONST.BANK_ACCOUNT.REGEX.US_ACCOUNT_NUMBER.test(values.accountNumber.trim()) && + !CONST.BANK_ACCOUNT.REGEX.MASKED_US_ACCOUNT_NUMBER.test(values.accountNumber.trim()) ) { - errorFields.accountNumber = 'bankAccount.error.accountNumber'; - } else if (values.accountNumber === routingNumber) { - errorFields.accountNumber = translate('bankAccount.error.routingAndAccountNumberCannotBeSame'); + errors.accountNumber = 'bankAccount.error.accountNumber'; + } else if (values.accountNumber && values.accountNumber === routingNumber) { + errors.accountNumber = translate('bankAccount.error.routingAndAccountNumberCannotBeSame'); } - if (!routingNumber || !CONST.BANK_ACCOUNT.REGEX.SWIFT_BIC.test(routingNumber) || !ValidationUtils.isValidRoutingNumber(routingNumber)) { - errorFields.routingNumber = 'bankAccount.error.routingNumber'; + if (routingNumber && (!CONST.BANK_ACCOUNT.REGEX.SWIFT_BIC.test(routingNumber) || !ValidationUtils.isValidRoutingNumber(routingNumber))) { + errors.routingNumber = 'bankAccount.error.routingNumber'; } if (!values.acceptTerms) { - errorFields.acceptTerms = 'common.error.acceptTerms'; + errors.acceptTerms = 'common.error.acceptTerms'; } - return errorFields; + return errors; }, [translate], ); diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index a57c80b0a7e2..3a963bc61295 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -80,54 +80,47 @@ class CompanyStep extends React.Component { * @returns {Object} - Object containing the errors for each inputID, e.g. {inputID1: error1, inputID2: error2} */ validate(values) { - const errors = {}; + const requiredFields = [ + 'companyName', + 'addressStreet', + 'addressZipCode', + 'addressCity', + 'addressState', + 'companyPhone', + 'website', + 'companyTaxID', + 'incorporationType', + 'incorporationDate', + 'incorporationState', + ]; + const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields); - if (!values.companyName) { - errors.companyName = 'bankAccount.error.companyName'; - } - - if (!values.addressStreet || !ValidationUtils.isValidAddress(values.addressStreet)) { + if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { errors.addressStreet = 'bankAccount.error.addressStreet'; } - if (!values.addressZipCode || !ValidationUtils.isValidZipCode(values.addressZipCode)) { + if (values.addressZipCode && !ValidationUtils.isValidZipCode(values.addressZipCode)) { errors.addressZipCode = 'bankAccount.error.zipCode'; } - if (!values.addressCity) { - errors.addressCity = 'bankAccount.error.addressCity'; - } - - if (!values.addressState) { - errors.addressState = 'bankAccount.error.addressState'; - } - - if (!values.companyPhone || !ValidationUtils.isValidUSPhone(values.companyPhone, true)) { + if (values.companyPhone && !ValidationUtils.isValidUSPhone(values.companyPhone, true)) { errors.companyPhone = 'bankAccount.error.phoneNumber'; } - if (!values.website || !ValidationUtils.isValidWebsite(values.website)) { + if (values.website && !ValidationUtils.isValidWebsite(values.website)) { errors.website = 'bankAccount.error.website'; } - if (!values.companyTaxID || !ValidationUtils.isValidTaxID(values.companyTaxID)) { + if (values.companyTaxID && !ValidationUtils.isValidTaxID(values.companyTaxID)) { errors.companyTaxID = 'bankAccount.error.taxID'; } - if (!values.incorporationType) { - errors.incorporationType = 'bankAccount.error.companyType'; - } - - if (!values.incorporationDate || !ValidationUtils.isValidDate(values.incorporationDate)) { + if (values.incorporationDate && !ValidationUtils.isValidDate(values.incorporationDate)) { errors.incorporationDate = 'common.error.dateInvalid'; - } else if (!values.incorporationDate || !ValidationUtils.isValidPastDate(values.incorporationDate)) { + } else if (values.incorporationDate && !ValidationUtils.isValidPastDate(values.incorporationDate)) { errors.incorporationDate = 'bankAccount.error.incorporationDateFuture'; } - if (!values.incorporationState) { - errors.incorporationState = 'bankAccount.error.incorporationState'; - } - if (!values.hasNoConnectionToCannabis) { errors.hasNoConnectionToCannabis = 'bankAccount.error.restrictedBusiness'; } diff --git a/src/pages/ReimbursementAccount/RequestorStep.js b/src/pages/ReimbursementAccount/RequestorStep.js index 0fa71833b34c..5fd962be3b0d 100644 --- a/src/pages/ReimbursementAccount/RequestorStep.js +++ b/src/pages/ReimbursementAccount/RequestorStep.js @@ -38,49 +38,26 @@ class RequestorStep extends React.Component { * @returns {Object} */ validate(values) { - const errors = {}; - - if (!ValidationUtils.isRequiredFulfilled(values.firstName)) { - errors.firstName = 'bankAccount.error.firstName'; - } - - if (!ValidationUtils.isRequiredFulfilled(values.lastName)) { - errors.lastName = 'bankAccount.error.lastName'; - } - - if (!ValidationUtils.isRequiredFulfilled(values.dob)) { - errors.dob = 'bankAccount.error.dob'; - } + const requiredFields = ['firstName', 'lastName', 'dob', 'ssnLast4', 'requestorAddressStreet', 'requestorAddressCity', 'requestorAddressState', 'requestorAddressZipCode']; + const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields); if (values.dob) { - if (!ValidationUtils.meetsMinimumAgeRequirement(values.dob)) { - errors.dob = 'bankAccount.error.age'; - } else if (!ValidationUtils.meetsMaximumAgeRequirement(values.dob)) { + if (!ValidationUtils.isValidPastDate(values.dob) || !ValidationUtils.meetsMaximumAgeRequirement(values.dob)) { errors.dob = 'bankAccount.error.dob'; + } else if (!ValidationUtils.meetsMinimumAgeRequirement(values.dob)) { + errors.dob = 'bankAccount.error.age'; } } - if (!ValidationUtils.isRequiredFulfilled(values.ssnLast4) || !ValidationUtils.isValidSSNLastFour(values.ssnLast4)) { + if (values.ssnLast4 && !ValidationUtils.isValidSSNLastFour(values.ssnLast4)) { errors.ssnLast4 = 'bankAccount.error.ssnLast4'; } - if (!ValidationUtils.isRequiredFulfilled(values.requestorAddressStreet)) { - errors.requestorAddressStreet = 'bankAccount.error.address'; - } - if (values.requestorAddressStreet && !ValidationUtils.isValidAddress(values.requestorAddressStreet)) { errors.requestorAddressStreet = 'bankAccount.error.addressStreet'; } - if (!ValidationUtils.isRequiredFulfilled(values.requestorAddressCity)) { - errors.requestorAddressCity = 'bankAccount.error.addressCity'; - } - - if (!ValidationUtils.isRequiredFulfilled(values.requestorAddressState)) { - errors.requestorAddressState = 'bankAccount.error.addressState'; - } - - if (!ValidationUtils.isRequiredFulfilled(values.requestorAddressZipCode) || !ValidationUtils.isValidZipCode(values.requestorAddressZipCode)) { + if (values.requestorAddressZipCode && !ValidationUtils.isValidZipCode(values.requestorAddressZipCode)) { errors.requestorAddressZipCode = 'bankAccount.error.zipCode'; } diff --git a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js index fa73d2b1a69a..90c469c4e25d 100644 --- a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js +++ b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js @@ -45,15 +45,14 @@ function DateOfBirthPage({translate, privatePersonalDetails}) { * @returns {Object} - An object containing the errors for each inputID */ const validate = useCallback((values) => { - const errors = {}; + const requiredFields = ['dob']; + const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields); + const minimumAge = CONST.DATE_BIRTH.MIN_AGE; const maximumAge = CONST.DATE_BIRTH.MAX_AGE; - - if (!values.dob || !ValidationUtils.isValidDate(values.dob)) { - errors.dob = 'common.error.fieldRequired'; - } const dateError = ValidationUtils.getAgeRequirementError(values.dob, minimumAge, maximumAge); - if (dateError) { + + if (values.dob && dateError) { errors.dob = dateError; } diff --git a/src/pages/settings/Security/CloseAccountPage.js b/src/pages/settings/Security/CloseAccountPage.js index 9ec5a52b8d3a..b2370156228a 100644 --- a/src/pages/settings/Security/CloseAccountPage.js +++ b/src/pages/settings/Security/CloseAccountPage.js @@ -2,7 +2,6 @@ import React, {useState, useEffect} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; -import _ from 'underscore'; import Str from 'expensify-common/lib/str'; import HeaderWithBackButton from '../../../components/HeaderWithBackButton'; import Navigation from '../../../libs/Navigation/Navigation'; @@ -20,6 +19,7 @@ import ONYXKEYS from '../../../ONYXKEYS'; import Form from '../../../components/Form'; import CONST from '../../../CONST'; import ConfirmModal from '../../../components/ConfirmModal'; +import * as ValidationUtils from '../../../libs/ValidationUtils'; const propTypes = { /** Session of currently logged in user */ @@ -63,10 +63,11 @@ function CloseAccountPage(props) { }; const validate = (values) => { + const requiredFields = ['phoneOrEmail']; const userEmailOrPhone = props.formatPhoneNumber(props.session.email); - const errors = {}; + const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields); - if (_.isEmpty(values.phoneOrEmail) || userEmailOrPhone.toLowerCase() !== values.phoneOrEmail.toLowerCase()) { + if (values.phoneOrEmail && userEmailOrPhone.toLowerCase() !== values.phoneOrEmail.toLowerCase()) { errors.phoneOrEmail = 'closeAccountPage.enterYourDefaultContactMethod'; } return errors; diff --git a/src/pages/settings/Wallet/AddDebitCardPage.js b/src/pages/settings/Wallet/AddDebitCardPage.js index 7a04db30abed..ea0b4698a443 100644 --- a/src/pages/settings/Wallet/AddDebitCardPage.js +++ b/src/pages/settings/Wallet/AddDebitCardPage.js @@ -60,36 +60,33 @@ function DebitCardPage(props) { * @returns {Boolean} */ const validate = (values) => { - const errors = {}; + const requiredFields = ['nameOnCard', 'cardNumber', 'expirationDate', 'securityCode', 'addressStreet', 'addressZipCode', 'addressState']; + const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields); - if (!values.nameOnCard || !ValidationUtils.isValidLegalName(values.nameOnCard)) { + if (values.nameOnCard && !ValidationUtils.isValidLegalName(values.nameOnCard)) { errors.nameOnCard = 'addDebitCardPage.error.invalidName'; } - if (!values.cardNumber || !ValidationUtils.isValidDebitCard(values.cardNumber.replace(/ /g, ''))) { + if (values.cardNumber && !ValidationUtils.isValidDebitCard(values.cardNumber.replace(/ /g, ''))) { errors.cardNumber = 'addDebitCardPage.error.debitCardNumber'; } - if (!values.expirationDate || !ValidationUtils.isValidExpirationDate(values.expirationDate)) { + if (values.expirationDate && !ValidationUtils.isValidExpirationDate(values.expirationDate)) { errors.expirationDate = 'addDebitCardPage.error.expirationDate'; } - if (!values.securityCode || !ValidationUtils.isValidSecurityCode(values.securityCode)) { + if (values.securityCode && !ValidationUtils.isValidSecurityCode(values.securityCode)) { errors.securityCode = 'addDebitCardPage.error.securityCode'; } - if (!values.addressStreet || !ValidationUtils.isValidAddress(values.addressStreet)) { + if (values.addressStreet && !ValidationUtils.isValidAddress(values.addressStreet)) { errors.addressStreet = 'addDebitCardPage.error.addressStreet'; } - if (!values.addressZipCode || !ValidationUtils.isValidZipCode(values.addressZipCode)) { + if (values.addressZipCode && !ValidationUtils.isValidZipCode(values.addressZipCode)) { errors.addressZipCode = 'addDebitCardPage.error.addressZipCode'; } - if (!values.addressState || !values.addressState) { - errors.addressState = 'addDebitCardPage.error.addressState'; - } - if (!values.acceptTerms) { errors.acceptTerms = 'common.error.acceptTerms'; } diff --git a/src/pages/settings/Wallet/AddPayPalMePage.js b/src/pages/settings/Wallet/AddPayPalMePage.js index 042e0c8576e6..a95163bbae5c 100644 --- a/src/pages/settings/Wallet/AddPayPalMePage.js +++ b/src/pages/settings/Wallet/AddPayPalMePage.js @@ -38,8 +38,10 @@ const defaultProps = { }; const validate = (values) => { - const errors = {}; - if (!ValidationUtils.isValidPaypalUsername(values.payPalMeUsername)) { + const requiredFields = ['payPalMeUsername']; + const errors = ValidationUtils.getFieldRequiredErrors(values, requiredFields); + + if (values.payPalMeUsername && !ValidationUtils.isValidPaypalUsername(values.payPalMeUsername)) { errors.payPalMeUsername = 'addPayPalMePage.formatError'; }