@@ -8,6 +8,7 @@ import useNetwork from '@hooks/useNetwork';
88import useOnyx from '@hooks/useOnyx' ;
99import { search } from '@libs/actions/Search' ;
1010import { getDisplayableExpensifyCards , getDisplayableThirdPartyCards , isPersonalCard , lastFourNumbersFromCardName } from '@libs/CardUtils' ;
11+ // eslint-disable-next-line no-restricted-imports -- Your Spend scopes approval/payment workflows to paid (Collect/Control) group policies, so this is a genuine billing/paid-only check.
1112import { arePaymentsEnabled , isPaidGroupPolicy } from '@libs/PolicyUtils' ;
1213import { buildSearchQueryJSON } from '@libs/SearchQueryUtils' ;
1314import { getSuggestedSearches , getSuggestedSearchesVisibility , TODO_SEARCH_KEYS } from '@libs/SearchUIUtils' ;
@@ -113,6 +114,24 @@ function getOutstandingReportsSignature(reports: OnyxCollection<Report> | undefi
113114 return ids . sort ( ) . join ( ',' ) ;
114115}
115116
117+ // Signature of the reports the user owns that are currently REPAID (reimbursed). The home query results are
118+ // cached snapshots that are not patched when a report's state changes, so without this the cached "Repaid"
119+ // total can be resurrected after the user cancels the payment of their last repaid expense (the snapshot count
120+ // gets wiped to null by `shouldCalculateTotals: false` searches, which would otherwise re-show the stale total).
121+ // Mirrors `getOutstandingReportsSignature` for the "Awaiting approval" row.
122+ function getRepaidReportsSignature ( reports : OnyxCollection < Report > | undefined , accountID : number ) : string {
123+ if ( ! reports ) {
124+ return '' ;
125+ }
126+ const ids : string [ ] = [ ] ;
127+ for ( const report of Object . values ( reports ) ) {
128+ if ( report ?. ownerAccountID === accountID && ( report . stateNum ?? 0 ) >= CONST . REPORT . STATE_NUM . APPROVED && report . statusNum === CONST . REPORT . STATUS_NUM . REIMBURSED ) {
129+ ids . push ( report . reportID ) ;
130+ }
131+ }
132+ return ids . sort ( ) . join ( ',' ) ;
133+ }
134+
116135function getYourSpendRowState ( { isApplicable, isOffline, searchResults} : GetYourSpendRowStateParams ) : YourSpendRowState {
117136 if ( ! isApplicable ) {
118137 return YOUR_SPEND_ROW_STATE . HIDDEN ;
@@ -158,6 +177,12 @@ function useYourSpendData(): UseYourSpendDataReturn {
158177 selector : ( reports ) => getOutstandingReportsSignature ( reports , paidGroupPolicyIDs , accountID ) ,
159178 } ) ;
160179
180+ // Signature of the reports the user owns that are currently REPAID. Lets the "Repaid" cached-total fallback
181+ // drop its stale value once the user no longer owns any repaid report (e.g. after cancelling a payment).
182+ const [ repaidReportsSignature ] = useOnyx ( ONYXKEYS . COLLECTION . REPORT , {
183+ selector : ( reports ) => getRepaidReportsSignature ( reports , accountID ) ,
184+ } ) ;
185+
161186 // Destructure here so downstream memos depend only on the sub-records, not on
162187 // the parent value that's rebuilt on every CARD_FEED_ERRORS tick.
163188 const { cardsWithBrokenFeedConnection, personalCardsWithBrokenConnection} = useCardFeedErrors ( ) ;
@@ -355,7 +380,16 @@ function useYourSpendData(): UseYourSpendDataReturn {
355380 cachedApprovalReady !== null &&
356381 cachedApprovalHash === approvalHash &&
357382 outstandingReportsSignature !== '' ;
358- const shouldUseCachedPayment = paymentRowStateRaw === YOUR_SPEND_ROW_STATE . HIDDEN_EMPTY && paymentCountIsMissing && paymentSearchResults !== undefined && cachedPaymentReady !== null ;
383+ // Only bridge a wiped/missing count with the cached total while the user still owns a REPAID report.
384+ // An empty signature means nothing is repaid, so the row must hide immediately after cancelling the
385+ // payment of the last repaid expense — otherwise the stale cached total is resurrected when a
386+ // `shouldCalculateTotals: false` search wipes the snapshot count to null.
387+ const shouldUseCachedPayment =
388+ paymentRowStateRaw === YOUR_SPEND_ROW_STATE . HIDDEN_EMPTY &&
389+ paymentCountIsMissing &&
390+ paymentSearchResults !== undefined &&
391+ cachedPaymentReady !== null &&
392+ repaidReportsSignature !== '' ;
359393
360394 const approvalRowState = shouldUseCachedApproval ? YOUR_SPEND_ROW_STATE . READY : approvalRowStateRaw ;
361395 const paymentRowState = shouldUseCachedPayment ? YOUR_SPEND_ROW_STATE . READY : paymentRowStateRaw ;
@@ -457,5 +491,5 @@ function useYourSpendData(): UseYourSpendDataReturn {
457491 } ;
458492}
459493
460- export { YOUR_SPEND_CARD_KIND , YOUR_SPEND_ROW_STATE , getOutstandingReportsSignature , getYourSpendApplicability , getYourSpendRowState , useYourSpendData } ;
494+ export { YOUR_SPEND_CARD_KIND , YOUR_SPEND_ROW_STATE , getOutstandingReportsSignature , getRepaidReportsSignature , getYourSpendApplicability , getYourSpendRowState , useYourSpendData } ;
461495export type { GetYourSpendRowStateParams , UseYourSpendDataReturn , YourSpendApplicability , YourSpendCardKind , YourSpendCardRow , YourSpendRowState , YourSpendRowTotals } ;
0 commit comments