From 4fa79fd5a73bb49cada0ef7b2c7d2232454ca758 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 17 Oct 2023 14:01:38 +0200 Subject: [PATCH 001/105] Add error message --- src/components/StatePicker/StateSelectorModal.js | 2 +- src/languages/en.ts | 2 ++ src/languages/types.ts | 3 +++ src/libs/actions/IOU.js | 4 ++-- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/StatePicker/StateSelectorModal.js b/src/components/StatePicker/StateSelectorModal.js index 378dcc4ebc8b..30379c72c4a0 100644 --- a/src/components/StatePicker/StateSelectorModal.js +++ b/src/components/StatePicker/StateSelectorModal.js @@ -10,7 +10,7 @@ import useLocalize from '../../hooks/useLocalize'; import ScreenWrapper from '../ScreenWrapper'; import styles from '../../styles/styles'; import searchCountryOptions from '../../libs/searchCountryOptions'; -import StringUtils from '../../libs/StringUtils'; +import * as StringUtils from '../../libs/StringUtils'; const propTypes = { /** Whether the modal is visible */ diff --git a/src/languages/en.ts b/src/languages/en.ts index f69f9ea4bedd..edbac8d1dc61 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -74,6 +74,7 @@ import type { FormattedMaxLengthParams, RequestedAmountMessageParams, TagSelectionParams, + ReceiptCreateFailureMessageParams, TranslationBase, WalletProgramParams, } from './types'; @@ -567,6 +568,7 @@ export default { invalidSplit: 'Split amounts do not equal total amount', other: 'Unexpected error, please try again later', genericCreateFailureMessage: 'Unexpected error requesting money, please try again later', + receiptCreateFailureMessage: ({receiptUrl}: ReceiptCreateFailureMessageParams) => `The receipt didn't upload. Save the file or dismiss this error and lose it.`, genericDeleteFailureMessage: 'Unexpected error deleting the money request, please try again later', genericEditFailureMessage: 'Unexpected error editing the money request, please try again later', genericSmartscanFailureMessage: 'Transaction is missing fields', diff --git a/src/languages/types.ts b/src/languages/types.ts index 3ee504ccddd7..193f226899d6 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -198,6 +198,8 @@ type FormattedMaxLengthParams = {formattedMaxLength: string}; type TagSelectionParams = {tagName: string}; +type ReceiptCreateFailureMessageParams = {tagName: string}; + type WalletProgramParams = {walletProgram: string}; /* Translation Object types */ @@ -313,6 +315,7 @@ export type { RemovedTheRequestParams, FormattedMaxLengthParams, TagSelectionParams, + ReceiptCreateFailureMessageParams, SetTheDistanceParams, UpdatedTheDistanceParams, WalletProgramParams, diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index d5676672dd33..de6fdccd7b4e 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -349,12 +349,12 @@ function buildOnyxDataForMoneyRequest( errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, [iouAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError(null), + errors: transaction.receipt ? ErrorUtils.getMicroSecondOnyxError(Localize.translateLocal('iou.error.receiptCreateFailureMessage', {receiptUrl: transaction.receipt.source})) : ErrorUtils.getMicroSecondOnyxError(null), }, } : { [iouAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: transaction.receipt ? ErrorUtils.getMicroSecondOnyxError(Localize.translateLocal('iou.error.receiptCreateFailureMessage', {receiptUrl: transaction.receipt.source})) : ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, }), }, From 418f5d7246330c1db21db885e7c849eeb546f165 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 17 Oct 2023 14:02:27 +0200 Subject: [PATCH 002/105] handle htmL --- src/components/DotIndicatorMessage.js | 5 ++++- src/libs/StringUtils.ts | 15 ++++++++++++++- .../PersonalDetails/CountrySelectionPage.js | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/components/DotIndicatorMessage.js b/src/components/DotIndicatorMessage.js index b3528b43dc75..f5d2bbbd5b1e 100644 --- a/src/components/DotIndicatorMessage.js +++ b/src/components/DotIndicatorMessage.js @@ -8,6 +8,9 @@ import * as Expensicons from './Icon/Expensicons'; import themeColors from '../styles/themes/default'; import Text from './Text'; import * as Localize from '../libs/Localize'; +import {hasHTML} from "../libs/StringUtils"; +import RenderHTML from "./RenderHTML"; +import * as StringUtils from "../libs/StringUtils"; const propTypes = { /** @@ -66,7 +69,7 @@ function DotIndicatorMessage(props) { key={i} style={styles.offlineFeedback.text} > - {message} + {StringUtils.hasHTML(message) ? : message} ))} diff --git a/src/libs/StringUtils.ts b/src/libs/StringUtils.ts index 8ef23bb0751b..b7021010aa05 100644 --- a/src/libs/StringUtils.ts +++ b/src/libs/StringUtils.ts @@ -10,4 +10,17 @@ function sanitizeString(str: string): string { return _.deburr(str).toLowerCase().replaceAll(CONST.REGEX.NON_ALPHABETIC_AND_NON_LATIN_CHARS, ''); } -export default {sanitizeString}; +/** + * Very simple function to identify if a string contains HTML elements. WARNING: Things like '' will return true + * @param str - The string to check. + * @returns True if it contains html elements + */ +function hasHTML(str: string): boolean { + const regex = new RegExp('/<\\/?[a-z][\\s\\S]*>/', 'i'); + return regex.test(str) +} + +export { + sanitizeString, + hasHTML, +}; diff --git a/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js b/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js index 741974776df1..ef19ba6aad9d 100644 --- a/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js +++ b/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js @@ -7,7 +7,7 @@ import ScreenWrapper from '../../../../components/ScreenWrapper'; import HeaderWithBackButton from '../../../../components/HeaderWithBackButton'; import SelectionList from '../../../../components/SelectionList'; import searchCountryOptions from '../../../../libs/searchCountryOptions'; -import StringUtils from '../../../../libs/StringUtils'; +import * as StringUtils from '../../../../libs/StringUtils'; import CONST from '../../../../CONST'; import useLocalize from '../../../../hooks/useLocalize'; From ccda700add1f128ac8c882507f532d8ea56058fa Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 17 Oct 2023 14:12:11 +0200 Subject: [PATCH 003/105] fix regex --- src/libs/StringUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/StringUtils.ts b/src/libs/StringUtils.ts index b7021010aa05..ae0df0e563d3 100644 --- a/src/libs/StringUtils.ts +++ b/src/libs/StringUtils.ts @@ -16,7 +16,7 @@ function sanitizeString(str: string): string { * @returns True if it contains html elements */ function hasHTML(str: string): boolean { - const regex = new RegExp('/<\\/?[a-z][\\s\\S]*>/', 'i'); + const regex = new RegExp('', 'i'); return regex.test(str) } From ee3fbdb2f6903a8bf8b426a9cf340a8145829c83 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 17 Oct 2023 14:12:48 +0200 Subject: [PATCH 004/105] style --- src/components/DotIndicatorMessage.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/DotIndicatorMessage.js b/src/components/DotIndicatorMessage.js index f5d2bbbd5b1e..3ed660865cc9 100644 --- a/src/components/DotIndicatorMessage.js +++ b/src/components/DotIndicatorMessage.js @@ -8,9 +8,8 @@ import * as Expensicons from './Icon/Expensicons'; import themeColors from '../styles/themes/default'; import Text from './Text'; import * as Localize from '../libs/Localize'; -import {hasHTML} from "../libs/StringUtils"; -import RenderHTML from "./RenderHTML"; -import * as StringUtils from "../libs/StringUtils"; +import RenderHTML from './RenderHTML'; +import * as StringUtils from '../libs/StringUtils'; const propTypes = { /** From 6cb7230ef7cd786c2e716b0b68d97ff01699d574 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 17 Oct 2023 17:00:23 +0200 Subject: [PATCH 005/105] use object for error --- src/CONST.ts | 1 + src/components/DotIndicatorMessage.js | 20 ++++++++++++++++++-- src/libs/actions/IOU.js | 2 +- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 55fd7faa5a6a..2651fb057a92 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1118,6 +1118,7 @@ const CONST = { DOCX: 'docx', SVG: 'svg', }, + RECEIPT_ERROR: 'receiptError', }, GROWL: { diff --git a/src/components/DotIndicatorMessage.js b/src/components/DotIndicatorMessage.js index 3ed660865cc9..e74974846141 100644 --- a/src/components/DotIndicatorMessage.js +++ b/src/components/DotIndicatorMessage.js @@ -10,6 +10,7 @@ import Text from './Text'; import * as Localize from '../libs/Localize'; import RenderHTML from './RenderHTML'; import * as StringUtils from '../libs/StringUtils'; +import CONST from '../CONST'; const propTypes = { /** @@ -39,6 +40,13 @@ function DotIndicatorMessage(props) { return null; } + function isReceiptError (message) { + if (_.isString(message)) { + return false; + } + return _.get(message, 'error', '') === CONST.IOU.RECEIPT_ERROR; + } + // To ensure messages are presented in order we are sort of destroying the data we are given // and rebuilding as an array so we can render the messages in order. We don't really care about // the microtime timestamps anyways so isn't the end of the world that we sort of lose them here. @@ -64,13 +72,21 @@ function DotIndicatorMessage(props) { {_.map(sortedMessages, (message, i) => ( + isReceiptError(message) ? ( + + bla + + ) : ( - {StringUtils.hasHTML(message) ? : message} + {message} - ))} + )))} ); diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index de6fdccd7b4e..60b5b1f1fad6 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -354,7 +354,7 @@ function buildOnyxDataForMoneyRequest( } : { [iouAction.reportActionID]: { - errors: transaction.receipt ? ErrorUtils.getMicroSecondOnyxError(Localize.translateLocal('iou.error.receiptCreateFailureMessage', {receiptUrl: transaction.receipt.source})) : ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: transaction.receipt ? {[DateUtils.getMicroseconds()]: {error: CONST.IOU.RECEIPT_ERROR, source: transaction.receipt.source, filename: transaction.filename}} : ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, }), }, From 874985736bd4b2343b0d0d667c2c03010bcc82a9 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 17 Oct 2023 17:23:20 +0200 Subject: [PATCH 006/105] Use new approach --- src/components/DotIndicatorMessage.js | 38 ++++++++++++++++++++------- src/languages/en.ts | 4 ++- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/components/DotIndicatorMessage.js b/src/components/DotIndicatorMessage.js index e74974846141..4486d439a05f 100644 --- a/src/components/DotIndicatorMessage.js +++ b/src/components/DotIndicatorMessage.js @@ -1,16 +1,17 @@ import React from 'react'; import _ from 'underscore'; import PropTypes from 'prop-types'; -import {View} from 'react-native'; +import {Keyboard, View} from 'react-native'; import styles from '../styles/styles'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; import themeColors from '../styles/themes/default'; import Text from './Text'; import * as Localize from '../libs/Localize'; -import RenderHTML from './RenderHTML'; -import * as StringUtils from '../libs/StringUtils'; import CONST from '../CONST'; +import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; +import addEncryptedAuthTokenToURL from "../libs/addEncryptedAuthTokenToURL"; +import fileDownload from '../libs/fileDownload'; const propTypes = { /** @@ -40,12 +41,19 @@ function DotIndicatorMessage(props) { return null; } - function isReceiptError (message) { + const isReceiptError = (message) => { if (_.isString(message)) { return false; } return _.get(message, 'error', '') === CONST.IOU.RECEIPT_ERROR; - } + }; + + /** + * Download the failed receipt. + */ + const downloadReceipt = (source, filename) => { + fileDownload(source, filename); + }; // To ensure messages are presented in order we are sort of destroying the data we are given // and rebuilding as an array so we can render the messages in order. We don't really care about @@ -73,12 +81,22 @@ function DotIndicatorMessage(props) { {_.map(sortedMessages, (message, i) => ( isReceiptError(message) ? ( - { + downloadReceipt(message.source, message.filename) + }} > - bla - + + {Localize.translateLocal('iou.error.receiptFailureMessage')} + {Localize.translateLocal('iou.error.saveFileMessage')} + {Localize.translateLocal('iou.error.loseFileMessage')} + + ) : ( `The receipt didn't upload. Save the file or dismiss this error and lose it.`, + receiptFailureMessage: 'The receipt didn\'t upload. ', + saveFileMessage: 'Save the file ', + loseFileMessage: 'or dismiss this error and lose it', genericDeleteFailureMessage: 'Unexpected error deleting the money request, please try again later', genericEditFailureMessage: 'Unexpected error editing the money request, please try again later', genericSmartscanFailureMessage: 'Transaction is missing fields', From 52f5e509b699ebaf1235e32f9f8988defe497738 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 17 Oct 2023 17:59:10 +0200 Subject: [PATCH 007/105] use function --- src/components/DotIndicatorMessage.js | 1 - src/languages/en.ts | 1 - src/languages/types.ts | 3 --- src/libs/ErrorUtils.ts | 10 +++++++++- src/libs/actions/IOU.js | 4 ++-- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/components/DotIndicatorMessage.js b/src/components/DotIndicatorMessage.js index 4486d439a05f..afb59114cc6d 100644 --- a/src/components/DotIndicatorMessage.js +++ b/src/components/DotIndicatorMessage.js @@ -10,7 +10,6 @@ import Text from './Text'; import * as Localize from '../libs/Localize'; import CONST from '../CONST'; import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; -import addEncryptedAuthTokenToURL from "../libs/addEncryptedAuthTokenToURL"; import fileDownload from '../libs/fileDownload'; const propTypes = { diff --git a/src/languages/en.ts b/src/languages/en.ts index 9dfeddcc2f36..d4c5e300708d 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -74,7 +74,6 @@ import type { FormattedMaxLengthParams, RequestedAmountMessageParams, TagSelectionParams, - ReceiptCreateFailureMessageParams, TranslationBase, WalletProgramParams, } from './types'; diff --git a/src/languages/types.ts b/src/languages/types.ts index 193f226899d6..3ee504ccddd7 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -198,8 +198,6 @@ type FormattedMaxLengthParams = {formattedMaxLength: string}; type TagSelectionParams = {tagName: string}; -type ReceiptCreateFailureMessageParams = {tagName: string}; - type WalletProgramParams = {walletProgram: string}; /* Translation Object types */ @@ -315,7 +313,6 @@ export type { RemovedTheRequestParams, FormattedMaxLengthParams, TagSelectionParams, - ReceiptCreateFailureMessageParams, SetTheDistanceParams, UpdatedTheDistanceParams, WalletProgramParams, diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index bf4fc0d810a4..4be126830500 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -42,6 +42,14 @@ function getMicroSecondOnyxError(error: string): Record { return {[DateUtils.getMicroseconds()]: error}; } +/** + * Method used to get an error object with microsecond as the key and am object as the value. + * @param error - error key or message to be saved + */ +function getMicroSecondOnyxErrorObject(error: Record): Record> { + return {[DateUtils.getMicroseconds()]: error}; +} + type OnyxDataWithErrors = { errors?: Errors; }; @@ -111,4 +119,4 @@ function addErrorMessage(errors: ErrorsList, inputID?: string, message?: string) } } -export {getAuthenticateErrorMessage, getMicroSecondOnyxError, getLatestErrorMessage, getLatestErrorField, getEarliestErrorField, addErrorMessage}; +export {getAuthenticateErrorMessage, getMicroSecondOnyxError, getMicroSecondOnyxErrorObject, getLatestErrorMessage, getLatestErrorField, getEarliestErrorField, addErrorMessage}; diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 60b5b1f1fad6..a6e028b6e7ea 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -349,12 +349,12 @@ function buildOnyxDataForMoneyRequest( errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, [iouAction.reportActionID]: { - errors: transaction.receipt ? ErrorUtils.getMicroSecondOnyxError(Localize.translateLocal('iou.error.receiptCreateFailureMessage', {receiptUrl: transaction.receipt.source})) : ErrorUtils.getMicroSecondOnyxError(null), + errors: transaction.receipt ? ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: transaction.receipt.source, filename: transaction.filename}) : ErrorUtils.getMicroSecondOnyxError(null), }, } : { [iouAction.reportActionID]: { - errors: transaction.receipt ? {[DateUtils.getMicroseconds()]: {error: CONST.IOU.RECEIPT_ERROR, source: transaction.receipt.source, filename: transaction.filename}} : ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: transaction.receipt ? ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: transaction.receipt.source, filename: transaction.filename}) : ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, }), }, From d97beb33efe4d1febd628b3a6baaa8c65666e19b Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 17 Oct 2023 18:00:30 +0200 Subject: [PATCH 008/105] remove unneeded helper function --- src/components/StatePicker/StateSelectorModal.js | 2 +- src/libs/StringUtils.ts | 15 +-------------- .../PersonalDetails/CountrySelectionPage.js | 2 +- 3 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/components/StatePicker/StateSelectorModal.js b/src/components/StatePicker/StateSelectorModal.js index 30379c72c4a0..378dcc4ebc8b 100644 --- a/src/components/StatePicker/StateSelectorModal.js +++ b/src/components/StatePicker/StateSelectorModal.js @@ -10,7 +10,7 @@ import useLocalize from '../../hooks/useLocalize'; import ScreenWrapper from '../ScreenWrapper'; import styles from '../../styles/styles'; import searchCountryOptions from '../../libs/searchCountryOptions'; -import * as StringUtils from '../../libs/StringUtils'; +import StringUtils from '../../libs/StringUtils'; const propTypes = { /** Whether the modal is visible */ diff --git a/src/libs/StringUtils.ts b/src/libs/StringUtils.ts index ae0df0e563d3..8ef23bb0751b 100644 --- a/src/libs/StringUtils.ts +++ b/src/libs/StringUtils.ts @@ -10,17 +10,4 @@ function sanitizeString(str: string): string { return _.deburr(str).toLowerCase().replaceAll(CONST.REGEX.NON_ALPHABETIC_AND_NON_LATIN_CHARS, ''); } -/** - * Very simple function to identify if a string contains HTML elements. WARNING: Things like '' will return true - * @param str - The string to check. - * @returns True if it contains html elements - */ -function hasHTML(str: string): boolean { - const regex = new RegExp('', 'i'); - return regex.test(str) -} - -export { - sanitizeString, - hasHTML, -}; +export default {sanitizeString}; diff --git a/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js b/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js index ef19ba6aad9d..741974776df1 100644 --- a/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js +++ b/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js @@ -7,7 +7,7 @@ import ScreenWrapper from '../../../../components/ScreenWrapper'; import HeaderWithBackButton from '../../../../components/HeaderWithBackButton'; import SelectionList from '../../../../components/SelectionList'; import searchCountryOptions from '../../../../libs/searchCountryOptions'; -import * as StringUtils from '../../../../libs/StringUtils'; +import StringUtils from '../../../../libs/StringUtils'; import CONST from '../../../../CONST'; import useLocalize from '../../../../hooks/useLocalize'; From ca1443a94469d02637aac37375c74e208973a8ac Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 17 Oct 2023 18:06:51 +0200 Subject: [PATCH 009/105] spanish --- src/languages/es.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/languages/es.ts b/src/languages/es.ts index 83e09b2a62c0..913056364970 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -561,6 +561,9 @@ export default { invalidSplit: 'La suma de las partes no equivale al monto total', other: 'Error inesperado, por favor inténtalo más tarde', genericCreateFailureMessage: 'Error inesperado solicitando dinero, Por favor, inténtalo más tarde', + receiptFailureMessage: 'El recibo no se subió. ', + saveFileMessage: 'Guarda el archivo ', + loseFileMessage: 'o descarta este error y piérdelo', genericDeleteFailureMessage: 'Error inesperado eliminando la solicitud de dinero. Por favor, inténtalo más tarde', genericEditFailureMessage: 'Error inesperado al guardar la solicitud de dinero. Por favor, inténtalo más tarde', genericSmartscanFailureMessage: 'La transacción tiene campos vacíos', From 0c559d1a9affc62563acda575a22cb8e6a8185c4 Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 18 Oct 2023 13:10:37 +0200 Subject: [PATCH 010/105] lint --- src/components/DotIndicatorMessage.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/DotIndicatorMessage.js b/src/components/DotIndicatorMessage.js index afb59114cc6d..2b1e4ceac97f 100644 --- a/src/components/DotIndicatorMessage.js +++ b/src/components/DotIndicatorMessage.js @@ -1,7 +1,7 @@ import React from 'react'; import _ from 'underscore'; import PropTypes from 'prop-types'; -import {Keyboard, View} from 'react-native'; +import {View} from 'react-native'; import styles from '../styles/styles'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; @@ -49,6 +49,9 @@ function DotIndicatorMessage(props) { /** * Download the failed receipt. + * + * @param {String} source + * @param {String} filename */ const downloadReceipt = (source, filename) => { fileDownload(source, filename); From 08d8e8667d4e24e83ff50eed7ee0b1e42372ac79 Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 18 Oct 2023 13:25:59 +0200 Subject: [PATCH 011/105] prettier --- src/components/DotIndicatorMessage.js | 19 ++++++++++--------- src/languages/en.ts | 2 +- src/libs/actions/IOU.js | 8 ++++++-- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/components/DotIndicatorMessage.js b/src/components/DotIndicatorMessage.js index 2b1e4ceac97f..2683e24e6e8e 100644 --- a/src/components/DotIndicatorMessage.js +++ b/src/components/DotIndicatorMessage.js @@ -81,12 +81,12 @@ function DotIndicatorMessage(props) { /> - {_.map(sortedMessages, (message, i) => ( + {_.map(sortedMessages, (message, i) => isReceiptError(message) ? ( { - downloadReceipt(message.source, message.filename) + downloadReceipt(message.source, message.filename); }} > ) : ( - - {message} - - )))} + + {message} + + ), + )} ); diff --git a/src/languages/en.ts b/src/languages/en.ts index d4c5e300708d..57d5eaf11e82 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -567,7 +567,7 @@ export default { invalidSplit: 'Split amounts do not equal total amount', other: 'Unexpected error, please try again later', genericCreateFailureMessage: 'Unexpected error requesting money, please try again later', - receiptFailureMessage: 'The receipt didn\'t upload. ', + receiptFailureMessage: "The receipt didn't upload. ", saveFileMessage: 'Save the file ', loseFileMessage: 'or dismiss this error and lose it', genericDeleteFailureMessage: 'Unexpected error deleting the money request, please try again later', diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index a6e028b6e7ea..2d19366e657a 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -349,12 +349,16 @@ function buildOnyxDataForMoneyRequest( errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, [iouAction.reportActionID]: { - errors: transaction.receipt ? ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: transaction.receipt.source, filename: transaction.filename}) : ErrorUtils.getMicroSecondOnyxError(null), + errors: transaction.receipt + ? ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: transaction.receipt.source, filename: transaction.filename}) + : ErrorUtils.getMicroSecondOnyxError(null), }, } : { [iouAction.reportActionID]: { - errors: transaction.receipt ? ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: transaction.receipt.source, filename: transaction.filename}) : ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: transaction.receipt + ? ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: transaction.receipt.source, filename: transaction.filename}) + : ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, }), }, From 040c5edcb4326211493d100e88203699dd271be4 Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 18 Oct 2023 16:48:05 +0200 Subject: [PATCH 012/105] allow multiple lines --- src/components/DotIndicatorMessage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/DotIndicatorMessage.js b/src/components/DotIndicatorMessage.js index 2683e24e6e8e..ef838fb0ed4a 100644 --- a/src/components/DotIndicatorMessage.js +++ b/src/components/DotIndicatorMessage.js @@ -92,7 +92,6 @@ function DotIndicatorMessage(props) { {Localize.translateLocal('iou.error.receiptFailureMessage')} {Localize.translateLocal('iou.error.saveFileMessage')} From 17a1c43ba0dab4f7d41e7256bb6b8942cf7e215f Mon Sep 17 00:00:00 2001 From: Alberto Date: Thu, 19 Oct 2023 12:04:11 +0200 Subject: [PATCH 013/105] add split flow --- src/libs/actions/IOU.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 2256b72e3851..c9bcf60e88ac 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1413,7 +1413,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, value: { [splitIOUReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: receipt ? ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source, filename}) : ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, }, }); @@ -1436,7 +1436,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co errors: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), }, [splitIOUReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: receipt ? ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source, filename}) : ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, }, }, From 1d95f4613ca3567efc635e1b0c3c308c8015cc1d Mon Sep 17 00:00:00 2001 From: Alberto Date: Thu, 19 Oct 2023 12:36:17 +0200 Subject: [PATCH 014/105] use isEmpty --- src/libs/actions/IOU.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index c9bcf60e88ac..a223ce212d70 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -359,16 +359,16 @@ function buildOnyxDataForMoneyRequest( errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), }, [iouAction.reportActionID]: { - errors: transaction.receipt - ? ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: transaction.receipt.source, filename: transaction.filename}) - : ErrorUtils.getMicroSecondOnyxError(null), + errors: _.isEmpty(transaction.receipt) + ? ErrorUtils.getMicroSecondOnyxError(null) + : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: transaction.receipt.source, filename: transaction.filename}), }, } : { [iouAction.reportActionID]: { - errors: transaction.receipt - ? ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: transaction.receipt.source, filename: transaction.filename}) - : ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: !_.isEmpty(transaction.receipt) + ? ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage') + : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: transaction.receipt.source, filename: transaction.filename}), }, }), }, @@ -1413,7 +1413,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, value: { [splitIOUReportAction.reportActionID]: { - errors: receipt ? ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source, filename}) : ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: _.isEmpty(receipt) ? ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage') : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source, filename}), }, }, }); @@ -1436,7 +1436,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co errors: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), }, [splitIOUReportAction.reportActionID]: { - errors: receipt ? ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source, filename}) : ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage'), + errors: _.isEmpty(receipt) ? ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage') : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source, filename}), }, }, }, From 0d010f9ede36a4c616d160fbbe98d4f2f7e90463 Mon Sep 17 00:00:00 2001 From: Alberto Date: Thu, 19 Oct 2023 12:47:24 +0200 Subject: [PATCH 015/105] use empty for split --- src/libs/actions/IOU.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index a223ce212d70..554e52f7383f 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1413,7 +1413,9 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, value: { [splitIOUReportAction.reportActionID]: { - errors: _.isEmpty(receipt) ? ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage') : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source, filename}), + errors: _.isEmpty(receipt) + ? ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage') + : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source, filename}), }, }, }); @@ -1436,7 +1438,9 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co errors: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), }, [splitIOUReportAction.reportActionID]: { - errors: _.isEmpty(receipt) ? ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage') : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source, filename}), + errors: _.isEmpty(receipt) + ? ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage') + : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source, filename}), }, }, }, From 8f961ac7430dab15850570a523ea474b70dcfaed Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Sat, 21 Oct 2023 00:00:14 +0100 Subject: [PATCH 016/105] Add a new button to the payment methods popover --- src/CONST.ts | 1 + src/components/AddPaymentMethodMenu.js | 31 +++++++++++++++++++++++++- src/components/KYCWall/BaseKYCWall.js | 1 + src/languages/en.ts | 2 ++ src/languages/es.ts | 2 ++ src/libs/ReportActionsUtils.ts | 21 +++++++++++++++++ 6 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index 048c2dee5bab..dd6df949c3fa 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1101,6 +1101,7 @@ const CONST = { PAYMENT_METHODS: { DEBIT_CARD: 'debitCard', BANK_ACCOUNT: 'bankAccount', + BUSINESS_BANK_ACCOUNT: 'businessBankAccount', }, PAYMENT_METHOD_ID_KEYS: { diff --git a/src/components/AddPaymentMethodMenu.js b/src/components/AddPaymentMethodMenu.js index 2c3af95a3fad..7ced56b5b63a 100644 --- a/src/components/AddPaymentMethodMenu.js +++ b/src/components/AddPaymentMethodMenu.js @@ -2,6 +2,7 @@ import _ from 'underscore'; import React from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; +import lodashGet from 'lodash/get'; import * as Expensicons from './Icon/Expensicons'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import compose from '../libs/compose'; @@ -11,6 +12,9 @@ import withWindowDimensions from './withWindowDimensions'; import Permissions from '../libs/Permissions'; import PopoverMenu from './PopoverMenu'; import refPropTypes from './refPropTypes'; +import iouReportPropTypes from '../pages/iouReportPropTypes'; +import * as ReportUtils from '../libs/ReportUtils'; +import * as ReportActionsUtils from '../libs/ReportActionsUtils'; const propTypes = { /** Should the component be visible? */ @@ -19,6 +23,9 @@ const propTypes = { /** Callback to execute when the component closes. */ onClose: PropTypes.func.isRequired, + /** The IOU/Expense report we are paying */ + iouReport: iouReportPropTypes, + /** Anchor position for the AddPaymentMenu. */ anchorPosition: PropTypes.shape({ horizontal: PropTypes.number, @@ -37,10 +44,17 @@ const propTypes = { /** Popover anchor ref */ anchorRef: refPropTypes, + /** Session info for the currently logged in user. */ + session: PropTypes.shape({ + /** Currently logged in user accountID */ + accountID: PropTypes.number, + }), + ...withLocalizePropTypes, }; const defaultProps = { + iouReport: {}, anchorPosition: {}, anchorAlignment: { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, @@ -48,6 +62,9 @@ const defaultProps = { }, betas: [], anchorRef: () => {}, + session: { + accountID: 0, + }, }; function AddPaymentMethodMenu(props) { @@ -61,12 +78,21 @@ function AddPaymentMethodMenu(props) { onItemSelected={props.onClose} menuItems={[ { - text: props.translate('common.bankAccount'), + text: props.translate('common.personalBankAccount'), icon: Expensicons.Bank, onSelected: () => { props.onItemSelected(CONST.PAYMENT_METHODS.BANK_ACCOUNT); }, }, + ...(ReportUtils.isIOUReport(this.props.iouReport) && ReportActionsUtils.hasRequestFromPayer(lodashGet(this.props.iouReport, "reportID", 0), this.props.session.accountID) + ? [ + { + text: props.translate('common.businessBankAccount'), + icon: Expensicons.Workspace, + onSelected: () => props.onItemSelected(CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT), + }, + ] + : []), ...(Permissions.canUseWallet(props.betas) ? [ { @@ -93,5 +119,8 @@ export default compose( betas: { key: ONYXKEYS.BETAS, }, + session: { + key: ONYXKEYS.SESSION, + }, }), )(AddPaymentMethodMenu); diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js index ccee8bc4e6a0..969cbfc20dcd 100644 --- a/src/components/KYCWall/BaseKYCWall.js +++ b/src/components/KYCWall/BaseKYCWall.js @@ -164,6 +164,7 @@ class KYCWall extends React.Component { <> this.setState({shouldShowAddPaymentMenu: false})} anchorRef={this.anchorRef} anchorPosition={{ diff --git a/src/languages/en.ts b/src/languages/en.ts index 11637846130a..0a97a47cd770 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -214,6 +214,8 @@ export default { more: 'More', debitCard: 'Debit card', bankAccount: 'Bank account', + personalBankAccount: 'Personal bank account', + businessBankAccount: 'Business bank account', join: 'Join', joinThread: 'Join thread', decline: 'Decline', diff --git a/src/languages/es.ts b/src/languages/es.ts index e4a5c37241f2..8df14012ac9a 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -204,6 +204,8 @@ export default { more: 'Más', debitCard: 'Tarjeta de débito', bankAccount: 'Cuenta bancaria', + personalBankAccount: 'Personal bank account', + businessBankAccount: 'Business bank account', join: 'Unirse', joinThread: 'Unirse al hilo', decline: 'Rechazar', diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index c795e5d1c3b1..5a763bbea895 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -605,6 +605,26 @@ function isNotifiableReportAction(reportAction: OnyxEntry): boolea return actions.includes(reportAction.actionName); } +/** + * Helper method to determine if the provided accountID has made a request on the specified report. + * + * @param reportID + * @param currentAccountID + * @returns + */ +function hasRequestFromPayer(reportID: string, currentAccountID: number): boolean { + if (!reportID) { + return false; + } + + const reportActions = Object.values(getAllReportActions(reportID)); + if (reportActions.length === 0) { + return false; + } + + return reportActions.some((action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && action.actorAccountID === currentAccountID); +} + export { extractLinksFromMessageHtml, getAllReportActions, @@ -644,4 +664,5 @@ export { isWhisperAction, shouldReportActionBeVisible, shouldReportActionBeVisibleAsLastAction, + hasRequestFromPayer, }; From 6c8bd199c0c73c752c58bd616fbeaaa5e500fb6b Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Sat, 21 Oct 2023 01:07:15 +0100 Subject: [PATCH 017/105] Add the business bank account option --- src/components/KYCWall/BaseKYCWall.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js index 969cbfc20dcd..a9814cac7492 100644 --- a/src/components/KYCWall/BaseKYCWall.js +++ b/src/components/KYCWall/BaseKYCWall.js @@ -97,6 +97,8 @@ class KYCWall extends React.Component { Navigation.navigate(this.props.addBankAccountRoute); } else if (paymentMethod === CONST.PAYMENT_METHODS.DEBIT_CARD) { Navigation.navigate(this.props.addDebitCardRoute); + } else if (paymentMethod === CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT) { + // Start the Bottom up flow } } From 1a46590703a67280313606cac66002fc750219cd Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Sun, 22 Oct 2023 21:23:54 +0100 Subject: [PATCH 018/105] Work on the Bottom Up flow command --- src/CONST.ts | 4 +- src/components/KYCWall/BaseKYCWall.js | 3 +- src/libs/actions/Policy.js | 262 ++++++++++++++++++++++++++ 3 files changed, 266 insertions(+), 3 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index dd6df949c3fa..d1db6431eecb 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1190,8 +1190,8 @@ const CONST = { TYPE: { FREE: 'free', PERSONAL: 'personal', - CORPORATE: 'corporate', - TEAM: 'team', + CORPORATE: 'corporate', // aka Control + TEAM: 'team', // aka Collect }, ROLE: { ADMIN: 'admin', diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js index a9814cac7492..e7f4b6ac1b31 100644 --- a/src/components/KYCWall/BaseKYCWall.js +++ b/src/components/KYCWall/BaseKYCWall.js @@ -9,6 +9,7 @@ import AddPaymentMethodMenu from '../AddPaymentMethodMenu'; import getClickedTargetLocation from '../../libs/getClickedTargetLocation'; import * as PaymentUtils from '../../libs/PaymentUtils'; import * as PaymentMethods from '../../libs/actions/PaymentMethods'; +import * as Policy from '../../libs/actions/Policy'; import ONYXKEYS from '../../ONYXKEYS'; import Log from '../../libs/Log'; import {propTypes, defaultProps} from './kycWallPropTypes'; @@ -98,7 +99,7 @@ class KYCWall extends React.Component { } else if (paymentMethod === CONST.PAYMENT_METHODS.DEBIT_CARD) { Navigation.navigate(this.props.addDebitCardRoute); } else if (paymentMethod === CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT) { - // Start the Bottom up flow + Policy.startCollectFlow(this.props.iouReport); } } diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js index 89324dd35485..02b56a079dbe 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -1289,6 +1289,267 @@ function buildOptimisticPolicyRecentlyUsedCategories(policyID, category) { return lodashUnion([category], policyRecentlyUsedCategories); } + +function startCollectBottomUpFlow(iouReport, firstName, lastName) { + + // This flow only works for IOU reports + if (!ReportUtils.isIOUReport(iouReport)) { + return; + } + + // Generate new vairbales for the policy + const policyID = generatePolicyID() + const workspaceName = generateDefaultWorkspaceName(sessionEmail); + const employeeAccountID = iouReport.ownerAccountID; + const employeeEmail = iouReport.ownerEmail; + const {customUnits, customUnitID, customUnitRateID} = buildOptimisticCustomUnits(); + + const { + announceChatReportID, + announceChatData, + announceReportActionData, + announceCreatedReportActionID, + adminsChatReportID, + adminsChatData, + adminsReportActionData, + adminsCreatedReportActionID, + expenseChatReportID: workspaceChatReportID, + expenseChatData: workspaceChatData, + expenseReportActionData: workspaceChatReportActionData, + expenseCreatedReportActionID: workspaceChatCreatedReportActionID, + } = ReportUtils.buildOptimisticWorkspaceChats(policyID, workspaceName); + + // Create the workspace chat for the employee + const employeeWorkspaceChat = createPolicyExpenseChats(policyID, {[employeeEmail]: employeeAccountID}); + + const optimisticData = [ + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + id: policyID, + type: CONST.POLICY.TYPE.TEAM, // We are creating a collect policy in this case + name: workspaceName, + role: CONST.POLICY.ROLE.ADMIN, + owner: sessionEmail, + isPolicyExpenseChatEnabled: true, + outputCurrency: lodashGet(allPersonalDetails, [sessionAccountID, 'localCurrencyCode'], CONST.CURRENCY.USD), + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + customUnits, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, + value: { + [sessionAccountID]: { + role: CONST.POLICY.ROLE.ADMIN, + errors: {}, + }, + [employeeAccountID]: { + role: CONST.POLICY.ROLE.USER, + errors: {}, + }, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ...announceChatData, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: announceReportActionData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ...adminsChatData, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: adminsReportActionData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${workspaceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ...workspaceChatData, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChatReportID}`, + value: workspaceChatReportActionData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS_DRAFTS}${policyID}`, + value: null, + }, + ...employeeWorkspaceChat.onyxOptimisticData, + ]; + + const successData = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: {pendingAction: null}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: { + [_.keys(announceChatData)[0]]: { + pendingAction: null, + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: { + [_.keys(adminsChatData)[0]]: { + pendingAction: null, + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${workspaceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChatReportID}`, + value: { + [_.keys(workspaceChatData)[0]]: { + pendingAction: null, + }, + }, + }, + ...employeeWorkspaceChat.onyxSuccessData + ]; + const failureData = [ + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${workspaceChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChatReportID}`, + value: null, + }, + ]; + + // Next we need to convert the IOU report to Expense report and clean up the DM chat + // Get the 1on1 chat where the request was originally made + const chatReportID = iouReport.chatReportID; ReportUtils.getReport(iouReport.chatReportID); + const reportPreviewID = iouReport.parentReportActionID; + + + const membersData = [{ + accountID: employeeAccountID, + email: employeeEmail, + workspaceChatReportID: employeeWorkspaceChat.reportCreationData[employeeEmail].reportID, + workspaceChatCreatedReportActionID: employeeWorkspaceChat.reportCreationData[employeeEmail].reportActionID, + }]; + + API.write( + 'BottomUpCollectFlow', + { + policyID, + announceChatReportID, + adminsChatReportID, + expenseChatReportID: workspaceChatReportID, + ownerEmail: sessionEmail, + makeMeAdmin: false, + policyName: workspaceName, + type: CONST.POLICY.TYPE.TEAM, + announceCreatedReportActionID, + adminsCreatedReportActionID, + expenseCreatedReportActionID: workspaceChatCreatedReportActionID, + customUnitID, + customUnitRateID, + iouReportID: iouReport.reportID, + membersData: JSON.stringify(membersData), + }, + {optimisticData, successData, failureData}, + ); + // Navigation.dismissModal(CONST.TEACHERS_UNITE.PUBLIC_ROOM_ID); +} + export { removeMembers, addMembersToWorkspace, @@ -1320,4 +1581,5 @@ export { openDraftWorkspaceRequest, buildOptimisticPolicyRecentlyUsedCategories, createDraftInitialWorkspace, + startCollectBottomUpFlow, }; From 02871ac9d817b17ca21a4e8bf1cbd4f38469403f Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Fri, 27 Oct 2023 18:17:30 +0200 Subject: [PATCH 019/105] Add FormProvider to BankAccountManualStep --- .../BankAccountManualStep.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/pages/ReimbursementAccount/BankAccountManualStep.js b/src/pages/ReimbursementAccount/BankAccountManualStep.js index 9d0b0c1f5bed..dd46bd3d22c0 100644 --- a/src/pages/ReimbursementAccount/BankAccountManualStep.js +++ b/src/pages/ReimbursementAccount/BankAccountManualStep.js @@ -15,10 +15,11 @@ import {withLocalizePropTypes} from '../../components/withLocalize'; import * as ValidationUtils from '../../libs/ValidationUtils'; import ONYXKEYS from '../../ONYXKEYS'; import exampleCheckImage from './exampleCheckImage'; -import Form from '../../components/Form'; import shouldDelayFocus from '../../libs/shouldDelayFocus'; import ScreenWrapper from '../../components/ScreenWrapper'; import StepPropTypes from './StepPropTypes'; +import FormProvider from '../../components/Form/FormProvider'; +import InputWrapper from '../../components/Form/InputWrapper'; const propTypes = { ..._.omit(StepPropTypes, _.keys(withLocalizePropTypes)), @@ -84,7 +85,7 @@ function BankAccountManualStep(props) { guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_BANK_ACCOUNT} onBackButtonPress={props.onBackButtonPress} /> -
- - - - + ); } From e44331715595fa03d6f443350ba99973fb9aa65a Mon Sep 17 00:00:00 2001 From: tienifr Date: Sat, 28 Oct 2023 16:13:07 +0700 Subject: [PATCH 020/105] fix: 30465 --- src/components/TagPicker/index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/TagPicker/index.js b/src/components/TagPicker/index.js index 05eca664bd0f..ea29305c16b9 100644 --- a/src/components/TagPicker/index.js +++ b/src/components/TagPicker/index.js @@ -17,7 +17,8 @@ function TagPicker({selectedTag, tag, policyTags, policyRecentlyUsedTags, onSubm const policyRecentlyUsedTagsList = lodashGet(policyRecentlyUsedTags, tag, []); const policyTagList = PolicyUtils.getTagList(policyTags, tag); - const policyTagsCount = _.size(_.filter(policyTagList, (policyTag) => policyTag.enabled)); + const enabledTags = _.filter(policyTagList, (policyTag) => policyTag.enabled); + const policyTagsCount = _.size(enabledTags); const isTagsCountBelowThreshold = policyTagsCount < CONST.TAG_LIST_THRESHOLD; const shouldShowTextInput = !isTagsCountBelowThreshold; @@ -38,14 +39,14 @@ function TagPicker({selectedTag, tag, policyTags, policyRecentlyUsedTags, onSubm const initialFocusedIndex = useMemo(() => { if (isTagsCountBelowThreshold && selectedOptions.length > 0) { - return _.chain(policyTagList) + return _.chain(enabledTags) .values() .findIndex((policyTag) => policyTag.name === selectedOptions[0].name, true) .value(); } return 0; - }, [policyTagList, selectedOptions, isTagsCountBelowThreshold]); + }, [enabledTags, selectedOptions, isTagsCountBelowThreshold]); const sections = useMemo( () => From 42e4ba3fcf441b58dff9e254121af0787da03316 Mon Sep 17 00:00:00 2001 From: Alberto Date: Mon, 30 Oct 2023 12:10:04 +0100 Subject: [PATCH 021/105] import update --- src/components/DotIndicatorMessage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/DotIndicatorMessage.js b/src/components/DotIndicatorMessage.js index bfd572bc50b3..e02635f35973 100644 --- a/src/components/DotIndicatorMessage.js +++ b/src/components/DotIndicatorMessage.js @@ -9,9 +9,9 @@ import themeColors from '@styles/themes/default'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; import Text from './Text'; -import CONST from '../CONST'; +import CONST from '@src/CONST'; import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; -import fileDownload from '../libs/fileDownload'; +import fileDownload from '@libs/fileDownload'; const propTypes = { /** From 8b0382b5805d11020cea62925a4cceace0521c6b Mon Sep 17 00:00:00 2001 From: Alberto Date: Mon, 30 Oct 2023 12:18:51 +0100 Subject: [PATCH 022/105] reorder --- src/components/DotIndicatorMessage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/DotIndicatorMessage.js b/src/components/DotIndicatorMessage.js index e02635f35973..fa8c7bd67c97 100644 --- a/src/components/DotIndicatorMessage.js +++ b/src/components/DotIndicatorMessage.js @@ -3,15 +3,15 @@ import React from 'react'; import {View} from 'react-native'; import _ from 'underscore'; import * as Localize from '@libs/Localize'; +import fileDownload from '@libs/fileDownload'; import stylePropTypes from '@styles/stylePropTypes'; import styles from '@styles/styles'; import themeColors from '@styles/themes/default'; +import CONST from '@src/CONST'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; import Text from './Text'; -import CONST from '@src/CONST'; import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; -import fileDownload from '@libs/fileDownload'; const propTypes = { /** From f8ce4eb16ff1c6dee199ffc7c002ac62da6a12ed Mon Sep 17 00:00:00 2001 From: Alberto Date: Mon, 30 Oct 2023 12:44:12 +0100 Subject: [PATCH 023/105] prettier --- src/components/DotIndicatorMessage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/DotIndicatorMessage.js b/src/components/DotIndicatorMessage.js index fa8c7bd67c97..31b96899ef56 100644 --- a/src/components/DotIndicatorMessage.js +++ b/src/components/DotIndicatorMessage.js @@ -2,16 +2,16 @@ import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; import _ from 'underscore'; -import * as Localize from '@libs/Localize'; import fileDownload from '@libs/fileDownload'; +import * as Localize from '@libs/Localize'; import stylePropTypes from '@styles/stylePropTypes'; import styles from '@styles/styles'; import themeColors from '@styles/themes/default'; import CONST from '@src/CONST'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; -import Text from './Text'; import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; +import Text from './Text'; const propTypes = { /** From 241793718d6f274ea70e229b915bee5e4961946c Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Mon, 30 Oct 2023 13:18:37 +0100 Subject: [PATCH 024/105] fix CheckboxWithLabel forwardedRef typing --- src/components/CheckboxWithLabel.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/CheckboxWithLabel.js b/src/components/CheckboxWithLabel.js index 4bffadecb733..f18ec346dfa2 100644 --- a/src/components/CheckboxWithLabel.js +++ b/src/components/CheckboxWithLabel.js @@ -7,6 +7,7 @@ import variables from '@styles/variables'; import Checkbox from './Checkbox'; import FormHelpMessage from './FormHelpMessage'; import PressableWithFeedback from './Pressable/PressableWithFeedback'; +import refPropTypes from './refPropTypes'; import Text from './Text'; /** @@ -54,7 +55,7 @@ const propTypes = { defaultValue: PropTypes.bool, /** React ref being forwarded to the Checkbox input */ - forwardedRef: PropTypes.func, + forwardedRef: refPropTypes, /** The ID used to uniquely identify the input in a Form */ /* eslint-disable-next-line react/no-unused-prop-types */ From b1b83496b5473ef4a813da9c0a4f00155755d30b Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Wed, 1 Nov 2023 17:06:51 +0000 Subject: [PATCH 025/105] Fix merge conflict --- src/components/AddPaymentMethodMenu.js | 2 +- src/libs/actions/Policy.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/AddPaymentMethodMenu.js b/src/components/AddPaymentMethodMenu.js index a0b839e736de..813e27e3b821 100644 --- a/src/components/AddPaymentMethodMenu.js +++ b/src/components/AddPaymentMethodMenu.js @@ -84,7 +84,7 @@ function AddPaymentMethodMenu(props) { props.onItemSelected(CONST.PAYMENT_METHODS.BANK_ACCOUNT); }, }, - ...(ReportUtils.isIOUReport(this.props.iouReport) && ReportActionsUtils.hasRequestFromPayer(lodashGet(this.props.iouReport, 'reportID', 0), this.props.session.accountID) + ...(ReportUtils.isIOUReport(props.iouReport) && ReportActionsUtils.hasRequestFromPayer(lodashGet(props.iouReport, 'reportID', 0), props.session.accountID) ? [ { text: props.translate('common.businessBankAccount'), diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js index 0187af2c76a8..06b8970794d2 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -1598,7 +1598,7 @@ function startCollectBottomUpFlow(iouReport, firstName, lastName) { ]; API.write( - 'BottomUpCollectFlow', + 'CreateWorkspaceFromIOUPayment', { policyID, announceChatReportID, From c814adedbb12d5d2d5be64d8d8ec4b0aa412ce45 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Thu, 2 Nov 2023 16:32:42 +0000 Subject: [PATCH 026/105] Fix issues in the flow --- src/components/AddPaymentMethodMenu.js | 4 +-- src/components/KYCWall/BaseKYCWall.js | 1 + src/components/MoneyReportHeader.js | 2 -- .../MoneyRequestConfirmationList.js | 1 - .../ReportActionItem/ReportPreview.js | 1 - src/components/SettlementButton.js | 32 ++----------------- 6 files changed, 5 insertions(+), 36 deletions(-) diff --git a/src/components/AddPaymentMethodMenu.js b/src/components/AddPaymentMethodMenu.js index 813e27e3b821..186cab115a39 100644 --- a/src/components/AddPaymentMethodMenu.js +++ b/src/components/AddPaymentMethodMenu.js @@ -84,11 +84,11 @@ function AddPaymentMethodMenu(props) { props.onItemSelected(CONST.PAYMENT_METHODS.BANK_ACCOUNT); }, }, - ...(ReportUtils.isIOUReport(props.iouReport) && ReportActionsUtils.hasRequestFromPayer(lodashGet(props.iouReport, 'reportID', 0), props.session.accountID) + ...(ReportUtils.isIOUReport(props.iouReport) && !ReportActionsUtils.hasRequestFromPayer(lodashGet(props.iouReport, 'reportID', 0), props.session.accountID) ? [ { text: props.translate('common.businessBankAccount'), - icon: Expensicons.Workspace, + icon: Expensicons.Building, onSelected: () => props.onItemSelected(CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT), }, ] diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js index 6093aae43d35..034f62466979 100644 --- a/src/components/KYCWall/BaseKYCWall.js +++ b/src/components/KYCWall/BaseKYCWall.js @@ -10,6 +10,7 @@ import Navigation from '@libs/Navigation/Navigation'; import * as PaymentUtils from '@libs/PaymentUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as PaymentMethods from '@userActions/PaymentMethods'; +import * as Policy from '@userActions/Policy'; import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; diff --git a/src/components/MoneyReportHeader.js b/src/components/MoneyReportHeader.js index a8fb5390ebb6..b3f0c52d5cdc 100644 --- a/src/components/MoneyReportHeader.js +++ b/src/components/MoneyReportHeader.js @@ -118,7 +118,6 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt onPress={(paymentType) => IOU.payMoneyRequest(paymentType, chatReport, moneyRequestReport)} enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS} addBankAccountRoute={bankAccountRoute} - shouldShowPaymentOptions style={[styles.pv2]} formattedAmount={formattedAmount} /> @@ -163,7 +162,6 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt onPress={(paymentType) => IOU.payMoneyRequest(paymentType, chatReport, moneyRequestReport)} enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS} addBankAccountRoute={bankAccountRoute} - shouldShowPaymentOptions formattedAmount={formattedAmount} /> diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index da1e03bd28e8..a8b4f95cae69 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -512,7 +512,6 @@ function MoneyRequestConfirmationList(props) { addDebitCardRoute={ROUTES.IOU_SEND_ADD_DEBIT_CARD} currency={props.iouCurrencyCode} policyID={props.policyID} - shouldShowPaymentOptions buttonSize={CONST.DROPDOWN_BUTTON_SIZE.LARGE} kycWallAnchorAlignment={{ horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index 00a4526b382f..b90b509fdefb 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -245,7 +245,6 @@ function ReportPreview(props) { onPress={(paymentType) => IOU.payMoneyRequest(paymentType, props.chatReport, props.iouReport)} enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS} addBankAccountRoute={bankAccountRoute} - shouldShowPaymentOptions style={[styles.mt3]} kycWallAnchorAlignment={{ horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, diff --git a/src/components/SettlementButton.js b/src/components/SettlementButton.js index d2030eac8d7d..53b85b5e0f05 100644 --- a/src/components/SettlementButton.js +++ b/src/components/SettlementButton.js @@ -40,9 +40,6 @@ const propTypes = { /** The route to redirect if user does not have a payment method setup */ enablePaymentsRoute: PropTypes.string.isRequired, - /** Should we show the payment options? */ - shouldShowPaymentOptions: PropTypes.bool, - /** The last payment method used per policy */ nvp_lastPaymentMethod: PropTypes.objectOf(PropTypes.string), @@ -97,7 +94,6 @@ const defaultProps = { betas: CONST.EMPTY_ARRAY, iouReport: CONST.EMPTY_OBJECT, nvp_lastPaymentMethod: CONST.EMPTY_OBJECT, - shouldShowPaymentOptions: false, style: [], policyID: '', formattedAmount: '', @@ -130,7 +126,6 @@ function SettlementButton({ onPress, pressOnEnter, policyID, - shouldShowPaymentOptions, style, }) { const {translate} = useLocalize(); @@ -165,33 +160,10 @@ function SettlementButton({ // To achieve the one tap pay experience we need to choose the correct payment type as default, // if user already paid for some request or expense, let's use the last payment method or use default. let paymentMethod = nvp_lastPaymentMethod[policyID] || ''; - if (!shouldShowPaymentOptions) { - if (!paymentMethod) { - // In case the user hasn't paid a request yet, let's default to VBBA payment type in case of expense reports - if (isExpenseReport) { - paymentMethod = CONST.IOU.PAYMENT_TYPE.VBBA; - } else if (canUseWallet) { - // If they have Wallet set up, use that payment method as default - paymentMethod = CONST.IOU.PAYMENT_TYPE.EXPENSIFY; - } else { - paymentMethod = CONST.IOU.PAYMENT_TYPE.ELSEWHERE; - } - } - - // In case of the settlement button in the report preview component, we do not show payment options and the label for Wallet and ACH type is simply "Pay". - return [ - { - ...paymentMethods[paymentMethod], - text: paymentMethod === CONST.IOU.PAYMENT_TYPE.ELSEWHERE ? translate('iou.payElsewhere') : translate('iou.pay'), - }, - ]; - } if (canUseWallet) { buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.EXPENSIFY]); } - if (isExpenseReport) { - buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.VBBA]); - } + buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.VBBA]); buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.ELSEWHERE]); // Put the preferred payment method to the front of the array so its shown as default @@ -199,7 +171,7 @@ function SettlementButton({ return _.sortBy(buttonOptions, (method) => (method.value === paymentMethod ? 0 : 1)); } return buttonOptions; - }, [betas, currency, formattedAmount, iouReport, nvp_lastPaymentMethod, policyID, shouldShowPaymentOptions, translate]); + }, [betas, currency, formattedAmount, iouReport, nvp_lastPaymentMethod, policyID, translate]); const selectPaymentType = (event, iouPaymentType, triggerKYCFlow) => { if (iouPaymentType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY || iouPaymentType === CONST.IOU.PAYMENT_TYPE.VBBA) { From cfe141bd6af6755d84cb1fc677bb7346372e9a48 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Thu, 2 Nov 2023 16:33:19 +0000 Subject: [PATCH 027/105] Update the personal bank account const name --- src/CONST.ts | 2 +- src/components/AddPaymentMethodMenu.js | 2 +- src/components/KYCWall/BaseKYCWall.js | 4 ++-- src/libs/PaymentUtils.ts | 2 +- src/libs/actions/PaymentMethods.ts | 10 +++++----- .../Wallet/ChooseTransferAccountPage.js | 4 ++-- src/pages/settings/Wallet/PaymentMethodList.js | 6 +++--- .../settings/Wallet/TransferBalancePage.js | 6 +++--- .../settings/Wallet/WalletPage/WalletPage.js | 18 +++++++++--------- .../settings/Wallet/walletTransferPropTypes.js | 2 +- src/types/onyx/BankAccount.ts | 2 +- src/types/onyx/WalletTransfer.ts | 2 +- 12 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index e270f0066139..cb12a2a5f746 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1111,7 +1111,7 @@ const CONST = { PAYMENT_METHODS: { DEBIT_CARD: 'debitCard', - BANK_ACCOUNT: 'bankAccount', + PERSONAL_BANK_ACCOUNT: 'bankAccount', BUSINESS_BANK_ACCOUNT: 'businessBankAccount', }, diff --git a/src/components/AddPaymentMethodMenu.js b/src/components/AddPaymentMethodMenu.js index 186cab115a39..f6ed7e8254a0 100644 --- a/src/components/AddPaymentMethodMenu.js +++ b/src/components/AddPaymentMethodMenu.js @@ -81,7 +81,7 @@ function AddPaymentMethodMenu(props) { text: props.translate('common.personalBankAccount'), icon: Expensicons.Bank, onSelected: () => { - props.onItemSelected(CONST.PAYMENT_METHODS.BANK_ACCOUNT); + props.onItemSelected(CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT); }, }, ...(ReportUtils.isIOUReport(props.iouReport) && !ReportActionsUtils.hasRequestFromPayer(lodashGet(props.iouReport, 'reportID', 0), props.session.accountID) diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js index 034f62466979..92df53353cf6 100644 --- a/src/components/KYCWall/BaseKYCWall.js +++ b/src/components/KYCWall/BaseKYCWall.js @@ -94,7 +94,7 @@ class KYCWall extends React.Component { */ selectPaymentMethod(paymentMethod) { this.props.onSelectPaymentMethod(paymentMethod); - if (paymentMethod === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + if (paymentMethod === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { Navigation.navigate(this.props.addBankAccountRoute); } else if (paymentMethod === CONST.PAYMENT_METHODS.DEBIT_CARD) { Navigation.navigate(this.props.addDebitCardRoute); @@ -138,7 +138,7 @@ class KYCWall extends React.Component { ) { Log.info('[KYC Wallet] User does not have valid payment method'); if (!this.props.shouldIncludeDebitCard) { - this.selectPaymentMethod(CONST.PAYMENT_METHODS.BANK_ACCOUNT); + this.selectPaymentMethod(CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT); return; } const clickedElementLocation = getClickedTargetLocation(targetElement); diff --git a/src/libs/PaymentUtils.ts b/src/libs/PaymentUtils.ts index b37db2584394..5bd70fee4d83 100644 --- a/src/libs/PaymentUtils.ts +++ b/src/libs/PaymentUtils.ts @@ -26,7 +26,7 @@ function hasExpensifyPaymentMethod(fundList: Record, bankAccountLi function getPaymentMethodDescription(accountType: AccountType, account: BankAccount['accountData'] | Fund['accountData']): string { if (account) { - if (accountType === CONST.PAYMENT_METHODS.BANK_ACCOUNT && 'accountNumber' in account) { + if (accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT && 'accountNumber' in account) { return `${Localize.translateLocal('paymentMethodList.accountLastFour')} ${account.accountNumber?.slice(-4)}`; } if (accountType === CONST.PAYMENT_METHODS.DEBIT_CARD && 'cardNumber' in account) { diff --git a/src/libs/actions/PaymentMethods.ts b/src/libs/actions/PaymentMethods.ts index bcc5d8142470..bc2ce95c8a43 100644 --- a/src/libs/actions/PaymentMethods.ts +++ b/src/libs/actions/PaymentMethods.ts @@ -81,7 +81,7 @@ function getMakeDefaultPaymentOnyxData( key: ONYXKEYS.USER_WALLET, value: { walletLinkedAccountID: bankAccountID || fundID, - walletLinkedAccountType: bankAccountID ? CONST.PAYMENT_METHODS.BANK_ACCOUNT : CONST.PAYMENT_METHODS.DEBIT_CARD, + walletLinkedAccountType: bankAccountID ? CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT : CONST.PAYMENT_METHODS.DEBIT_CARD, // Only clear the error if this is optimistic data. If this is failure data, we do not want to clear the error that came from the server. errors: null, }, @@ -91,7 +91,7 @@ function getMakeDefaultPaymentOnyxData( key: ONYXKEYS.USER_WALLET, value: { walletLinkedAccountID: bankAccountID || fundID, - walletLinkedAccountType: bankAccountID ? CONST.PAYMENT_METHODS.BANK_ACCOUNT : CONST.PAYMENT_METHODS.DEBIT_CARD, + walletLinkedAccountType: bankAccountID ? CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT : CONST.PAYMENT_METHODS.DEBIT_CARD, }, }, ]; @@ -99,7 +99,7 @@ function getMakeDefaultPaymentOnyxData( if (previousPaymentMethod?.methodID) { onyxData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: previousPaymentMethod.accountType === CONST.PAYMENT_METHODS.BANK_ACCOUNT ? ONYXKEYS.BANK_ACCOUNT_LIST : ONYXKEYS.FUND_LIST, + key: previousPaymentMethod.accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT ? ONYXKEYS.BANK_ACCOUNT_LIST : ONYXKEYS.FUND_LIST, value: { [previousPaymentMethod.methodID]: { isDefault: !isOptimisticData, @@ -111,7 +111,7 @@ function getMakeDefaultPaymentOnyxData( if (currentPaymentMethod?.methodID) { onyxData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: currentPaymentMethod.accountType === CONST.PAYMENT_METHODS.BANK_ACCOUNT ? ONYXKEYS.BANK_ACCOUNT_LIST : ONYXKEYS.FUND_LIST, + key: currentPaymentMethod.accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT ? ONYXKEYS.BANK_ACCOUNT_LIST : ONYXKEYS.FUND_LIST, value: { [currentPaymentMethod.methodID]: { isDefault: isOptimisticData, @@ -223,7 +223,7 @@ function clearDebitCardFormErrorAndSubmit() { * */ function transferWalletBalance(paymentMethod: PaymentMethod) { - const paymentMethodIDKey = paymentMethod.accountType === CONST.PAYMENT_METHODS.BANK_ACCOUNT ? CONST.PAYMENT_METHOD_ID_KEYS.BANK_ACCOUNT : CONST.PAYMENT_METHOD_ID_KEYS.DEBIT_CARD; + const paymentMethodIDKey = paymentMethod.accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT ? CONST.PAYMENT_METHOD_ID_KEYS.BANK_ACCOUNT : CONST.PAYMENT_METHOD_ID_KEYS.DEBIT_CARD; type TransferWalletBalanceParameters = Partial, number | undefined>>; diff --git a/src/pages/settings/Wallet/ChooseTransferAccountPage.js b/src/pages/settings/Wallet/ChooseTransferAccountPage.js index a44e21390b80..9fe95f477ac9 100644 --- a/src/pages/settings/Wallet/ChooseTransferAccountPage.js +++ b/src/pages/settings/Wallet/ChooseTransferAccountPage.js @@ -36,7 +36,7 @@ function ChooseTransferAccountPage(props) { * @param {Object} account of the selected account data */ const selectAccountAndNavigateBack = (event, accountType, account) => { - PaymentMethods.saveWalletTransferAccountTypeAndID(accountType, accountType === CONST.PAYMENT_METHODS.BANK_ACCOUNT ? account.bankAccountID : account.fundID); + PaymentMethods.saveWalletTransferAccountTypeAndID(accountType, accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT ? account.bankAccountID : account.fundID); Navigation.goBack(ROUTES.SETTINGS_WALLET_TRANSFER_BALANCE); }; @@ -69,7 +69,7 @@ function ChooseTransferAccountPage(props) { method.accountType === CONST.PAYMENT_METHODS.BANK_ACCOUNT || method.accountType === CONST.PAYMENT_METHODS.DEBIT_CARD, + (method) => method.accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT || method.accountType === CONST.PAYMENT_METHODS.DEBIT_CARD, ).length; return defaultablePaymentMethodCount > 1; } diff --git a/src/pages/settings/Wallet/TransferBalancePage.js b/src/pages/settings/Wallet/TransferBalancePage.js index 1df07365a1f6..fbcb7a9dc1df 100644 --- a/src/pages/settings/Wallet/TransferBalancePage.js +++ b/src/pages/settings/Wallet/TransferBalancePage.js @@ -84,7 +84,7 @@ function TransferBalancePage(props) { title: props.translate('transferAmountPage.ach'), description: props.translate('transferAmountPage.achSummary'), icon: Expensicons.Bank, - type: CONST.PAYMENT_METHODS.BANK_ACCOUNT, + type: CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, }, ]; @@ -145,7 +145,7 @@ function TransferBalancePage(props) { { - if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { return paymentMethod.selectedPaymentMethod.bankAccountID; } if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { @@ -150,12 +150,12 @@ function WalletPage({bankAccountList, betas, cardList, fundList, isLoadingPaymen // The delete/default menu if (accountType) { let formattedSelectedPaymentMethod; - if (accountType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + if (accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { formattedSelectedPaymentMethod = { title: account.addressName, icon: account.icon, description: PaymentUtils.getPaymentMethodDescription(accountType, account), - type: CONST.PAYMENT_METHODS.BANK_ACCOUNT, + type: CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, }; } else if (accountType === CONST.PAYMENT_METHODS.DEBIT_CARD) { formattedSelectedPaymentMethod = { @@ -200,7 +200,7 @@ function WalletPage({bankAccountList, betas, cardList, fundList, isLoadingPaymen return; } - if (paymentType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + if (paymentType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { BankAccounts.openPersonalBankAccountSetupView(); return; } @@ -226,7 +226,7 @@ function WalletPage({bankAccountList, betas, cardList, fundList, isLoadingPaymen const previousPaymentMethod = _.find(paymentMethods, (method) => method.isDefault); const currentPaymentMethod = _.find(paymentMethods, (method) => method.methodID === paymentMethod.methodID); - if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { PaymentMethods.makeDefaultPaymentMethod(paymentMethod.selectedPaymentMethod.bankAccountID, null, previousPaymentMethod, currentPaymentMethod); } else if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { PaymentMethods.makeDefaultPaymentMethod(null, paymentMethod.selectedPaymentMethod.fundID, previousPaymentMethod, currentPaymentMethod); @@ -241,7 +241,7 @@ function WalletPage({bankAccountList, betas, cardList, fundList, isLoadingPaymen ]); const deletePaymentMethod = useCallback(() => { - if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { BankAccounts.deletePaymentBankAccount(paymentMethod.selectedPaymentMethod.bankAccountID); } else if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { PaymentMethods.deletePaymentCard(paymentMethod.selectedPaymentMethod.fundID); @@ -292,7 +292,7 @@ function WalletPage({bankAccountList, betas, cardList, fundList, isLoadingPaymen // We should reset selected payment method state values and close corresponding modals if the selected payment method is deleted let shouldResetPaymentMethodData = false; - if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT && _.isEmpty(bankAccountList[paymentMethod.methodID])) { + if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT && _.isEmpty(bankAccountList[paymentMethod.methodID])) { shouldResetPaymentMethodData = true; } else if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD && _.isEmpty(fundList[paymentMethod.methodID])) { shouldResetPaymentMethodData = true; @@ -308,7 +308,7 @@ function WalletPage({bankAccountList, betas, cardList, fundList, isLoadingPaymen const shouldShowMakeDefaultButton = !paymentMethod.isSelectedPaymentMethodDefault && Permissions.canUseWallet(betas) && - !(paymentMethod.formattedSelectedPaymentMethod.type === CONST.PAYMENT_METHODS.BANK_ACCOUNT && paymentMethod.selectedPaymentMethod.type === CONST.BANK_ACCOUNT.TYPE.BUSINESS); + !(paymentMethod.formattedSelectedPaymentMethod.type === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT && paymentMethod.selectedPaymentMethod.type === CONST.BANK_ACCOUNT.TYPE.BUSINESS); // Determines whether or not the modal popup is mounted from the bottom of the screen instead of the side mount on Web or Desktop screens const isPopoverBottomMount = anchorPosition.anchorPositionTop === 0 || isSmallScreenWidth; @@ -362,7 +362,7 @@ function WalletPage({bankAccountList, betas, cardList, fundList, isLoadingPaymen navigateToWalletOrTransferBalancePage(source)} onSelectPaymentMethod={(selectedPaymentMethod) => { - if (hasActivatedWallet || selectedPaymentMethod !== CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + if (hasActivatedWallet || selectedPaymentMethod !== CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { return; } // To allow upgrading to a gold wallet, continue with the KYC flow after adding a bank account diff --git a/src/pages/settings/Wallet/walletTransferPropTypes.js b/src/pages/settings/Wallet/walletTransferPropTypes.js index 38c65fcfd703..9e25213382e9 100644 --- a/src/pages/settings/Wallet/walletTransferPropTypes.js +++ b/src/pages/settings/Wallet/walletTransferPropTypes.js @@ -7,7 +7,7 @@ const walletTransferPropTypes = PropTypes.shape({ selectedAccountID: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), /** Type to filter the payment Method list */ - filterPaymentMethodType: PropTypes.oneOf([CONST.PAYMENT_METHODS.DEBIT_CARD, CONST.PAYMENT_METHODS.BANK_ACCOUNT, '']), + filterPaymentMethodType: PropTypes.oneOf([CONST.PAYMENT_METHODS.DEBIT_CARD, CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, '']), /** Whether the success screen is shown to user. */ shouldShowSuccess: PropTypes.bool, diff --git a/src/types/onyx/BankAccount.ts b/src/types/onyx/BankAccount.ts index 4a7b6dac7387..2b2bee7df6b0 100644 --- a/src/types/onyx/BankAccount.ts +++ b/src/types/onyx/BankAccount.ts @@ -4,7 +4,7 @@ import * as OnyxCommon from './OnyxCommon'; type BankAccount = { /** The bank account type */ - accountType?: typeof CONST.PAYMENT_METHODS.BANK_ACCOUNT; + accountType?: typeof CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT; /** string like 'Account ending in XXXX' */ description?: string; diff --git a/src/types/onyx/WalletTransfer.ts b/src/types/onyx/WalletTransfer.ts index e1b1468d6536..cebea543884b 100644 --- a/src/types/onyx/WalletTransfer.ts +++ b/src/types/onyx/WalletTransfer.ts @@ -25,7 +25,7 @@ type WalletTransfer = { paymentMethodType?: ValueOf>; }; -type FilterMethodPaymentType = typeof CONST.PAYMENT_METHODS.DEBIT_CARD | typeof CONST.PAYMENT_METHODS.BANK_ACCOUNT | null; +type FilterMethodPaymentType = typeof CONST.PAYMENT_METHODS.DEBIT_CARD | typeof CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT | null; export default WalletTransfer; From ae33908060085d29e0ae3572b6bce00fbfa54b61 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Thu, 2 Nov 2023 23:31:23 +0000 Subject: [PATCH 028/105] Flow clean up --- src/CONST.ts | 1 + src/components/KYCWall/BaseKYCWall.js | 2 +- src/libs/ReportUtils.js | 45 +++++++++++++++++++++++ src/libs/actions/Policy.js | 41 ++++++++++++++------- src/pages/home/report/ReportActionItem.js | 2 +- 5 files changed, 75 insertions(+), 16 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index cb12a2a5f746..08a4252238d6 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -503,6 +503,7 @@ const CONST = { CREATED: 'CREATED', IOU: 'IOU', MODIFIEDEXPENSE: 'MODIFIEDEXPENSE', + MOVED: 'MOVED', REIMBURSEMENTQUEUED: 'REIMBURSEMENTQUEUED', RENAMED: 'RENAMED', REPORTPREVIEW: 'REPORTPREVIEW', diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js index 92df53353cf6..46479d554bdf 100644 --- a/src/components/KYCWall/BaseKYCWall.js +++ b/src/components/KYCWall/BaseKYCWall.js @@ -99,7 +99,7 @@ class KYCWall extends React.Component { } else if (paymentMethod === CONST.PAYMENT_METHODS.DEBIT_CARD) { Navigation.navigate(this.props.addDebitCardRoute); } else if (paymentMethod === CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT) { - Policy.startCollectFlow(this.props.iouReport); + Policy.createWorkspaceFromIOUPayment(this.props.iouReport); } } diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 10630d7fb122..d3b16a73f16f 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -2612,6 +2612,7 @@ function buildOptimisticIOUReportAction( whisperedToAccountIDs: _.contains([CONST.IOU.RECEIPT_STATE.SCANREADY, CONST.IOU.RECEIPT_STATE.SCANNING], receipt.state) ? [currentUserAccountID] : [], }; } + /** * Builds an optimistic APPROVED report action with a randomly generated reportActionID. * @@ -2650,6 +2651,49 @@ function buildOptimisticApprovedReportAction(amount, currency, expenseReportID) }; } +/** + * Builds an optimistic MOVED report action with a randomly generated reportActionID. + * This action is used when we move reports across workspaces. + * + * @param {String} fromPolicyID + * @param {String} toPolicyID + * @param {Number} newParentReportID + * @param {Number} movedReportID An ID of the report we are moveing across workspaces + * + * @returns {Object} + */ +function buildOptimisticMovedReportAction(fromPolicyID, toPolicyID, newParentReportID, movedReportID) { + const originalMessage = { + fromPolicyID, + toPolicyID, + newParentReportID, + movedReportID, + }; + + const policyName = getPolicyName(allReports[`${ONYXKEYS.COLLECTION.REPORT}${newParentReportID}`]); + + return { + actionName: CONST.REPORT.ACTIONS.TYPE.MOVED, + actorAccountID: currentUserAccountID, + automatic: false, + avatar: lodashGet(currentUserPersonalDetails, 'avatar', UserUtils.getDefaultAvatarURL(currentUserAccountID)), + isAttachment: false, + originalMessage, + message: "moved report", + person: [ + { + style: 'strong', + text: lodashGet(currentUserPersonalDetails, 'displayName', currentUserEmail), + type: 'TEXT', + }, + ], + reportActionID: NumberUtils.rand64(), + shouldShow: true, + created: DateUtils.getDBTime(), + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }; +} + /** * Builds an optimistic SUBMITTED report action with a randomly generated reportActionID. * @@ -4216,6 +4260,7 @@ export { buildOptimisticEditedTaskReportAction, buildOptimisticIOUReport, buildOptimisticApprovedReportAction, + buildOptimisticMovedReportAction, buildOptimisticSubmittedReportAction, buildOptimisticExpenseReport, buildOptimisticIOUReportAction, diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js index 941fa814a9b4..a022d8f61f18 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -1360,7 +1360,7 @@ function buildOptimisticPolicyRecentlyUsedCategories(policyID, category) { return lodashUnion([category], policyRecentlyUsedCategories); } -function startCollectBottomUpFlow(iouReport, firstName, lastName) { +function createWorkspaceFromIOUPayment(iouReport) { // This flow only works for IOU reports if (!ReportUtils.isIOUReport(iouReport)) { return; @@ -1544,6 +1544,7 @@ function startCollectBottomUpFlow(iouReport, firstName, lastName) { }, ...employeeWorkspaceChat.onyxSuccessData, ]; + const failureData = [ { onyxMethod: Onyx.METHOD.SET, @@ -1585,17 +1586,29 @@ function startCollectBottomUpFlow(iouReport, firstName, lastName) { // Next we need to convert the IOU report to Expense report and clean up the DM chat // Get the 1on1 chat where the request was originally made const chatReportID = iouReport.chatReportID; - ReportUtils.getReport(iouReport.chatReportID); + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + value: { + ...iouReport, + type: CONST.REPORT.TYPE.EXPENSE + }, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + value: iouReport, + }); + + const chatReport = ReportUtils.getReport(iouReport.chatReportID); const reportPreviewID = iouReport.parentReportActionID; - const membersData = [ - { - accountID: employeeAccountID, - email: employeeEmail, - workspaceChatReportID: employeeWorkspaceChat.reportCreationData[employeeEmail].reportID, - workspaceChatCreatedReportActionID: employeeWorkspaceChat.reportCreationData[employeeEmail].reportActionID, - }, - ]; + const memberData = { + accountID: Number(employeeAccountID), + email: employeeEmail, + workspaceChatReportID: Number(employeeWorkspaceChat.reportCreationData[employeeEmail].reportID), + workspaceChatCreatedReportActionID: Number(employeeWorkspaceChat.reportCreationData[employeeEmail].reportActionID), + }; API.write( 'CreateWorkspaceFromIOUPayment', @@ -1604,7 +1617,7 @@ function startCollectBottomUpFlow(iouReport, firstName, lastName) { announceChatReportID, adminsChatReportID, expenseChatReportID: workspaceChatReportID, - ownerEmail: sessionEmail, + ownerEmail: '', makeMeAdmin: false, policyName: workspaceName, type: CONST.POLICY.TYPE.TEAM, @@ -1614,11 +1627,11 @@ function startCollectBottomUpFlow(iouReport, firstName, lastName) { customUnitID, customUnitRateID, iouReportID: iouReport.reportID, - membersData: JSON.stringify(membersData), + memberData: JSON.stringify(memberData), + reportActionID: 0, }, {optimisticData, successData, failureData}, ); - // Navigation.dismissModal(CONST.TEACHERS_UNITE.PUBLIC_ROOM_ID); } export { @@ -1647,11 +1660,11 @@ export { openWorkspaceMembersPage, openWorkspaceInvitePage, removeWorkspace, + createWorkspaceFromIOUPayment, setWorkspaceInviteMembersDraft, clearErrors, dismissAddedWithPrimaryLoginMessages, openDraftWorkspaceRequest, buildOptimisticPolicyRecentlyUsedCategories, createDraftInitialWorkspace, - startCollectBottomUpFlow, }; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index a7a3bc0739f3..eb0ae4f8978a 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -422,7 +422,7 @@ function ReportActionItem(props) { isHidden={isHidden} style={[ _.contains( - [..._.values(CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG), CONST.REPORT.ACTIONS.TYPE.IOU, CONST.REPORT.ACTIONS.TYPE.APPROVED], + [..._.values(CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG), CONST.REPORT.ACTIONS.TYPE.IOU, CONST.REPORT.ACTIONS.TYPE.APPROVED, CONST.REPORT.ACTIONS.TYPE.MOVED], props.action.actionName, ) ? styles.colorMuted From 3dc351255b70f5cce05e4a44000e6c79dd9767bc Mon Sep 17 00:00:00 2001 From: Alberto Date: Fri, 3 Nov 2023 13:04:03 +0100 Subject: [PATCH 029/105] docs --- src/components/DotIndicatorMessage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/DotIndicatorMessage.js b/src/components/DotIndicatorMessage.js index 6610db1b4a61..3297f70fafc6 100644 --- a/src/components/DotIndicatorMessage.js +++ b/src/components/DotIndicatorMessage.js @@ -45,6 +45,7 @@ const defaultProps = { * Check if the error includes a receipt. * * @param {String} message + * @returns {Boolean} */ const isReceiptError = (message) => { if (_.isString(message)) { From 93d176c82676d091313aea251db3ab48a94fac56 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Fri, 3 Nov 2023 14:26:39 +0000 Subject: [PATCH 030/105] Offer correct payment options --- src/components/AddPaymentMethodMenu.js | 18 +++++++++++------- src/components/KYCWall/BaseKYCWall.js | 6 +++++- src/pages/home/report/ReportActionItem.js | 17 +++++++++-------- .../home/report/ReportActionItemFragment.js | 2 +- .../home/report/ReportActionItemMessage.js | 1 - 5 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/components/AddPaymentMethodMenu.js b/src/components/AddPaymentMethodMenu.js index f6ed7e8254a0..85419387cc6e 100644 --- a/src/components/AddPaymentMethodMenu.js +++ b/src/components/AddPaymentMethodMenu.js @@ -77,14 +77,18 @@ function AddPaymentMethodMenu(props) { anchorRef={props.anchorRef} onItemSelected={props.onClose} menuItems={[ - { - text: props.translate('common.personalBankAccount'), - icon: Expensicons.Bank, - onSelected: () => { - props.onItemSelected(CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT); + ...(ReportUtils.isIOUReport(props.iouReport) + ?[ + { + text: props.translate('common.personalBankAccount'), + icon: Expensicons.Bank, + onSelected: () => { + props.onItemSelected(CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT); + }, }, - }, - ...(ReportUtils.isIOUReport(props.iouReport) && !ReportActionsUtils.hasRequestFromPayer(lodashGet(props.iouReport, 'reportID', 0), props.session.accountID) + ] + : []), + ...(!ReportActionsUtils.hasRequestFromPayer(lodashGet(props.iouReport, 'reportID', 0), props.session.accountID) ? [ { text: props.translate('common.businessBankAccount'), diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js index 46479d554bdf..add5d4ba042b 100644 --- a/src/components/KYCWall/BaseKYCWall.js +++ b/src/components/KYCWall/BaseKYCWall.js @@ -99,7 +99,11 @@ class KYCWall extends React.Component { } else if (paymentMethod === CONST.PAYMENT_METHODS.DEBIT_CARD) { Navigation.navigate(this.props.addDebitCardRoute); } else if (paymentMethod === CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT) { - Policy.createWorkspaceFromIOUPayment(this.props.iouReport); + if (ReportUtils.isIOUReport(this.props.iouReport)) { + Policy.createWorkspaceFromIOUPayment(this.props.iouReport); + return; + } + Navigation.navigate(this.props.addBankAccountRoute); } } diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 39d3f04b3961..ace51be25ea3 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -425,14 +425,15 @@ function ReportActionItem(props) { action={props.action} displayAsGroup={props.displayAsGroup} isHidden={isHidden} - style={[ - _.contains( - [..._.values(CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG), CONST.REPORT.ACTIONS.TYPE.IOU, CONST.REPORT.ACTIONS.TYPE.APPROVED, CONST.REPORT.ACTIONS.TYPE.MOVED], - props.action.actionName, - ) - ? styles.colorMuted - : undefined, - ]} + // style={[ + // _.contains( + // [..._.values(CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG), CONST.REPORT.ACTIONS.TYPE.IOU, CONST.REPORT.ACTIONS.TYPE.APPROVED, CONST.REPORT.ACTIONS.TYPE.MOVED], + // props.action.actionName, + // ) + // ? styles.colorMuted + // : undefined, + // ]} + style={[styles.colorMuted]} /> {hasBeenFlagged && (