From f699846f372ec2104d52c1ce9f6dbf54e8649a94 Mon Sep 17 00:00:00 2001 From: dominictb Date: Thu, 27 Jun 2024 13:08:53 +0700 Subject: [PATCH 01/15] fix: save IOU request description in html Signed-off-by: dominictb --- src/libs/actions/IOU.ts | 2 +- src/pages/iou/request/step/IOURequestStepDescription.tsx | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 44f92d73e8dc..2eeaae2ea242 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3107,7 +3107,7 @@ function updateMoneyRequestDescription( policyCategories: OnyxEntry, ) { const transactionChanges: TransactionChanges = { - comment, + comment: ReportUtils.getParsedComment(comment), }; const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; diff --git a/src/pages/iou/request/step/IOURequestStepDescription.tsx b/src/pages/iou/request/step/IOURequestStepDescription.tsx index 081f0b104355..bc9e731ab9a0 100644 --- a/src/pages/iou/request/step/IOURequestStepDescription.tsx +++ b/src/pages/iou/request/step/IOURequestStepDescription.tsx @@ -14,6 +14,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; import * as IOUUtils from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; +import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; @@ -75,7 +76,9 @@ function IOURequestStepDescription({ const focusTimeoutRef = useRef(null); // In the split flow, when editing we use SPLIT_TRANSACTION_DRAFT to save draft value const isEditingSplitBill = iouType === CONST.IOU.TYPE.SPLIT && action === CONST.IOU.ACTION.EDIT; - const currentDescription = isEditingSplitBill && !lodashIsEmpty(splitDraftTransaction) ? splitDraftTransaction?.comment.comment ?? '' : transaction?.comment.comment ?? ''; + const currentDescriptionInMarkdown = parseHtmlToMarkdown( + isEditingSplitBill && !lodashIsEmpty(splitDraftTransaction) ? splitDraftTransaction?.comment.comment ?? '' : transaction?.comment.comment ?? '', + ); useFocusEffect( useCallback(() => { focusTimeoutRef.current = setTimeout(() => { @@ -120,7 +123,7 @@ function IOURequestStepDescription({ const newComment = value.moneyRequestComment.trim(); // Only update comment if it has changed - if (newComment === currentDescription) { + if (newComment === currentDescriptionInMarkdown) { navigateBack(); return; } @@ -170,7 +173,7 @@ function IOURequestStepDescription({ InputComponent={TextInput} inputID={INPUT_IDS.MONEY_REQUEST_COMMENT} name={INPUT_IDS.MONEY_REQUEST_COMMENT} - defaultValue={currentDescription} + defaultValue={currentDescriptionInMarkdown} label={translate('moneyRequestConfirmationList.whatsItFor')} accessibilityLabel={translate('moneyRequestConfirmationList.whatsItFor')} role={CONST.ROLE.PRESENTATION} From cb71610a84e874bd110d6e08990fbd2801183e83 Mon Sep 17 00:00:00 2001 From: dominictb Date: Thu, 12 Dec 2024 23:14:05 +0700 Subject: [PATCH 02/15] use description param --- src/components/ReportActionItem/MoneyRequestView.tsx | 2 +- src/libs/ReportUtils.ts | 1 + src/libs/actions/IOU.ts | 4 +++- src/pages/iou/request/step/IOURequestStepDescription.tsx | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index e29811ce3b03..e083c60879ba 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -541,7 +541,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals ; + description?: string; cardID: number; originalAmount: number; originalCurrency: string; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 409682fcede3..814365c8e180 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3409,8 +3409,9 @@ function updateMoneyRequestDescription( policyTagList: OnyxEntry, policyCategories: OnyxEntry, ) { + const parsedComment = ReportUtils.getParsedComment(comment); const transactionChanges: TransactionChanges = { - comment: ReportUtils.getParsedComment(comment), + comment: parsedComment, }; const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; @@ -3421,6 +3422,7 @@ function updateMoneyRequestDescription( data = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories); } const {params, onyxData} = data; + params.description = parsedComment; API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DESCRIPTION, params, onyxData); } diff --git a/src/pages/iou/request/step/IOURequestStepDescription.tsx b/src/pages/iou/request/step/IOURequestStepDescription.tsx index 979784ed5ccf..d1edd46ec2e6 100644 --- a/src/pages/iou/request/step/IOURequestStepDescription.tsx +++ b/src/pages/iou/request/step/IOURequestStepDescription.tsx @@ -13,7 +13,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; import * as IOUUtils from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; -import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser'; +import Parser from '@libs/Parser'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; @@ -73,7 +73,7 @@ function IOURequestStepDescription({ const {inputCallbackRef} = useAutoFocusInput(true); // In the split flow, when editing we use SPLIT_TRANSACTION_DRAFT to save draft value const isEditingSplitBill = iouType === CONST.IOU.TYPE.SPLIT && action === CONST.IOU.ACTION.EDIT; - const currentDescriptionInMarkdown = parseHtmlToMarkdown( + const currentDescriptionInMarkdown = Parser.htmlToMarkdown( isEditingSplitBill && !lodashIsEmpty(splitDraftTransaction) ? splitDraftTransaction?.comment?.comment ?? '' : transaction?.comment?.comment ?? '', ); From e00242a85b6da2a1e0515c68b94e66a8d1a38376 Mon Sep 17 00:00:00 2001 From: dominictb Date: Wed, 15 Jan 2025 10:15:49 +0700 Subject: [PATCH 03/15] show markdown in modified expense action --- src/libs/ModifiedExpenseMessage.ts | 5 +++-- src/libs/actions/IOU.ts | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index e2f42ddab94c..df18947d3acd 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -11,6 +11,7 @@ import Log from './Log'; import * as PolicyUtils from './PolicyUtils'; import * as ReportActionsUtils from './ReportActionsUtils'; import * as TransactionUtils from './TransactionUtils'; +import Parser from '@libs/Parser'; let allPolicyTags: OnyxCollection = {}; Onyx.connect({ @@ -181,8 +182,8 @@ function getForReportAction(reportOrID: string | SearchReport | undefined, repor const hasModifiedComment = isReportActionOriginalMessageAnObject && 'oldComment' in reportActionOriginalMessage && 'newComment' in reportActionOriginalMessage; if (hasModifiedComment) { buildMessageFragmentForValue( - reportActionOriginalMessage?.newComment ?? '', - reportActionOriginalMessage?.oldComment ?? '', + Parser.htmlToMarkdown(reportActionOriginalMessage?.newComment ?? ''), + Parser.htmlToMarkdown(reportActionOriginalMessage?.oldComment ?? ''), Localize.translateLocal('common.description'), true, setFragments, diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index aa869279d6b9..94b8260db4eb 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -102,6 +102,7 @@ import { getMoneyRequestSpendBreakdown, getOptimisticDataForParentReportAction, getOutstandingChildRequest, + getParsedComment, getPersonalDetailsForAccountID, getReportNameValuePairs, getReportOrDraftReport, @@ -3664,9 +3665,9 @@ function updateMoneyRequestDescription( policyTagList: OnyxEntry, policyCategories: OnyxEntry, ) { - const parsedComment = ReportUtils.getParsedComment(comment); + const parsedComment = getParsedComment(comment); const transactionChanges: TransactionChanges = { - comment: parsedComment, + comment, }; const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; From 824e44603f2434e7694e74882fd149f6dc48e9b7 Mon Sep 17 00:00:00 2001 From: dominictb Date: Wed, 15 Jan 2025 10:36:02 +0700 Subject: [PATCH 04/15] fix lint --- .../ReportActionItem/MoneyRequestView.tsx | 158 +++++++++++------- src/libs/ModifiedExpenseMessage.ts | 2 +- 2 files changed, 94 insertions(+), 66 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index a77847bb17ce..94ad013651f7 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -18,22 +18,51 @@ import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import useViolations from '@hooks/useViolations'; import type {ViolationField} from '@hooks/useViolations'; -import * as CurrencyUtils from '@libs/CurrencyUtils'; +import {convertToDisplayString} from '@libs/CurrencyUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; -import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import {isTaxTrackingEnabled} from '@libs/PolicyUtils'; -import * as ReceiptUtils from '@libs/ReceiptUtils'; -import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import * as ReportUtils from '@libs/ReportUtils'; +import {hasEnabledOptions} from '@libs/OptionsListUtils'; +import {getTagLists, hasDependentTags, isTaxTrackingEnabled} from '@libs/PolicyUtils'; +import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; +import {getOriginalMessage, isMoneyRequestAction, isPayAction} from '@libs/ReportActionsUtils'; +import { + canEditFieldOfMoneyRequest, + canEditMoneyRequest, + canUserPerformWriteAction as canUserPerformWriteActionReportUtils, + getAddWorkspaceRoomOrChatReportErrors, + getTransactionDetails, + getTripIDFromTransactionParentReportID, + isInvoiceReport, + isMoneyRequestReport, + isPaidGroupPolicy, + isReportApproved, + isReportInGroupPolicy, + isSettled as isSettledReportUtils, + isTrackExpenseReport, +} from '@libs/ReportUtils'; import type {TransactionDetails} from '@libs/ReportUtils'; -import * as TagsOptionsListUtils from '@libs/TagsOptionsListUtils'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import {hasEnabledTags} from '@libs/TagsOptionsListUtils'; +import { + didReceiptScanSucceed as didReceiptScanSucceedTransactionUtils, + getBillable, + getCardName, + getDescription, + getDistanceInMeters, + getTagForDisplay, + getTaxName, + hasMissingSmartscanFields, + hasReceipt as hasReceiptTransactionUtils, + hasReservationList, + hasRoute as hasRouteTransactionUtils, + isCardTransaction as isCardTransactionUtils, + isDistanceRequest as isDistanceRequestTransactionUtils, + isReceiptBeingScanned as isReceiptBeingScannedTransactionUtils, + shouldShowAttendees as shouldShowAttendeesTransactionUtils, +} from '@libs/TransactionUtils'; import ViolationsUtils from '@libs/Violations/ViolationsUtils'; import Navigation from '@navigation/Navigation'; import AnimatedEmptyStateBackground from '@pages/home/report/AnimatedEmptyStateBackground'; -import * as IOU from '@userActions/IOU'; -import * as Transaction from '@userActions/Transaction'; +import {cleanUpMoneyRequest, updateMoneyRequestBillable} from '@userActions/IOU'; +import {clearError} from '@userActions/Transaction'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import * as Report from '@src/libs/actions/Report'; @@ -72,7 +101,7 @@ const receiptFieldViolationNames: OnyxTypes.ViolationName[] = [CONST.VIOLATIONS. const getTransactionID = (report: OnyxEntry, parentReportActions: OnyxEntry) => { const parentReportAction = parentReportActions?.[report?.parentReportActionID ?? '-1']; - const originalMessage = parentReportAction && ReportActionsUtils.isMoneyRequestAction(parentReportAction) ? ReportActionsUtils.getOriginalMessage(parentReportAction) : undefined; + const originalMessage = parentReportAction && isMoneyRequestAction(parentReportAction) ? getOriginalMessage(parentReportAction) : undefined; return originalMessage?.IOUTransactionID ?? -1; }; @@ -98,10 +127,10 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals const [transactionViolations] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${getTransactionID(report, parentReportActions)}`); const parentReportAction = parentReportActions?.[report?.parentReportActionID ?? '-1']; - const isTrackExpense = ReportUtils.isTrackExpenseReport(report); + const isTrackExpense = isTrackExpenseReport(report); const moneyRequestReport = parentReport; const linkedTransactionID = useMemo(() => { - const originalMessage = parentReportAction && ReportActionsUtils.isMoneyRequestAction(parentReportAction) ? ReportActionsUtils.getOriginalMessage(parentReportAction) : undefined; + const originalMessage = parentReportAction && isMoneyRequestAction(parentReportAction) ? getOriginalMessage(parentReportAction) : undefined; return originalMessage?.IOUTransactionID ?? '-1'; }, [parentReportAction]); @@ -122,74 +151,74 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals originalAmount: transactionOriginalAmount, originalCurrency: transactionOriginalCurrency, postedDate: transactionPostedDate, - } = useMemo>(() => ReportUtils.getTransactionDetails(transaction) ?? {}, [transaction]); + } = useMemo>(() => getTransactionDetails(transaction) ?? {}, [transaction]); const isEmptyMerchant = transactionMerchant === '' || transactionMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; - const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction); - const formattedTransactionAmount = transactionAmount ? CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency) : ''; - const formattedPerAttendeeAmount = transactionAmount ? CurrencyUtils.convertToDisplayString(transactionAmount / (transactionAttendees?.length ?? 1), transactionCurrency) : ''; - const formattedOriginalAmount = transactionOriginalAmount && transactionOriginalCurrency && CurrencyUtils.convertToDisplayString(transactionOriginalAmount, transactionOriginalCurrency); - const isCardTransaction = TransactionUtils.isCardTransaction(transaction); - const cardProgramName = TransactionUtils.getCardName(transaction); + const isDistanceRequest = isDistanceRequestTransactionUtils(transaction); + const formattedTransactionAmount = transactionAmount ? convertToDisplayString(transactionAmount, transactionCurrency) : ''; + const formattedPerAttendeeAmount = transactionAmount ? convertToDisplayString(transactionAmount / (transactionAttendees?.length ?? 1), transactionCurrency) : ''; + const formattedOriginalAmount = transactionOriginalAmount && transactionOriginalCurrency && convertToDisplayString(transactionOriginalAmount, transactionOriginalCurrency); + const isCardTransaction = isCardTransactionUtils(transaction); + const cardProgramName = getCardName(transaction); const shouldShowCard = isCardTransaction && cardProgramName; - const isApproved = ReportUtils.isReportApproved(moneyRequestReport); - const isInvoice = ReportUtils.isInvoiceReport(moneyRequestReport); - const isPaidReport = ReportActionsUtils.isPayAction(parentReportAction); + const isApproved = isReportApproved(moneyRequestReport); + const isInvoice = isInvoiceReport(moneyRequestReport); + const isPaidReport = isPayAction(parentReportAction); const taxRates = policy?.taxRates; const formattedTaxAmount = updatedTransaction?.taxAmount - ? CurrencyUtils.convertToDisplayString(Math.abs(updatedTransaction?.taxAmount), transactionCurrency) - : CurrencyUtils.convertToDisplayString(Math.abs(transactionTaxAmount ?? 0), transactionCurrency); + ? convertToDisplayString(Math.abs(updatedTransaction?.taxAmount), transactionCurrency) + : convertToDisplayString(Math.abs(transactionTaxAmount ?? 0), transactionCurrency); const taxRatesDescription = taxRates?.name; - const taxRateTitle = updatedTransaction ? TransactionUtils.getTaxName(policy, updatedTransaction) : TransactionUtils.getTaxName(policy, transaction); + const taxRateTitle = updatedTransaction ? getTaxName(policy, updatedTransaction) : getTaxName(policy, transaction); - const isSettled = ReportUtils.isSettled(moneyRequestReport?.reportID); + const isSettled = isSettledReportUtils(moneyRequestReport?.reportID); const isCancelled = moneyRequestReport && moneyRequestReport?.isCancelledIOU; // Flags for allowing or disallowing editing an expense // Used for non-restricted fields such as: description, category, tag, billable, etc... - const canUserPerformWriteAction = !!ReportUtils.canUserPerformWriteAction(report) && !readonly; - const canEdit = ReportActionsUtils.isMoneyRequestAction(parentReportAction) && ReportUtils.canEditMoneyRequest(parentReportAction, transaction) && canUserPerformWriteAction; + const canUserPerformWriteAction = !!canUserPerformWriteActionReportUtils(report) && !readonly; + const canEdit = isMoneyRequestAction(parentReportAction) && canEditMoneyRequest(parentReportAction, transaction) && canUserPerformWriteAction; const canEditTaxFields = canEdit && !isDistanceRequest; - const canEditAmount = canUserPerformWriteAction && ReportUtils.canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.AMOUNT); - const canEditMerchant = canUserPerformWriteAction && ReportUtils.canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.MERCHANT); - const canEditDate = canUserPerformWriteAction && ReportUtils.canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DATE); - const canEditReceipt = canUserPerformWriteAction && ReportUtils.canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.RECEIPT); - const hasReceipt = TransactionUtils.hasReceipt(updatedTransaction ?? transaction); - const isReceiptBeingScanned = hasReceipt && TransactionUtils.isReceiptBeingScanned(updatedTransaction ?? transaction); - const didReceiptScanSucceed = hasReceipt && TransactionUtils.didReceiptScanSucceed(transaction); - const canEditDistance = canUserPerformWriteAction && ReportUtils.canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DISTANCE); - const canEditDistanceRate = canUserPerformWriteAction && ReportUtils.canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DISTANCE_RATE); + const canEditAmount = canUserPerformWriteAction && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.AMOUNT); + const canEditMerchant = canUserPerformWriteAction && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.MERCHANT); + const canEditDate = canUserPerformWriteAction && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DATE); + const canEditReceipt = canUserPerformWriteAction && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.RECEIPT); + const hasReceipt = hasReceiptTransactionUtils(updatedTransaction ?? transaction); + const isReceiptBeingScanned = hasReceipt && isReceiptBeingScannedTransactionUtils(updatedTransaction ?? transaction); + const didReceiptScanSucceed = hasReceipt && didReceiptScanSucceedTransactionUtils(transaction); + const canEditDistance = canUserPerformWriteAction && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DISTANCE); + const canEditDistanceRate = canUserPerformWriteAction && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DISTANCE_RATE); const isAdmin = policy?.role === 'admin'; - const isApprover = ReportUtils.isMoneyRequestReport(moneyRequestReport) && moneyRequestReport?.managerID !== null && session?.accountID === moneyRequestReport?.managerID; + const isApprover = isMoneyRequestReport(moneyRequestReport) && moneyRequestReport?.managerID !== null && session?.accountID === moneyRequestReport?.managerID; const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const isRequestor = currentUserPersonalDetails.accountID === parentReportAction?.actorAccountID; // A flag for verifying that the current report is a sub-report of a workspace chat // if the policy of the report is either Collect or Control, then this report must be tied to workspace chat - const isPolicyExpenseChat = ReportUtils.isReportInGroupPolicy(report); + const isPolicyExpenseChat = isReportInGroupPolicy(report); - const policyTagLists = useMemo(() => PolicyUtils.getTagLists(policyTagList), [policyTagList]); + const policyTagLists = useMemo(() => getTagLists(policyTagList), [policyTagList]); const iouType = isTrackExpense ? CONST.IOU.TYPE.TRACK : CONST.IOU.TYPE.SUBMIT; // Flags for showing categories and tags // transactionCategory can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const shouldShowCategory = isPolicyExpenseChat && (transactionCategory || OptionsListUtils.hasEnabledOptions(policyCategories ?? {})); + const shouldShowCategory = isPolicyExpenseChat && (transactionCategory || hasEnabledOptions(policyCategories ?? {})); // transactionTag can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const shouldShowTag = isPolicyExpenseChat && (transactionTag || TagsOptionsListUtils.hasEnabledTags(policyTagLists)); + const shouldShowTag = isPolicyExpenseChat && (transactionTag || hasEnabledTags(policyTagLists)); const shouldShowBillable = isPolicyExpenseChat && (!!transactionBillable || !(policy?.disabledFields?.defaultBillable ?? true) || !!updatedTransaction?.billable); - const shouldShowAttendees = useMemo(() => TransactionUtils.shouldShowAttendees(iouType, policy), [iouType, policy]); + const shouldShowAttendees = useMemo(() => shouldShowAttendeesTransactionUtils(iouType, policy), [iouType, policy]); const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest); - const tripID = ReportUtils.getTripIDFromTransactionParentReportID(parentReport?.parentReportID); - const shouldShowViewTripDetails = TransactionUtils.hasReservationList(transaction) && !!tripID; + const tripID = getTripIDFromTransactionParentReportID(parentReport?.parentReportID); + const shouldShowViewTripDetails = hasReservationList(transaction) && !!tripID; - const {getViolationsForField} = useViolations(transactionViolations ?? [], isReceiptBeingScanned || !ReportUtils.isPaidGroupPolicy(report)); + const {getViolationsForField} = useViolations(transactionViolations ?? [], isReceiptBeingScanned || !isPaidGroupPolicy(report)); const hasViolations = useCallback( (field: ViolationField, data?: OnyxTypes.TransactionViolation['data'], policyHasDependentTags = false, tagValue?: string): boolean => getViolationsForField(field, data, policyHasDependentTags, tagValue).length > 0, @@ -199,15 +228,15 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals let amountDescription = `${translate('iou.amount')}`; let dateDescription = `${translate('common.date')}`; - const hasRoute = TransactionUtils.hasRoute(transactionBackup ?? transaction, isDistanceRequest); + const hasRoute = hasRouteTransactionUtils(transactionBackup ?? transaction, isDistanceRequest); const {unit, rate} = DistanceRequestUtils.getRate({transaction, policy}); - const distance = TransactionUtils.getDistanceInMeters(transactionBackup ?? transaction, unit); + const distance = getDistanceInMeters(transactionBackup ?? transaction, unit); const currency = transactionCurrency ?? CONST.CURRENCY.USD; const rateToDisplay = DistanceRequestUtils.getRateForDisplay(unit, rate, currency, translate, toLocaleDigit, isOffline); const distanceToDisplay = DistanceRequestUtils.getDistanceForDisplay(hasRoute, distance, unit, rate, translate); let merchantTitle = isEmptyMerchant ? '' : transactionMerchant; let amountTitle = formattedTransactionAmount ? formattedTransactionAmount.toString() : ''; - if (TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction)) { + if (hasReceiptTransactionUtils(transaction) && isReceiptBeingScannedTransactionUtils(transaction)) { merchantTitle = translate('iou.receiptStatusTitle'); amountTitle = translate('iou.receiptStatusTitle'); } @@ -216,7 +245,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals if (!updatedTransaction) { return undefined; } - return TransactionUtils.getDescription(updatedTransaction ?? null); + return getDescription(updatedTransaction ?? null); }, [updatedTransaction]); const isEmptyUpdatedMerchant = updatedTransaction?.modifiedMerchant === '' || updatedTransaction?.modifiedMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; const updatedMerchantTitle = isEmptyUpdatedMerchant ? '' : updatedTransaction?.modifiedMerchant ?? merchantTitle; @@ -224,10 +253,10 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals const saveBillable = useCallback( (newBillable: boolean) => { // If the value hasn't changed, don't request to save changes on the server and just close the modal - if (newBillable === TransactionUtils.getBillable(transaction)) { + if (newBillable === getBillable(transaction)) { return; } - IOU.updateMoneyRequestBillable(transaction?.transactionID ?? '-1', report?.reportID ?? '-1', newBillable, policy, policyTagList, policyCategories); + updateMoneyRequestBillable(transaction?.transactionID ?? '-1', report?.reportID ?? '-1', newBillable, policy, policyTagList, policyCategories); }, [transaction, report, policy, policyTagList, policyCategories], ); @@ -256,9 +285,9 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals } let receiptURIs; - const hasErrors = TransactionUtils.hasMissingSmartscanFields(transaction); + const hasErrors = hasMissingSmartscanFields(transaction); if (hasReceipt) { - receiptURIs = ReceiptUtils.getThumbnailAndImageURIs(updatedTransaction ?? transaction); + receiptURIs = getThumbnailAndImageURIs(updatedTransaction ?? transaction); } const pendingAction = transaction?.pendingAction; // Need to return undefined when we have pendingAction to avoid the duplicate pending action @@ -358,7 +387,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals const isReceiptAllowed = !isPaidReport && !isInvoice; const shouldShowReceiptEmptyState = - isReceiptAllowed && !hasReceipt && !isApproved && !isSettled && (canEditReceipt || isAdmin || isApprover || isRequestor) && (canEditReceipt || ReportUtils.isPaidGroupPolicy(report)); + isReceiptAllowed && !hasReceipt && !isApproved && !isSettled && (canEditReceipt || isAdmin || isApprover || isRequestor) && (canEditReceipt || isPaidGroupPolicy(report)); const [receiptImageViolations, receiptViolations] = useMemo(() => { const imageViolations = []; @@ -382,8 +411,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals // Whether to show receipt audit result (e.g.`Verified`, `Issue Found`) and messages (e.g. `Receipt not verified. Please confirm accuracy.`) // `!!(receiptViolations.length || didReceiptScanSucceed)` is for not showing `Verified` when `receiptViolations` is empty and `didReceiptScanSucceed` is false. - const shouldShowAuditMessage = - !isReceiptBeingScanned && (hasReceipt || receiptRequiredViolation) && !!(receiptViolations.length || didReceiptScanSucceed) && ReportUtils.isPaidGroupPolicy(report); + const shouldShowAuditMessage = !isReceiptBeingScanned && (hasReceipt || receiptRequiredViolation) && !!(receiptViolations.length || didReceiptScanSucceed) && isPaidGroupPolicy(report); const shouldShowReceiptAudit = isReceiptAllowed && (shouldShowReceiptEmptyState || hasReceipt); const errors = { @@ -392,8 +420,8 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals }; const tagList = policyTagLists.map(({name, orderWeight, tags}, index) => { - const tagForDisplay = TransactionUtils.getTagForDisplay(updatedTransaction ?? transaction, index); - const shouldShow = !!tagForDisplay || OptionsListUtils.hasEnabledOptions(tags); + const tagForDisplay = getTagForDisplay(updatedTransaction ?? transaction, index); + const shouldShow = !!tagForDisplay || hasEnabledOptions(tags); if (!shouldShow) { return null; } @@ -404,7 +432,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals tagListIndex: index, tagListName: name, }, - PolicyUtils.hasDependentTags(policy, policyTagList), + hasDependentTags(policy, policyTagList), tagForDisplay, ); return ( @@ -460,16 +488,16 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals } if (transaction?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { - if (chatReport?.reportID && ReportUtils.getAddWorkspaceRoomOrChatReportErrors(chatReport)) { + if (chatReport?.reportID && getAddWorkspaceRoomOrChatReportErrors(chatReport)) { Report.navigateToConciergeChatAndDeleteReport(chatReport.reportID, true, true); return; } if (parentReportAction) { - IOU.cleanUpMoneyRequest(transaction?.transactionID ?? linkedTransactionID, parentReportAction, true); + cleanUpMoneyRequest(transaction?.transactionID ?? linkedTransactionID, parentReportAction, true); return; } } - Transaction.clearError(transaction?.transactionID ?? linkedTransactionID); + clearError(transaction?.transactionID ?? linkedTransactionID); ReportActions.clearAllRelatedReportActionErrors(report?.reportID ?? '-1', parentReportAction); }} > diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index df18947d3acd..14df6b0a0bac 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -1,5 +1,6 @@ import Onyx from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import Parser from '@libs/Parser'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PolicyTagLists, Report, ReportAction} from '@src/types/onyx'; @@ -11,7 +12,6 @@ import Log from './Log'; import * as PolicyUtils from './PolicyUtils'; import * as ReportActionsUtils from './ReportActionsUtils'; import * as TransactionUtils from './TransactionUtils'; -import Parser from '@libs/Parser'; let allPolicyTags: OnyxCollection = {}; Onyx.connect({ From f7f6d8bc51215b0f52471fba9c4ae7683089a4b2 Mon Sep 17 00:00:00 2001 From: dominictb Date: Wed, 15 Jan 2025 16:49:05 +0700 Subject: [PATCH 05/15] fix lint --- src/ROUTES.ts | 111 +++++++++++++----- .../ReportActionItem/MoneyRequestView.tsx | 87 +++++++------- src/libs/ModifiedExpenseMessage.ts | 96 +++++++-------- src/libs/actions/IOU.ts | 7 +- src/libs/actions/ReportActions.ts | 4 +- src/libs/actions/Transaction.ts | 5 +- .../step/IOURequestStepDescription.tsx | 26 ++-- 7 files changed, 192 insertions(+), 144 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index fa40bfad4e63..2f9ef28a515a 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -457,28 +457,48 @@ const ROUTES = { }, MONEY_REQUEST_STEP_AMOUNT: { route: ':action/:iouType/amount/:transactionID/:reportID/:pageIndex?', - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, pageIndex: string, backTo = '') => - getUrlWithBackToParam(`${action as string}/${iouType as string}/amount/${transactionID}/${reportID}/${pageIndex}`, backTo), + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, pageIndex: string, backTo = '') => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_AMOUNT route'); + } + return getUrlWithBackToParam(`${action as string}/${iouType as string}/amount/${transactionID}/${reportID}/${pageIndex}`, backTo); + }, }, MONEY_REQUEST_STEP_TAX_RATE: { route: ':action/:iouType/taxRate/:transactionID/:reportID?', - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`${action as string}/${iouType as string}/taxRate/${transactionID}/${reportID}`, backTo), + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, backTo = '') => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_TAX_RATE route'); + } + return getUrlWithBackToParam(`${action as string}/${iouType as string}/taxRate/${transactionID}/${reportID}`, backTo); + }, }, MONEY_REQUEST_STEP_TAX_AMOUNT: { route: ':action/:iouType/taxAmount/:transactionID/:reportID?', - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`${action as string}/${iouType as string}/taxAmount/${transactionID}/${reportID}`, backTo), + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, backTo = '') => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_TAX_AMOUNT route'); + } + return getUrlWithBackToParam(`${action as string}/${iouType as string}/taxAmount/${transactionID}/${reportID}`, backTo); + }, }, MONEY_REQUEST_STEP_CATEGORY: { route: ':action/:iouType/category/:transactionID/:reportID/:reportActionID?', - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '', reportActionID?: string) => - getUrlWithBackToParam(`${action as string}/${iouType as string}/category/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo), + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, backTo = '', reportActionID?: string) => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_CATEGORY route'); + } + return getUrlWithBackToParam(`${action as string}/${iouType as string}/category/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo); + }, }, MONEY_REQUEST_ATTENDEE: { route: ':action/:iouType/attendees/:transactionID/:reportID', - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`${action as string}/${iouType as string}/attendees/${transactionID}/${reportID}`, backTo), + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, backTo = '') => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_ATTENDEE route'); + } + return getUrlWithBackToParam(`${action as string}/${iouType as string}/attendees/${transactionID}/${reportID}`, backTo); + }, }, MONEY_REQUEST_UPGRADE: { route: ':action/:iouType/upgrade/:transactionID/:reportID', @@ -608,28 +628,48 @@ const ROUTES = { }, MONEY_REQUEST_STEP_DATE: { route: ':action/:iouType/date/:transactionID/:reportID/:reportActionID?', - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '', reportActionID?: string) => - getUrlWithBackToParam(`${action as string}/${iouType as string}/date/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo), + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, backTo = '', reportActionID?: string) => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_DATE route'); + } + return getUrlWithBackToParam(`${action as string}/${iouType as string}/date/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo); + }, }, MONEY_REQUEST_STEP_DESCRIPTION: { route: ':action/:iouType/description/:transactionID/:reportID/:reportActionID?', - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '', reportActionID?: string) => - getUrlWithBackToParam(`${action as string}/${iouType as string}/description/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo), + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, backTo = '', reportActionID?: string) => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_DESCRIPTION route'); + } + return getUrlWithBackToParam(`${action as string}/${iouType as string}/description/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo); + }, }, MONEY_REQUEST_STEP_DISTANCE: { route: ':action/:iouType/distance/:transactionID/:reportID', - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`${action as string}/${iouType as string}/distance/${transactionID}/${reportID}`, backTo), + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, backTo = '') => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_DISTANCE route'); + } + return getUrlWithBackToParam(`${action as string}/${iouType as string}/distance/${transactionID}/${reportID}`, backTo); + }, }, MONEY_REQUEST_STEP_DISTANCE_RATE: { route: ':action/:iouType/distanceRate/:transactionID/:reportID', - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`${action as string}/${iouType as string}/distanceRate/${transactionID}/${reportID}`, backTo), + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, backTo = '') => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_DISTANCE_RATE route'); + } + return getUrlWithBackToParam(`${action as string}/${iouType as string}/distanceRate/${transactionID}/${reportID}`, backTo); + }, }, MONEY_REQUEST_STEP_MERCHANT: { route: ':action/:iouType/merchant/:transactionID/:reportID', - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`${action as string}/${iouType as string}/merchant/${transactionID}/${reportID}`, backTo), + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, backTo = '') => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_MERCHANT route'); + } + return getUrlWithBackToParam(`${action as string}/${iouType as string}/merchant/${transactionID}/${reportID}`, backTo); + }, }, MONEY_REQUEST_STEP_PARTICIPANTS: { route: ':action/:iouType/participants/:transactionID/:reportID', @@ -643,13 +683,21 @@ const ROUTES = { }, MONEY_REQUEST_STEP_SCAN: { route: ':action/:iouType/scan/:transactionID/:reportID', - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`${action as string}/${iouType as string}/scan/${transactionID}/${reportID}`, backTo), + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, backTo = '') => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_SCAN route'); + } + return getUrlWithBackToParam(`${action as string}/${iouType as string}/scan/${transactionID}/${reportID}`, backTo); + }, }, MONEY_REQUEST_STEP_TAG: { route: ':action/:iouType/tag/:orderWeight/:transactionID/:reportID/:reportActionID?', - getRoute: (action: IOUAction, iouType: IOUType, orderWeight: number, transactionID: string, reportID: string, backTo = '', reportActionID?: string) => - getUrlWithBackToParam(`${action as string}/${iouType as string}/tag/${orderWeight}/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo), + getRoute: (action: IOUAction, iouType: IOUType, orderWeight: number, transactionID: string | undefined, reportID: string | undefined, backTo = '', reportActionID?: string) => { + if (!transactionID || !reportID) { + Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_TAG route'); + } + return getUrlWithBackToParam(`${action as string}/${iouType as string}/tag/${orderWeight}/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo); + }, }, MONEY_REQUEST_STEP_WAYPOINT: { route: ':action/:iouType/waypoint/:transactionID/:reportID/:pageIndex', @@ -1432,12 +1480,21 @@ const ROUTES = { TRACK_TRAINING_MODAL: 'track-training', TRAVEL_TRIP_SUMMARY: { route: 'r/:reportID/trip/:transactionID', - getRoute: (reportID: string, transactionID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/trip/${transactionID}`, backTo), + getRoute: (reportID: string | undefined, transactionID: string | undefined, backTo?: string) => { + if (!reportID || !transactionID) { + Log.warn('Invalid reportID or transactionID is used to build the TRAVEL_TRIP_SUMMARY route'); + } + return getUrlWithBackToParam(`r/${reportID}/trip/${transactionID}`, backTo); + }, }, TRAVEL_TRIP_DETAILS: { route: 'r/:reportID/trip/:transactionID/:reservationIndex', - getRoute: (reportID: string, transactionID: string, reservationIndex: number, backTo?: string) => - getUrlWithBackToParam(`r/${reportID}/trip/${transactionID}/${reservationIndex}`, backTo), + getRoute: (reportID: string | undefined, transactionID: string | undefined, reservationIndex: number, backTo?: string) => { + if (!reportID || !transactionID) { + Log.warn('Invalid reportID or transactionID is used to build the TRAVEL_TRIP_DETAILS route'); + } + return getUrlWithBackToParam(`r/${reportID}/trip/${transactionID}/${reservationIndex}`, backTo); + }, }, ONBOARDING_ROOT: { route: 'onboarding', diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 94ad013651f7..fce512ba9572 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -100,9 +100,12 @@ const receiptImageViolationNames: OnyxTypes.ViolationName[] = [ const receiptFieldViolationNames: OnyxTypes.ViolationName[] = [CONST.VIOLATIONS.MODIFIED_AMOUNT, CONST.VIOLATIONS.MODIFIED_DATE]; const getTransactionID = (report: OnyxEntry, parentReportActions: OnyxEntry) => { - const parentReportAction = parentReportActions?.[report?.parentReportActionID ?? '-1']; + if (!report?.parentReportActionID) { + return CONST.DEFAULT_NUMBER_ID; + } + const parentReportAction = parentReportActions?.[report?.parentReportActionID]; const originalMessage = parentReportAction && isMoneyRequestAction(parentReportAction) ? getOriginalMessage(parentReportAction) : undefined; - return originalMessage?.IOUTransactionID ?? -1; + return originalMessage?.IOUTransactionID ?? CONST.DEFAULT_NUMBER_ID; }; function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = false, updatedTransaction, isFromReviewDuplicates = false}: MoneyRequestViewProps) { @@ -110,8 +113,10 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals const session = useSession(); const {isOffline} = useNetwork(); const {translate, toLocaleDigit} = useLocalize(); - const parentReportID = report?.parentReportID ?? '-1'; - const policyID = report?.policyID ?? '-1'; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const parentReportID = report?.parentReportID || CONST.DEFAULT_NUMBER_ID; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const policyID = report?.policyID || CONST.DEFAULT_NUMBER_ID; const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${parentReport?.parentReportID}`, { selector: (chatReportValue) => chatReportValue && {reportID: chatReportValue.reportID, errorFields: chatReportValue.errorFields}, @@ -126,16 +131,18 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals }); const [transactionViolations] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${getTransactionID(report, parentReportActions)}`); - const parentReportAction = parentReportActions?.[report?.parentReportActionID ?? '-1']; + const parentReportAction = report?.parentReportActionID ? parentReportActions?.[report?.parentReportActionID] : undefined; const isTrackExpense = isTrackExpenseReport(report); const moneyRequestReport = parentReport; const linkedTransactionID = useMemo(() => { const originalMessage = parentReportAction && isMoneyRequestAction(parentReportAction) ? getOriginalMessage(parentReportAction) : undefined; - return originalMessage?.IOUTransactionID ?? '-1'; + return originalMessage?.IOUTransactionID; }, [parentReportAction]); - const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${linkedTransactionID}`); - const [transactionBackup] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_BACKUP}${linkedTransactionID}`); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${linkedTransactionID || CONST.DEFAULT_NUMBER_ID}`); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const [transactionBackup] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_BACKUP}${linkedTransactionID || CONST.DEFAULT_NUMBER_ID}`); const { created: transactionDate, @@ -256,7 +263,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals if (newBillable === getBillable(transaction)) { return; } - updateMoneyRequestBillable(transaction?.transactionID ?? '-1', report?.reportID ?? '-1', newBillable, policy, policyTagList, policyCategories); + updateMoneyRequestBillable(transaction?.transactionID, report?.reportID, newBillable, policy, policyTagList, policyCategories); }, [transaction, report, policy, policyTagList, policyCategories], ); @@ -349,13 +356,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals titleStyle={styles.flex1} onPress={() => Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute( - CONST.IOU.ACTION.EDIT, - iouType, - transaction?.transactionID ?? '-1', - report?.reportID ?? '-1', - Navigation.getReportRHPActiveRoute(), - ), + ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(CONST.IOU.ACTION.EDIT, iouType, transaction?.transactionID, report?.reportID, Navigation.getReportRHPActiveRoute()), ) } /> @@ -372,8 +373,8 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals ROUTES.MONEY_REQUEST_STEP_DISTANCE_RATE.getRoute( CONST.IOU.ACTION.EDIT, iouType, - transaction?.transactionID ?? '-1', - report?.reportID ?? '-1', + transaction?.transactionID, + report?.reportID, Navigation.getReportRHPActiveRoute(), ), ) @@ -452,8 +453,8 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals CONST.IOU.ACTION.EDIT, iouType, orderWeight, - transaction?.transactionID ?? '', - report?.reportID ?? '-1', + transaction?.transactionID, + report?.reportID, Navigation.getReportRHPActiveRoute(), ), ) @@ -498,7 +499,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals } } clearError(transaction?.transactionID ?? linkedTransactionID); - ReportActions.clearAllRelatedReportActionErrors(report?.reportID ?? '-1', parentReportAction); + ReportActions.clearAllRelatedReportActionErrors(report?.reportID, parentReportAction); }} > {hasReceipt && ( @@ -529,8 +530,8 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals ROUTES.MONEY_REQUEST_STEP_SCAN.getRoute( CONST.IOU.ACTION.EDIT, iouType, - transaction?.transactionID ?? '-1', - report?.reportID ?? '-1', + transaction?.transactionID, + report?.reportID, Navigation.getReportRHPActiveRoute(), ), ) @@ -554,8 +555,8 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals ROUTES.MONEY_REQUEST_STEP_AMOUNT.getRoute( CONST.IOU.ACTION.EDIT, iouType, - transaction?.transactionID ?? '-1', - report?.reportID ?? '-1', + transaction?.transactionID, + report?.reportID, '', Navigation.getReportRHPActiveRoute(), ), @@ -578,8 +579,8 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute( CONST.IOU.ACTION.EDIT, iouType, - transaction?.transactionID ?? '-1', - report?.reportID ?? '-1', + transaction?.transactionID, + report?.reportID, Navigation.getReportRHPActiveRoute(), ), ) @@ -605,8 +606,8 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals ROUTES.MONEY_REQUEST_STEP_MERCHANT.getRoute( CONST.IOU.ACTION.EDIT, iouType, - transaction?.transactionID ?? '-1', - report?.reportID ?? '-1', + transaction?.transactionID, + report?.reportID, Navigation.getReportRHPActiveRoute(), ), ) @@ -627,13 +628,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals titleStyle={styles.flex1} onPress={() => Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_DATE.getRoute( - CONST.IOU.ACTION.EDIT, - iouType, - transaction?.transactionID ?? '-1', - report?.reportID ?? '-1' ?? '-1', - Navigation.getReportRHPActiveRoute(), - ), + ROUTES.MONEY_REQUEST_STEP_DATE.getRoute(CONST.IOU.ACTION.EDIT, iouType, transaction?.transactionID, report?.reportID, Navigation.getReportRHPActiveRoute()), ) } brickRoadIndicator={getErrorForField('date') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} @@ -653,8 +648,8 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute( CONST.IOU.ACTION.EDIT, iouType, - transaction?.transactionID ?? '-1', - report?.reportID ?? '-1', + transaction?.transactionID, + report?.reportID, Navigation.getReportRHPActiveRoute(), ), ) @@ -688,8 +683,8 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute( CONST.IOU.ACTION.EDIT, iouType, - transaction?.transactionID ?? '-1', - report?.reportID ?? '-1', + transaction?.transactionID, + report?.reportID, Navigation.getReportRHPActiveRoute(), ), ) @@ -712,8 +707,8 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute( CONST.IOU.ACTION.EDIT, iouType, - transaction?.transactionID ?? '-1', - report?.reportID ?? '-1', + transaction?.transactionID, + report?.reportID, Navigation.getReportRHPActiveRoute(), ), ) @@ -728,9 +723,9 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals onPress={() => { const reservations = transaction?.receipt?.reservationList?.length ?? 0; if (reservations > 1) { - Navigation.navigate(ROUTES.TRAVEL_TRIP_SUMMARY.getRoute(report?.reportID ?? '-1', transaction?.transactionID ?? '-1', Navigation.getReportRHPActiveRoute())); + Navigation.navigate(ROUTES.TRAVEL_TRIP_SUMMARY.getRoute(report?.reportID, transaction?.transactionID, Navigation.getReportRHPActiveRoute())); } - Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(report?.reportID ?? '-1', transaction?.transactionID ?? '-1', 0, Navigation.getReportRHPActiveRoute())); + Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(report?.reportID, transaction?.transactionID, 0, Navigation.getReportRHPActiveRoute())); }} /> )} @@ -745,9 +740,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals }`} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} - onPress={() => - Navigation.navigate(ROUTES.MONEY_REQUEST_ATTENDEE.getRoute(CONST.IOU.ACTION.EDIT, iouType, transaction?.transactionID ?? '-1', report?.reportID ?? '-1')) - } + onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_ATTENDEE.getRoute(CONST.IOU.ACTION.EDIT, iouType, transaction?.transactionID, report?.reportID))} interactive shouldRenderAsHTML /> diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index 14df6b0a0bac..5c431b48250e 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -1,17 +1,17 @@ import Onyx from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import Parser from '@libs/Parser'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PolicyTagLists, Report, ReportAction} from '@src/types/onyx'; import type {SearchReport} from '@src/types/onyx/SearchResults'; -import * as CurrencyUtils from './CurrencyUtils'; +import {convertToDisplayString} from './CurrencyUtils'; import DateUtils from './DateUtils'; -import * as Localize from './Localize'; +import {translateLocal} from './Localize'; import Log from './Log'; -import * as PolicyUtils from './PolicyUtils'; -import * as ReportActionsUtils from './ReportActionsUtils'; -import * as TransactionUtils from './TransactionUtils'; +import Parser from './Parser'; +import {getCleanedTagName, getSortedTagKeys} from './PolicyUtils'; +import {getOriginalMessage, isModifiedExpenseAction} from './ReportActionsUtils'; +import {getTagArrayFromName} from './TransactionUtils'; let allPolicyTags: OnyxCollection = {}; Onyx.connect({ @@ -56,17 +56,17 @@ function buildMessageFragmentForValue( const newValueToDisplay = valueInQuotes ? `"${newValue}"` : newValue; const oldValueToDisplay = valueInQuotes ? `"${oldValue}"` : oldValue; const displayValueName = shouldConvertToLowercase ? valueName.toLowerCase() : valueName; - const isOldValuePartialMerchant = valueName === Localize.translateLocal('common.merchant') && oldValue === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; + const isOldValuePartialMerchant = valueName === translateLocal('common.merchant') && oldValue === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; // In case of a partial merchant value, we want to avoid user seeing the "(none)" value in the message. if (!oldValue || isOldValuePartialMerchant) { - const fragment = Localize.translateLocal('iou.setTheRequest', {valueName: displayValueName, newValueToDisplay}); + const fragment = translateLocal('iou.setTheRequest', {valueName: displayValueName, newValueToDisplay}); setFragments.push(fragment); } else if (!newValue) { - const fragment = Localize.translateLocal('iou.removedTheRequest', {valueName: displayValueName, oldValueToDisplay}); + const fragment = translateLocal('iou.removedTheRequest', {valueName: displayValueName, oldValueToDisplay}); removalFragments.push(fragment); } else { - const fragment = Localize.translateLocal('iou.updatedTheRequest', {valueName: displayValueName, newValueToDisplay, oldValueToDisplay}); + const fragment = translateLocal('iou.updatedTheRequest', {valueName: displayValueName, newValueToDisplay, oldValueToDisplay}); changeFragments.push(fragment); } } @@ -92,9 +92,9 @@ function getMessageLine(prefix: string, messageFragments: string[]): string { return `${acc} ${value}`; } if (messageFragments.length === 2) { - return `${acc} ${Localize.translateLocal('common.and')} ${value}`; + return `${acc} ${translateLocal('common.and')} ${value}`; } - return `${acc}, ${Localize.translateLocal('common.and')} ${value}`; + return `${acc}, ${translateLocal('common.and')} ${value}`; } if (index === 0) { return `${acc} ${value}`; @@ -121,11 +121,11 @@ function getForDistanceRequest(newMerchant: string, oldMerchant: string, newAmou Log.hmmm("Distance request merchant doesn't match NewDot format. Defaulting to showing as distance changed.", {newMerchant, oldMerchant}); } - const translatedChangedField = Localize.translateLocal(`common.${changedField}`).toLowerCase(); + const translatedChangedField = translateLocal(`common.${changedField}`).toLowerCase(); if (!oldMerchant.length) { - return Localize.translateLocal('iou.setTheDistanceMerchant', {translatedChangedField, newMerchant, newAmountToDisplay: newAmount}); + return translateLocal('iou.setTheDistanceMerchant', {translatedChangedField, newMerchant, newAmountToDisplay: newAmount}); } - return Localize.translateLocal('iou.updatedTheDistanceMerchant', { + return translateLocal('iou.updatedTheDistanceMerchant', { translatedChangedField, newMerchant, oldMerchant, @@ -141,12 +141,12 @@ function getForDistanceRequest(newMerchant: string, oldMerchant: string, newAmou * If we change this function be sure to update the backend as well. */ function getForReportAction(reportOrID: string | SearchReport | undefined, reportAction: OnyxEntry): string { - if (!ReportActionsUtils.isModifiedExpenseAction(reportAction)) { + if (!isModifiedExpenseAction(reportAction)) { return ''; } - const reportActionOriginalMessage = ReportActionsUtils.getOriginalMessage(reportAction); + const reportActionOriginalMessage = getOriginalMessage(reportAction); const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] : reportOrID; - const policyID = report?.policyID ?? '-1'; + const policyID = report?.policyID ?? CONST.DEFAULT_NUMBER_ID; const removalFragments: string[] = []; const setFragments: string[] = []; @@ -165,10 +165,10 @@ function getForReportAction(reportOrID: string | SearchReport | undefined, repor if (hasModifiedAmount) { const oldCurrency = reportActionOriginalMessage?.oldCurrency; const oldAmountValue = reportActionOriginalMessage?.oldAmount ?? 0; - const oldAmount = oldAmountValue > 0 ? CurrencyUtils.convertToDisplayString(reportActionOriginalMessage?.oldAmount ?? 0, oldCurrency) : ''; + const oldAmount = oldAmountValue > 0 ? convertToDisplayString(reportActionOriginalMessage?.oldAmount ?? 0, oldCurrency) : ''; const currency = reportActionOriginalMessage?.currency; - const amount = CurrencyUtils.convertToDisplayString(reportActionOriginalMessage?.amount ?? 0, currency); + const amount = convertToDisplayString(reportActionOriginalMessage?.amount ?? 0, currency); // Only Distance edits should modify amount and merchant (which stores distance) in a single transaction. // We check the merchant is in distance format (includes @) as a sanity check @@ -176,7 +176,7 @@ function getForReportAction(reportOrID: string | SearchReport | undefined, repor return getForDistanceRequest(reportActionOriginalMessage?.merchant ?? '', reportActionOriginalMessage?.oldMerchant ?? '', amount, oldAmount); } - buildMessageFragmentForValue(amount, oldAmount, Localize.translateLocal('iou.amount'), false, setFragments, removalFragments, changeFragments); + buildMessageFragmentForValue(amount, oldAmount, translateLocal('iou.amount'), false, setFragments, removalFragments, changeFragments); } const hasModifiedComment = isReportActionOriginalMessageAnObject && 'oldComment' in reportActionOriginalMessage && 'newComment' in reportActionOriginalMessage; @@ -184,7 +184,7 @@ function getForReportAction(reportOrID: string | SearchReport | undefined, repor buildMessageFragmentForValue( Parser.htmlToMarkdown(reportActionOriginalMessage?.newComment ?? ''), Parser.htmlToMarkdown(reportActionOriginalMessage?.oldComment ?? ''), - Localize.translateLocal('common.description'), + translateLocal('common.description'), true, setFragments, removalFragments, @@ -195,22 +195,14 @@ function getForReportAction(reportOrID: string | SearchReport | undefined, repor if (reportActionOriginalMessage?.oldCreated && reportActionOriginalMessage?.created) { const formattedOldCreated = DateUtils.formatWithUTCTimeZone(reportActionOriginalMessage.oldCreated, CONST.DATE.FNS_FORMAT_STRING); - buildMessageFragmentForValue( - reportActionOriginalMessage.created, - formattedOldCreated, - Localize.translateLocal('common.date'), - false, - setFragments, - removalFragments, - changeFragments, - ); + buildMessageFragmentForValue(reportActionOriginalMessage.created, formattedOldCreated, translateLocal('common.date'), false, setFragments, removalFragments, changeFragments); } if (hasModifiedMerchant) { buildMessageFragmentForValue( reportActionOriginalMessage?.merchant ?? '', reportActionOriginalMessage?.oldMerchant ?? '', - Localize.translateLocal('common.merchant'), + translateLocal('common.merchant'), true, setFragments, removalFragments, @@ -223,7 +215,7 @@ function getForReportAction(reportOrID: string | SearchReport | undefined, repor buildMessageFragmentForValue( reportActionOriginalMessage?.category ?? '', reportActionOriginalMessage?.oldCategory ?? '', - Localize.translateLocal('common.category'), + translateLocal('common.category'), true, setFragments, removalFragments, @@ -236,10 +228,10 @@ function getForReportAction(reportOrID: string | SearchReport | undefined, repor const policyTags = allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {}; const transactionTag = reportActionOriginalMessage?.tag ?? ''; const oldTransactionTag = reportActionOriginalMessage?.oldTag ?? ''; - const splittedTag = TransactionUtils.getTagArrayFromName(transactionTag); - const splittedOldTag = TransactionUtils.getTagArrayFromName(oldTransactionTag); - const localizedTagListName = Localize.translateLocal('common.tag'); - const sortedTagKeys = PolicyUtils.getSortedTagKeys(policyTags); + const splittedTag = getTagArrayFromName(transactionTag); + const splittedOldTag = getTagArrayFromName(oldTransactionTag); + const localizedTagListName = translateLocal('common.tag'); + const sortedTagKeys = getSortedTagKeys(policyTags); sortedTagKeys.forEach((policyTagKey, index) => { const policyTagListName = policyTags[policyTagKey].name || localizedTagListName; @@ -249,8 +241,8 @@ function getForReportAction(reportOrID: string | SearchReport | undefined, repor if (newTag !== oldTag) { buildMessageFragmentForValue( - PolicyUtils.getCleanedTagName(newTag), - PolicyUtils.getCleanedTagName(oldTag), + getCleanedTagName(newTag), + getCleanedTagName(oldTag), policyTagListName, true, setFragments, @@ -266,10 +258,10 @@ function getForReportAction(reportOrID: string | SearchReport | undefined, repor if (hasModifiedTaxAmount) { const currency = reportActionOriginalMessage?.currency; - const taxAmount = CurrencyUtils.convertToDisplayString(getTaxAmountAbsValue(reportActionOriginalMessage?.taxAmount ?? 0), currency); + const taxAmount = convertToDisplayString(getTaxAmountAbsValue(reportActionOriginalMessage?.taxAmount ?? 0), currency); const oldTaxAmountValue = getTaxAmountAbsValue(reportActionOriginalMessage?.oldTaxAmount ?? 0); - const oldTaxAmount = oldTaxAmountValue > 0 ? CurrencyUtils.convertToDisplayString(oldTaxAmountValue, currency) : ''; - buildMessageFragmentForValue(taxAmount, oldTaxAmount, Localize.translateLocal('iou.taxAmount'), false, setFragments, removalFragments, changeFragments); + const oldTaxAmount = oldTaxAmountValue > 0 ? convertToDisplayString(oldTaxAmountValue, currency) : ''; + buildMessageFragmentForValue(taxAmount, oldTaxAmount, translateLocal('iou.taxAmount'), false, setFragments, removalFragments, changeFragments); } const hasModifiedTaxRate = isReportActionOriginalMessageAnObject && 'oldTaxRate' in reportActionOriginalMessage && 'taxRate' in reportActionOriginalMessage; @@ -277,7 +269,7 @@ function getForReportAction(reportOrID: string | SearchReport | undefined, repor buildMessageFragmentForValue( reportActionOriginalMessage?.taxRate ?? '', reportActionOriginalMessage?.oldTaxRate ?? '', - Localize.translateLocal('iou.taxRate'), + translateLocal('iou.taxRate'), false, setFragments, removalFragments, @@ -290,7 +282,7 @@ function getForReportAction(reportOrID: string | SearchReport | undefined, repor buildMessageFragmentForValue( reportActionOriginalMessage?.billable ?? '', reportActionOriginalMessage?.oldBillable ?? '', - Localize.translateLocal('iou.expense'), + translateLocal('iou.expense'), true, setFragments, removalFragments, @@ -301,9 +293,9 @@ function getForReportAction(reportOrID: string | SearchReport | undefined, repor const hasModifiedReimbursable = isReportActionOriginalMessageAnObject && 'oldReimbursable' in reportActionOriginalMessage && 'reimbursable' in reportActionOriginalMessage; if (hasModifiedReimbursable) { buildMessageFragmentForValue( - getBooleanLiteralMessage(reportActionOriginalMessage?.reimbursable, Localize.translateLocal('iou.reimbursable'), Localize.translateLocal('iou.nonReimbursable')), - getBooleanLiteralMessage(reportActionOriginalMessage?.oldReimbursable, Localize.translateLocal('iou.reimbursable'), Localize.translateLocal('iou.nonReimbursable')), - Localize.translateLocal('iou.expense'), + getBooleanLiteralMessage(reportActionOriginalMessage?.reimbursable, translateLocal('iou.reimbursable'), translateLocal('iou.nonReimbursable')), + getBooleanLiteralMessage(reportActionOriginalMessage?.oldReimbursable, translateLocal('iou.reimbursable'), translateLocal('iou.nonReimbursable')), + translateLocal('iou.expense'), true, setFragments, removalFragments, @@ -316,7 +308,7 @@ function getForReportAction(reportOrID: string | SearchReport | undefined, repor buildMessageFragmentForValue( reportActionOriginalMessage.oldAttendees ?? '', reportActionOriginalMessage.attendees ?? '', - Localize.translateLocal('iou.attendees'), + translateLocal('iou.attendees'), false, setFragments, removalFragments, @@ -325,11 +317,11 @@ function getForReportAction(reportOrID: string | SearchReport | undefined, repor } const message = - getMessageLine(`\n${Localize.translateLocal('iou.changed')}`, changeFragments) + - getMessageLine(`\n${Localize.translateLocal('iou.set')}`, setFragments) + - getMessageLine(`\n${Localize.translateLocal('iou.removed')}`, removalFragments); + getMessageLine(`\n${translateLocal('iou.changed')}`, changeFragments) + + getMessageLine(`\n${translateLocal('iou.set')}`, setFragments) + + getMessageLine(`\n${translateLocal('iou.removed')}`, removalFragments); if (message === '') { - return Localize.translateLocal('iou.changedTheExpense'); + return translateLocal('iou.changedTheExpense'); } return `${message.substring(1, message.length)}`; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 94b8260db4eb..3d95fd348b9c 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3452,13 +3452,16 @@ function updateMoneyRequestDate( /** Updates the billable field of an expense */ function updateMoneyRequestBillable( - transactionID: string, - transactionThreadReportID: string, + transactionID: string | undefined, + transactionThreadReportID: string | undefined, value: boolean, policy: OnyxEntry, policyTagList: OnyxEntry, policyCategories: OnyxEntry, ) { + if (!transactionID || !transactionThreadReportID) { + return; + } const transactionChanges: TransactionChanges = { billable: value, }; diff --git a/src/libs/actions/ReportActions.ts b/src/libs/actions/ReportActions.ts index 89517a753c26..012120606485 100644 --- a/src/libs/actions/ReportActions.ts +++ b/src/libs/actions/ReportActions.ts @@ -81,9 +81,9 @@ function clearReportActionErrors(reportID: string, reportAction: ReportAction, k ignore: `undefined` means we want to check both parent and children report actions ignore: `parent` or `child` means we want to ignore checking parent or child report actions because they've been previously checked */ -function clearAllRelatedReportActionErrors(reportID: string, reportAction: ReportAction | null | undefined, ignore?: IgnoreDirection, keys?: string[]) { +function clearAllRelatedReportActionErrors(reportID: string | undefined, reportAction: ReportAction | null | undefined, ignore?: IgnoreDirection, keys?: string[]) { const errorKeys = keys ?? Object.keys(reportAction?.errors ?? {}); - if (!reportAction || errorKeys.length === 0) { + if (!reportID || !reportAction || errorKeys.length === 0) { return; } diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index c8a007458242..a69142fc0fe2 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -457,7 +457,10 @@ function abandonReviewDuplicateTransactions() { Onyx.set(ONYXKEYS.REVIEW_DUPLICATES, null); } -function clearError(transactionID: string) { +function clearError(transactionID: string | undefined) { + if (!transactionID) { + return; + } Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {errors: null, errorFields: {route: null, waypoints: null, routes: null}}); } diff --git a/src/pages/iou/request/step/IOURequestStepDescription.tsx b/src/pages/iou/request/step/IOURequestStepDescription.tsx index 865cb13e9d40..0aa1345d9d4e 100644 --- a/src/pages/iou/request/step/IOURequestStepDescription.tsx +++ b/src/pages/iou/request/step/IOURequestStepDescription.tsx @@ -11,15 +11,15 @@ import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as ErrorUtils from '@libs/ErrorUtils'; -import * as IOUUtils from '@libs/IOUUtils'; +import {addErrorMessage} from '@libs/ErrorUtils'; +import {shouldUseTransactionDraft} from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; import Parser from '@libs/Parser'; -import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import * as ReportUtils from '@libs/ReportUtils'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import {isMoneyRequestAction} from '@libs/ReportActionsUtils'; +import {canEditMoneyRequest} from '@libs/ReportUtils'; +import {areRequiredFieldsEmpty} from '@libs/TransactionUtils'; import variables from '@styles/variables'; -import * as IOU from '@userActions/IOU'; +import {setDraftSplitTransaction, setMoneyRequestDescription, updateMoneyRequestDescription} from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -79,7 +79,7 @@ function IOURequestStepDescription({ const errors = {}; if (values.moneyRequestComment.length > CONST.DESCRIPTION_LIMIT) { - ErrorUtils.addErrorMessage( + addErrorMessage( errors, 'moneyRequestComment', translate('common.error.characterLimitExceedCounter', {length: values.moneyRequestComment.length, limit: CONST.DESCRIPTION_LIMIT}), @@ -115,16 +115,16 @@ function IOURequestStepDescription({ // In the split flow, when editing we use SPLIT_TRANSACTION_DRAFT to save draft value if (isEditingSplitBill) { - IOU.setDraftSplitTransaction(transaction?.transactionID, {comment: newComment}); + setDraftSplitTransaction(transaction?.transactionID, {comment: newComment}); navigateBack(); return; } - const isTransactionDraft = IOUUtils.shouldUseTransactionDraft(action); + const isTransactionDraft = shouldUseTransactionDraft(action); - IOU.setMoneyRequestDescription(transaction?.transactionID, newComment, isTransactionDraft); + setMoneyRequestDescription(transaction?.transactionID, newComment, isTransactionDraft); if (action === CONST.IOU.ACTION.EDIT) { - IOU.updateMoneyRequestDescription(transaction?.transactionID, reportID, newComment, policy, policyTags, policyCategories); + updateMoneyRequestDescription(transaction?.transactionID, reportID, newComment, policy, policyTags, policyCategories); } navigateBack(); @@ -132,9 +132,9 @@ function IOURequestStepDescription({ const isEditing = action === CONST.IOU.ACTION.EDIT; const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT; - const canEditSplitBill = isSplitBill && reportAction && session?.accountID === reportAction.actorAccountID && TransactionUtils.areRequiredFieldsEmpty(transaction); + const canEditSplitBill = isSplitBill && reportAction && session?.accountID === reportAction.actorAccountID && areRequiredFieldsEmpty(transaction); // eslint-disable-next-line rulesdir/no-negated-variables - const shouldShowNotFoundPage = isEditing && (isSplitBill ? !canEditSplitBill : !ReportActionsUtils.isMoneyRequestAction(reportAction) || !ReportUtils.canEditMoneyRequest(reportAction)); + const shouldShowNotFoundPage = isEditing && (isSplitBill ? !canEditSplitBill : !isMoneyRequestAction(reportAction) || !canEditMoneyRequest(reportAction)); const isReportInGroupPolicy = !!report?.policyID && report.policyID !== CONST.POLICY.ID_FAKE; return ( From 51073b30301275acfd2410eba906be646fae7b16 Mon Sep 17 00:00:00 2001 From: dominictb Date: Wed, 15 Jan 2025 17:25:44 +0700 Subject: [PATCH 06/15] fix lint --- src/libs/actions/ReportActions.ts | 9 +++-- src/libs/actions/Transaction.ts | 57 +++++++++++++++++++------------ 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/libs/actions/ReportActions.ts b/src/libs/actions/ReportActions.ts index 012120606485..393f703537fa 100644 --- a/src/libs/actions/ReportActions.ts +++ b/src/libs/actions/ReportActions.ts @@ -6,7 +6,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; import type ReportAction from '@src/types/onyx/ReportAction'; -import * as Report from './Report'; +import {deleteReport} from './Report'; type IgnoreDirection = 'parent' | 'child'; @@ -40,8 +40,7 @@ function clearReportActionErrors(reportID: string, reportAction: ReportAction, k }); // If there's a linked transaction, delete that too - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const linkedTransactionID = ReportActionUtils.getLinkedTransactionID(reportAction.reportActionID, originalReportID || '-1'); + const linkedTransactionID = ReportActionUtils.getLinkedTransactionID(reportAction.reportActionID, originalReportID); if (linkedTransactionID) { Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${linkedTransactionID}`, null); Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportAction.childReportID}`, null); @@ -50,7 +49,7 @@ function clearReportActionErrors(reportID: string, reportAction: ReportAction, k // Delete the failed task report too const taskReportID = ReportActionUtils.getReportActionMessage(reportAction)?.taskReportID; if (taskReportID && ReportActionUtils.isCreatedTaskReportAction(reportAction)) { - Report.deleteReport(taskReportID); + deleteReport(taskReportID); } return; } @@ -101,7 +100,7 @@ function clearAllRelatedReportActionErrors(reportID: string | undefined, reportA const childActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportAction.childReportID}`] ?? {}; Object.values(childActions).forEach((action) => { const childErrorKeys = Object.keys(action.errors ?? {}).filter((err) => errorKeys.includes(err)); - clearAllRelatedReportActionErrors(reportAction.childReportID ?? '-1', action, 'parent', childErrorKeys); + clearAllRelatedReportActionErrors(reportAction.childReportID, action, 'parent', childErrorKeys); }); } } diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index a69142fc0fe2..7bd22ffc9c69 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -355,7 +355,7 @@ function updateWaypoints(transactionID: string, waypoints: WaypointCollection, i function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmissedPersonalDetails: PersonalDetails) { const currentTransactionViolations = transactionIDs.map((id) => ({transactionID: id, violations: allTransactionViolation?.[id] ?? []})); const currentTransactions = transactionIDs.map((id) => allTransactions?.[id]); - const transactionsReportActions = currentTransactions.map((transaction) => ReportActionsUtils.getIOUActionForReportID(transaction.reportID ?? '', transaction.transactionID ?? '')); + const transactionsReportActions = currentTransactions.map((transaction) => ReportActionsUtils.getIOUActionForReportID(transaction.reportID, transaction.transactionID)); const optimisticDissmidedViolationReportActions = transactionsReportActions.map(() => { return buildOptimisticDismissedViolationReportAction({reason: 'manual', violationName: CONST.VIOLATIONS.DUPLICATED_TRANSACTION}); }); @@ -363,13 +363,16 @@ function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmiss const optimisticData: OnyxUpdate[] = []; const failureData: OnyxUpdate[] = []; - const optimisticReportActions: OnyxUpdate[] = transactionsReportActions.map((action, index) => ({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${action?.childReportID ?? '-1'}`, - value: { - [optimisticDissmidedViolationReportActions.at(index)?.reportActionID ?? '']: optimisticDissmidedViolationReportActions.at(index) as ReportAction, - }, - })); + const optimisticReportActions: OnyxUpdate[] = transactionsReportActions.map((action, index) => { + const optimisticDissmidedViolationReportAction = optimisticDissmidedViolationReportActions.at(index); + return { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${action?.childReportID}`, + value: optimisticDissmidedViolationReportAction + ? {[optimisticDissmidedViolationReportAction.reportActionID]: optimisticDissmidedViolationReportAction as ReportAction} + : undefined, + }; + }); const optimisticDataTransactionViolations: OnyxUpdate[] = currentTransactionViolations.map((transactionViolations) => ({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionViolations.transactionID}`, @@ -411,25 +414,35 @@ function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmiss }, })); - const failureReportActions: OnyxUpdate[] = transactionsReportActions.map((action, index) => ({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${action?.childReportID ?? '-1'}`, - value: { - [optimisticDissmidedViolationReportActions.at(index)?.reportActionID ?? '']: null, - }, - })); + const failureReportActions: OnyxUpdate[] = transactionsReportActions.map((action, index) => { + const optimisticDissmidedViolationReportAction = optimisticDissmidedViolationReportActions.at(index); + return { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${action?.childReportID}`, + value: optimisticDissmidedViolationReportAction + ? { + [optimisticDissmidedViolationReportAction.reportActionID]: null, + } + : undefined, + }; + }); failureData.push(...failureDataTransactionViolations); failureData.push(...failureDataTransaction); failureData.push(...failureReportActions); - const successData: OnyxUpdate[] = transactionsReportActions.map((action, index) => ({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${action?.childReportID ?? '-1'}`, - value: { - [optimisticDissmidedViolationReportActions.at(index)?.reportActionID ?? '']: null, - }, - })); + const successData: OnyxUpdate[] = transactionsReportActions.map((action, index) => { + const optimisticDissmidedViolationReportAction = optimisticDissmidedViolationReportActions.at(index); + return { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${action?.childReportID}`, + value: optimisticDissmidedViolationReportAction + ? { + [optimisticDissmidedViolationReportAction.reportActionID]: null, + } + : undefined, + }; + }); // We are creating duplicate resolved report actions for each duplicate transactions and all the report actions // should be correctly linked with their parent report but the BE is sometimes linking report actions to different // parent reports than the one we set optimistically, resulting in duplicate report actions. Therefore, we send the BE From 96edb95d16f44d0de9a67ee40423c489dbd55087 Mon Sep 17 00:00:00 2001 From: dominictb Date: Fri, 7 Feb 2025 02:11:30 +0700 Subject: [PATCH 07/15] fix lint --- src/ROUTES.ts | 2 +- src/libs/actions/Transaction.ts | 5 +---- src/pages/iou/request/step/IOURequestStepDescription.tsx | 1 + 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index b614d62acb46..a88c1c2f0b04 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -538,7 +538,7 @@ const ROUTES = { }, MONEY_REQUEST_STEP_CATEGORY: { route: ':action/:iouType/category/:transactionID/:reportID/:reportActionID?', - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined | undefined, backTo = '', reportActionID?: string) => { + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, backTo = '', reportActionID?: string) => { if (!transactionID || !reportID) { Log.warn('Invalid transactionID or reportID is used to build the MONEY_REQUEST_STEP_CATEGORY route'); } diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index beddbd587b02..d2f996c3e965 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -473,10 +473,7 @@ function abandonReviewDuplicateTransactions() { Onyx.set(ONYXKEYS.REVIEW_DUPLICATES, null); } -function clearError(transactionID: string | undefined) { - if (!transactionID) { - return; - } +function clearError(transactionID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {errors: null, errorFields: {route: null, waypoints: null, routes: null}}); } diff --git a/src/pages/iou/request/step/IOURequestStepDescription.tsx b/src/pages/iou/request/step/IOURequestStepDescription.tsx index e5b906c4cb19..b29c8b9e5c7d 100644 --- a/src/pages/iou/request/step/IOURequestStepDescription.tsx +++ b/src/pages/iou/request/step/IOURequestStepDescription.tsx @@ -14,6 +14,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {addErrorMessage} from '@libs/ErrorUtils'; import {shouldUseTransactionDraft} from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; +import Parser from '@libs/Parser'; import {isMoneyRequestAction} from '@libs/ReportActionsUtils'; import {canEditMoneyRequest} from '@libs/ReportUtils'; import {areRequiredFieldsEmpty} from '@libs/TransactionUtils'; From 00c314ae5b39136dda488f338b2fc1ef2ac9b565 Mon Sep 17 00:00:00 2001 From: dominictb Date: Fri, 7 Feb 2025 03:02:57 +0700 Subject: [PATCH 08/15] implement html description for request money --- src/libs/API/parameters/RequestMoneyParams.ts | 1 + src/libs/actions/IOU.ts | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libs/API/parameters/RequestMoneyParams.ts b/src/libs/API/parameters/RequestMoneyParams.ts index 4fe0a0a29e9f..ea375f32fe0d 100644 --- a/src/libs/API/parameters/RequestMoneyParams.ts +++ b/src/libs/API/parameters/RequestMoneyParams.ts @@ -28,6 +28,7 @@ type RequestMoneyParams = { transactionThreadReportID: string; createdReportActionIDForThread: string | undefined; reimbursible?: boolean; + description?: string; }; export default RequestMoneyParams; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 5dfdd796d6dd..f9379616d964 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4064,7 +4064,7 @@ function updateMoneyRequestDescription( ) { const parsedComment = getParsedComment(comment); const transactionChanges: TransactionChanges = { - comment, + comment: parsedComment, }; const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; @@ -4407,6 +4407,8 @@ function shareTrackedExpense(trackedExpenseParams: TrackedExpenseParams) { function requestMoney(requestMoneyInformation: RequestMoneyInformation) { const {report, participantParams, policyParams = {}, transactionParams, gpsPoints, action, reimbursible} = requestMoneyInformation; const {payeeAccountID} = participantParams; + const parsedComment = getParsedComment(transactionParams.comment ?? ''); + transactionParams.comment = parsedComment; const { amount, currency, @@ -4536,6 +4538,7 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) { transactionThreadReportID, createdReportActionIDForThread, reimbursible, + description: parsedComment, }; // eslint-disable-next-line rulesdir/no-multiple-api-calls From 32820201c43fcb9920d44af8ad6a25ba376e108f Mon Sep 17 00:00:00 2001 From: dominictb Date: Fri, 7 Feb 2025 03:24:57 +0700 Subject: [PATCH 09/15] show markdown text in iou preview --- .../MoneyRequestPreview/MoneyRequestPreviewContent.tsx | 3 ++- src/components/ReportActionItem/ReportPreview.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index 7eaee699018d..3d8b7b927214 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -32,6 +32,7 @@ import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types'; import {getAvatarsForAccountIDs} from '@libs/OptionsListUtils'; +import Parser from '@libs/Parser'; import {getCleanedTagName} from '@libs/PolicyUtils'; import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; import {getOriginalMessage, getReportAction, isMessageDeleted, isMoneyRequestAction as isMoneyRequestActionReportActionsUtils} from '@libs/ReportActionsUtils'; @@ -139,7 +140,7 @@ function MoneyRequestPreviewContent({ category, } = useMemo>(() => getTransactionDetails(transaction) ?? {}, [transaction]); - const description = truncate(StringUtils.lineBreaksToSpaces(requestComment), {length: CONST.REQUEST_PREVIEW.MAX_LENGTH}); + const description = truncate(StringUtils.lineBreaksToSpaces(Parser.htmlToMarkdown(requestComment ?? '')), {length: CONST.REQUEST_PREVIEW.MAX_LENGTH}); const requestMerchant = truncate(merchant, {length: CONST.REQUEST_PREVIEW.MAX_LENGTH}); const hasReceipt = hasReceiptTransactionUtils(transaction); const isScanning = hasReceipt && isReceiptBeingScanned(transaction); diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index b05add5bc162..9bf570f33cf8 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -26,6 +26,7 @@ import {convertToDisplayString} from '@libs/CurrencyUtils'; import {canUseTouchScreen} from '@libs/DeviceCapabilities'; import HapticFeedback from '@libs/HapticFeedback'; import Navigation from '@libs/Navigation/Navigation'; +import Parser from '@libs/Parser'; import Performance from '@libs/Performance'; import {getConnectedIntegration} from '@libs/PolicyUtils'; import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; @@ -241,7 +242,7 @@ function ReportPreview({ const showRTERViolationMessage = numberOfRequests === 1 && hasPendingUI(lastTransaction, getTransactionViolations(lastTransaction?.transactionID)); const shouldShowBrokenConnectionViolation = numberOfRequests === 1 && shouldShowBrokenConnectionViolationTransactionUtils(transactionIDList, iouReport, policy); let formattedMerchant = numberOfRequests === 1 ? getMerchant(lastTransaction) : null; - const formattedDescription = numberOfRequests === 1 ? getDescription(lastTransaction) : null; + const formattedDescription = numberOfRequests === 1 ? Parser.htmlToMarkdown(getDescription(lastTransaction)) : null; if (isPartialMerchant(formattedMerchant ?? '')) { formattedMerchant = null; From 94d58637c7bdf9542792610b563e7d8722c1569c Mon Sep 17 00:00:00 2001 From: dominictb Date: Wed, 5 Mar 2025 04:36:03 +0700 Subject: [PATCH 10/15] implement html description for track expense --- src/libs/API/parameters/TrackExpenseParams.ts | 1 + src/libs/actions/IOU.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/libs/API/parameters/TrackExpenseParams.ts b/src/libs/API/parameters/TrackExpenseParams.ts index 89b8d79e3b9a..e11b119e2f83 100644 --- a/src/libs/API/parameters/TrackExpenseParams.ts +++ b/src/libs/API/parameters/TrackExpenseParams.ts @@ -28,6 +28,7 @@ type TrackExpenseParams = { waypoints?: string; actionableWhisperReportActionID?: string; customUnitRateID?: string; + description?: string; }; export default TrackExpenseParams; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index c606ce6be200..92e4840166af 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4872,6 +4872,8 @@ function trackExpense(params: CreateTrackExpenseParams) { const {report, action, isDraftPolicy, participantParams, policyParams: policyData = {}, transactionParams: transactionData} = params; const {participant, payeeAccountID, payeeEmail} = participantParams; const {policy, policyCategories, policyTagList} = policyData; + const parsedComment = getParsedComment(transactionData.comment ?? ''); + transactionData.comment = parsedComment; const { amount, currency, @@ -5074,6 +5076,7 @@ function trackExpense(params: CreateTrackExpenseParams) { createdReportActionIDForThread, waypoints: sanitizedWaypoints, customUnitRateID, + description: parsedComment, }; if (actionableWhisperReportActionIDParam) { parameters.actionableWhisperReportActionID = actionableWhisperReportActionIDParam; From 9b78a33c802f4475e6a5fc886d41714a876c1bcb Mon Sep 17 00:00:00 2001 From: dominictb Date: Wed, 5 Mar 2025 05:09:51 +0700 Subject: [PATCH 11/15] implement html description for categorize & share track expense --- src/libs/API/parameters/CategorizeTrackedExpenseParams.ts | 1 + src/libs/API/parameters/ShareTrackedExpenseParams.ts | 1 + src/libs/ReportUtils.ts | 1 + src/libs/actions/IOU.ts | 2 ++ 4 files changed, 5 insertions(+) diff --git a/src/libs/API/parameters/CategorizeTrackedExpenseParams.ts b/src/libs/API/parameters/CategorizeTrackedExpenseParams.ts index 4b1d40dadaa3..96eb81324b81 100644 --- a/src/libs/API/parameters/CategorizeTrackedExpenseParams.ts +++ b/src/libs/API/parameters/CategorizeTrackedExpenseParams.ts @@ -28,6 +28,7 @@ type CategorizeTrackedExpenseParams = { adminsCreatedReportActionID?: string; guidedSetupData?: string; engagementChoice?: string; + description?: string; }; export default CategorizeTrackedExpenseParams; diff --git a/src/libs/API/parameters/ShareTrackedExpenseParams.ts b/src/libs/API/parameters/ShareTrackedExpenseParams.ts index c851af4e227b..be143ae82ed8 100644 --- a/src/libs/API/parameters/ShareTrackedExpenseParams.ts +++ b/src/libs/API/parameters/ShareTrackedExpenseParams.ts @@ -28,6 +28,7 @@ type ShareTrackedExpenseParams = { adminsCreatedReportActionID?: string; engagementChoice?: string; guidedSetupData?: string; + description?: string; }; export default ShareTrackedExpenseParams; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 8021d115546c..e07e5c7352d7 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3492,6 +3492,7 @@ function getTransactionDetails(transaction: OnyxInputOrEntry, creat function getTransactionCommentObject(transaction: OnyxEntry): Comment { return { ...transaction?.comment, + comment: Parser.htmlToMarkdown(transaction?.comment?.comment ?? ''), waypoints: getWaypoints(transaction), }; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 92e4840166af..897977ebc46d 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4495,6 +4495,7 @@ function categorizeTrackedExpense(trackedExpenseParams: TrackedExpenseParams) { adminsCreatedReportActionID: createdWorkspaceParams?.adminsCreatedReportActionID, engagementChoice: createdWorkspaceParams?.engagementChoice, guidedSetupData: createdWorkspaceParams?.guidedSetupData, + description: transactionParams.comment, }; API.write(WRITE_COMMANDS.CATEGORIZE_TRACKED_EXPENSE, parameters, {optimisticData, successData, failureData}); @@ -4555,6 +4556,7 @@ function shareTrackedExpense(trackedExpenseParams: TrackedExpenseParams) { adminsCreatedReportActionID: createdWorkspaceParams?.adminsCreatedReportActionID, engagementChoice: createdWorkspaceParams?.engagementChoice, guidedSetupData: createdWorkspaceParams?.guidedSetupData, + description: transactionParams.comment, }; API.write(WRITE_COMMANDS.SHARE_TRACKED_EXPENSE, parameters, {optimisticData, successData, failureData}); From dbd1178b0a45f43ac5f72ea94740ca0653d6b43e Mon Sep 17 00:00:00 2001 From: dominictb Date: Thu, 6 Mar 2025 02:30:59 +0700 Subject: [PATCH 12/15] implement html description for distance request --- src/libs/API/parameters/CreateDistanceRequestParams.ts | 1 + src/libs/actions/IOU.ts | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/libs/API/parameters/CreateDistanceRequestParams.ts b/src/libs/API/parameters/CreateDistanceRequestParams.ts index 81385d092530..915a3e4cc133 100644 --- a/src/libs/API/parameters/CreateDistanceRequestParams.ts +++ b/src/libs/API/parameters/CreateDistanceRequestParams.ts @@ -20,6 +20,7 @@ type CreateDistanceRequestParams = { payerEmail?: string; splits?: string; chatType?: string; + description?: string; }; export default CreateDistanceRequestParams; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 897977ebc46d..425bba4e8c83 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6352,6 +6352,8 @@ function createDistanceRequest(distanceRequestInformation: CreateDistanceRequest policyParams = {}, } = distanceRequestInformation; const {policy, policyCategories, policyTagList} = policyParams; + const parsedComment = getParsedComment(transactionParams.comment); + transactionParams.comment = parsedComment; const {amount, comment, currency, created, category, tag, taxAmount, taxCode, merchant, billable, validWaypoints, customUnitRateID = '', splitShares = {}} = transactionParams; // If the report is an iou or expense report, we should get the linked chat report to be passed to the getMoneyRequestInformation function @@ -6412,6 +6414,7 @@ function createDistanceRequest(distanceRequestInformation: CreateDistanceRequest billable, splits: JSON.stringify(splits), chatType: splitData.chatType, + description: parsedComment, }; } else { const participant = participants.at(0) ?? {}; @@ -6478,6 +6481,7 @@ function createDistanceRequest(distanceRequestInformation: CreateDistanceRequest createdReportActionIDForThread, payerEmail, customUnitRateID, + description: parsedComment, }; } From 290a12451fb869b5d01d7c312e7a4b106497269d Mon Sep 17 00:00:00 2001 From: dominictb Date: Thu, 6 Mar 2025 02:43:27 +0700 Subject: [PATCH 13/15] implement html description for split bills --- src/libs/API/parameters/SplitBillParams.ts | 1 + .../API/parameters/StartSplitBillParams.ts | 1 + src/libs/actions/IOU.ts | 20 ++++++++++++------- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/libs/API/parameters/SplitBillParams.ts b/src/libs/API/parameters/SplitBillParams.ts index 76252abe3292..3fda11b9ca98 100644 --- a/src/libs/API/parameters/SplitBillParams.ts +++ b/src/libs/API/parameters/SplitBillParams.ts @@ -17,6 +17,7 @@ type SplitBillParams = { splitPayerAccountIDs: number[]; taxCode: string; taxAmount: number; + description?: string; }; export default SplitBillParams; diff --git a/src/libs/API/parameters/StartSplitBillParams.ts b/src/libs/API/parameters/StartSplitBillParams.ts index 499073c88de9..10f1029a0fba 100644 --- a/src/libs/API/parameters/StartSplitBillParams.ts +++ b/src/libs/API/parameters/StartSplitBillParams.ts @@ -16,6 +16,7 @@ type StartSplitBillParams = { chatType?: string; taxCode?: string; taxAmount?: number; + description?: string; }; export default StartSplitBillParams; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 425bba4e8c83..e21676ae6ec6 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5622,6 +5622,7 @@ function splitBill({ taxCode = '', taxAmount = 0, }: SplitBillActionsParams) { + const parsedComment = getParsedComment(comment); const {splitData, splits, onyxData} = createSplitsAndOnyxData({ participants, currentUserLogin, @@ -5629,7 +5630,7 @@ function splitBill({ existingSplitChatReportID, transactionParams: { amount, - comment, + comment: parsedComment, currency, merchant, created, @@ -5648,7 +5649,7 @@ function splitBill({ amount, splits: JSON.stringify(splits), currency, - comment, + comment: parsedComment, category, merchant, created, @@ -5662,6 +5663,7 @@ function splitBill({ splitPayerAccountIDs, taxCode, taxAmount, + description: parsedComment, }; API.write(WRITE_COMMANDS.SPLIT_BILL, parameters, onyxData); @@ -5693,6 +5695,7 @@ function splitBillAndOpenReport({ taxAmount = 0, existingSplitChatReportID, }: SplitBillActionsParams) { + const parsedComment = getParsedComment(comment); const {splitData, splits, onyxData} = createSplitsAndOnyxData({ participants, currentUserLogin, @@ -5700,7 +5703,7 @@ function splitBillAndOpenReport({ existingSplitChatReportID, transactionParams: { amount, - comment, + comment: parsedComment, currency, merchant, created, @@ -5721,7 +5724,7 @@ function splitBillAndOpenReport({ currency, merchant, created, - comment, + comment: parsedComment, category, tag, billable, @@ -5733,6 +5736,7 @@ function splitBillAndOpenReport({ splitPayerAccountIDs, taxCode, taxAmount, + description: parsedComment, }; API.write(WRITE_COMMANDS.SPLIT_BILL_AND_OPEN_REPORT, parameters, onyxData); @@ -5780,6 +5784,7 @@ function startSplitBill({ const participantAccountIDs = participants.map((participant) => Number(participant.accountID)); const {splitChatReport, existingSplitChatReport} = getOrCreateOptimisticSplitChatReport(existingSplitChatReportID, participants, participantAccountIDs, currentUserAccountID); const isOwnPolicyExpenseChat = !!splitChatReport.isOwnPolicyExpenseChat; + const parsedComment = getParsedComment(comment); const {name: filename, source, state = CONST.IOU.RECEIPT_STATE.SCANREADY} = receipt; const receiptObject: Receipt = {state, source}; @@ -5790,7 +5795,7 @@ function startSplitBill({ amount: 0, currency, reportID: CONST.REPORT.SPLIT_REPORTID, - comment, + comment: parsedComment, merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, receipt: receiptObject, category, @@ -5808,7 +5813,7 @@ function startSplitBill({ CONST.IOU.REPORT_ACTION_TYPE.SPLIT, 0, CONST.CURRENCY.USD, - comment, + parsedComment, participants, splitTransaction.transactionID, undefined, @@ -6045,7 +6050,7 @@ function startSplitBill({ transactionID: splitTransaction.transactionID, splits: JSON.stringify(splits), receipt, - comment, + comment: parsedComment, category, tag, currency, @@ -6055,6 +6060,7 @@ function startSplitBill({ chatType: splitChatReport?.chatType, taxCode, taxAmount, + description: parsedComment, }; API.write(WRITE_COMMANDS.START_SPLIT_BILL, parameters, {optimisticData, successData, failureData}); From 7df8a6d4cf1a8be436c68717e4893efe32d467c5 Mon Sep 17 00:00:00 2001 From: dominictb Date: Fri, 7 Mar 2025 02:33:51 +0700 Subject: [PATCH 14/15] implement html description for invoices --- src/libs/API/parameters/SendInvoiceParams.ts | 1 + src/libs/actions/IOU.ts | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libs/API/parameters/SendInvoiceParams.ts b/src/libs/API/parameters/SendInvoiceParams.ts index 2b172ee6ce6d..4ef7b4023bc6 100644 --- a/src/libs/API/parameters/SendInvoiceParams.ts +++ b/src/libs/API/parameters/SendInvoiceParams.ts @@ -23,6 +23,7 @@ type SendInvoiceParams = RequireAtLeastOne< createdIOUReportActionID: string; createdReportActionIDForThread: string | undefined; reportActionID: string; + description?: string; }, 'receiverEmail' | 'receiverInvoiceRoomID' >; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index e21676ae6ec6..aebae86e02c5 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4817,6 +4817,11 @@ function sendInvoice( companyName?: string, companyWebsite?: string, ) { + const parsedComment = getParsedComment(transaction?.comment?.comment?.trim() ?? ''); + if (transaction?.comment) { + // eslint-disable-next-line no-param-reassign + transaction.comment.comment = parsedComment; + } const { senderWorkspaceID, receiver, @@ -4840,7 +4845,7 @@ function sendInvoice( accountID: currentUserAccountID, amount: transaction?.amount ?? 0, currency: transaction?.currency ?? '', - comment: transaction?.comment?.comment?.trim() ?? '', + comment: parsedComment, merchant: transaction?.merchant ?? '', category: transaction?.category, date: transaction?.created ?? '', @@ -4852,6 +4857,7 @@ function sendInvoice( transactionThreadReportID, companyName, companyWebsite, + description: parsedComment, ...(invoiceChatReport?.reportID ? {receiverInvoiceRoomID: invoiceChatReport.reportID} : {receiverEmail: receiver.login ?? ''}), }; From 26631ad0a9ef2758340dddf1992c3b86112d1e30 Mon Sep 17 00:00:00 2001 From: dominictb Date: Fri, 7 Mar 2025 02:53:16 +0700 Subject: [PATCH 15/15] implement html description for complete split bill --- src/libs/API/parameters/CompleteSplitBillParams.ts | 1 + src/libs/actions/IOU.ts | 10 ++++++++-- src/pages/iou/SplitBillDetailsPage.tsx | 3 ++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/libs/API/parameters/CompleteSplitBillParams.ts b/src/libs/API/parameters/CompleteSplitBillParams.ts index 123ee7b05257..69faf7f0a677 100644 --- a/src/libs/API/parameters/CompleteSplitBillParams.ts +++ b/src/libs/API/parameters/CompleteSplitBillParams.ts @@ -11,6 +11,7 @@ type CompleteSplitBillParams = { taxCode?: string; taxAmount?: number; billable?: boolean; + description?: string; }; export default CompleteSplitBillParams; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index aebae86e02c5..4b632c07a7ea 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6090,6 +6090,11 @@ function completeSplitBill( sessionAccountID: number, sessionEmail?: string, ) { + const parsedComment = getParsedComment(updatedTransaction?.comment?.comment ?? ''); + if (updatedTransaction?.comment) { + // eslint-disable-next-line no-param-reassign + updatedTransaction.comment.comment = parsedComment; + } const currentUserEmailForIOUSplit = addSMSDomainIfPhoneNumber(sessionEmail); const transactionID = updatedTransaction?.transactionID; const unmodifiedTransaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; @@ -6216,7 +6221,7 @@ function completeSplitBill( amount: isPolicyExpenseChat ? -splitAmount : splitAmount, currency: currency ?? '', reportID: oneOnOneIOUReport?.reportID, - comment: updatedTransaction?.comment?.comment, + comment: parsedComment, created: updatedTransaction?.modifiedCreated, merchant: updatedTransaction?.modifiedMerchant, receipt: {...updatedTransaction?.receipt, state: CONST.IOU.RECEIPT_STATE.OPEN}, @@ -6236,7 +6241,7 @@ function completeSplitBill( CONST.IOU.REPORT_ACTION_TYPE.CREATE, splitAmount, currency ?? '', - updatedTransaction?.comment?.comment ?? '', + parsedComment, currentUserEmailForIOUSplit, [participant], oneOnOneTransaction.transactionID, @@ -6320,6 +6325,7 @@ function completeSplitBill( taxCode: transactionTaxCode, taxAmount: transactionTaxAmount, billable: transactionBillable, + description: parsedComment, }; API.write(WRITE_COMMANDS.COMPLETE_SPLIT_BILL, parameters, {optimisticData, successData, failureData}); diff --git a/src/pages/iou/SplitBillDetailsPage.tsx b/src/pages/iou/SplitBillDetailsPage.tsx index cb8d6766d8b6..327d88d303fe 100644 --- a/src/pages/iou/SplitBillDetailsPage.tsx +++ b/src/pages/iou/SplitBillDetailsPage.tsx @@ -17,6 +17,7 @@ import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SplitDetailsNavigatorParamList} from '@libs/Navigation/types'; import {getParticipantsOption, getPolicyExpenseReportOption} from '@libs/OptionsListUtils'; +import Parser from '@libs/Parser'; import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils'; import {getTransactionDetails, isPolicyExpenseChat} from '@libs/ReportUtils'; import type {OptionData} from '@libs/ReportUtils'; @@ -114,7 +115,7 @@ function SplitBillDetailsPage({route, report, reportAction}: SplitBillDetailsPag selectedParticipants={participantsExcludingPayee} iouAmount={splitAmount ?? 0} iouCurrencyCode={splitCurrency} - iouComment={splitComment} + iouComment={Parser.htmlToMarkdown(splitComment ?? '')} iouCreated={splitCreated} shouldDisplayReceipt iouMerchant={splitMerchant}