Skip to content

Feature/pocket repository conflict resolution - #3152

Open
Kartikey15dem wants to merge 11 commits into
openMF:devfrom
Kartikey15dem:feature/pocket-repository-conflict-resolution
Open

Feature/pocket repository conflict resolution#3152
Kartikey15dem wants to merge 11 commits into
openMF:devfrom
Kartikey15dem:feature/pocket-repository-conflict-resolution

Conversation

@Kartikey15dem

@Kartikey15dem Kartikey15dem commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Fixes - Jira-#590

Implemented conflict resolution for the pocket repository to avoid ambiguity between pocket network APIs and offline fallback.

Summary by CodeRabbit

  • New Features

    • Pocket accounts now synchronize more reliably between local and online data.
    • Pocket data can be refreshed to provide a clean, up-to-date view.
    • Total balances are displayed separately for each currency.
  • Bug Fixes

    • Improved handling of account linking and removal when connectivity is limited.
    • Corrected currency formatting across supported platforms.
    • Updated the pocket account search hint for clearer wording.
    • Pocket dashboard cards now expand to accommodate their content.

feat(core/network):implement pocket repository

feat(core/network):implement pocket repository

feat(core/network):implement pocket repository

# Conflicts:
#	core/data/src/commonMain/kotlin/org/mifos/mobile/core/data/di/RepositoryModule.kt
#	core/data/src/commonMain/kotlin/org/mifos/mobile/core/data/mapper/pocket/PocketAccountMapper.kt
#	core/data/src/commonMain/kotlin/org/mifos/mobile/core/data/repository/PocketRepository.kt
#	core/model/src/commonMain/kotlin/org/mifos/mobile/core/model/entity/pocket/PocketAccount.kt
feat(feat/pocket):implement pocket dashboard screen

feat(feature/pocket): implement pocket dashboard screen

feat(feature/pocket): implement pocket dashboard screen

# Conflicts:
#	cmp-navigation/src/commonMain/kotlin/cmp/navigation/authenticated/AuthenticatedNavigation.kt
#	feature/home/src/commonMain/composeResources/values/strings.xml
#	feature/pocket/src/commonMain/kotlin/org/mifos/mobile/feature/pocket/pocketDashboard/PocketDashboardScreen.kt
#	feature/pocket/src/commonMain/kotlin/org/mifos/mobile/feature/pocket/pocketDashboard/PocketDashboardViewModel.kt

# Conflicts:
#	cmp-navigation/src/commonMain/kotlin/cmp/navigation/authenticated/AuthenticatedNavigation.kt
#	feature/pocket/src/commonMain/kotlin/org/mifos/mobile/feature/pocket/pocketDashboard/PocketDashboardScreen.kt
#	feature/pocket/src/commonMain/kotlin/org/mifos/mobile/feature/pocket/pocketDashboard/PocketDashboardViewModel.kt
feat(feature/pocket): add pull to refresh functionality
feat(core/data): rebased onto pocket repository changes
fix(cmp-navigation): implement platform specific database module
fix(core/data): fix web application checks failing
feat(core/data): add conflict resolution in pocket repository
@Kartikey15dem
Kartikey15dem requested review from a team and Copilot August 2, 2026 15:19
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds pocket cache reset and local/server reconciliation. The dashboard clears cached data before loading, calculates totals by currency, and updates pocket presentation and native currency formatting.

Changes

Pocket cache and dashboard updates

Layer / File(s) Summary
Pocket cache reconciliation and persistence
core/data/..., core/database/...
PocketRepository now exposes resetPocketCache(). Implementations clear cached data or provide a no-op. Non-JS synchronization reconciles local and server pocket mappings, persists local state, and performs best-effort server operations. Database replacement is transactional, and dispatcher resolution is explicitly typed.
Dashboard refresh and currency totals
feature/pocket/...
The dashboard resets the pocket cache before loading. Balance totals are grouped and formatted by currency.
Pocket presentation and currency formatting
core/common/..., feature/pocket/...
The native formatter assigns currencyCode. The search hint changes to “Search in %1$s accounts”. The dashboard card uses a minimum height and can expand.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PocketDashboardViewModel
  participant PocketRepositoryImp
  participant PocketServer
  participant PocketAccountDao
  PocketDashboardViewModel->>PocketRepositoryImp: resetPocketCache()
  PocketDashboardViewModel->>PocketRepositoryImp: loadPocketData()
  PocketRepositoryImp->>PocketServer: reconcile pocket mappings
  PocketRepositoryImp->>PocketAccountDao: replaceAllPocketAccounts()
  PocketRepositoryImp-->>PocketDashboardViewModel: return local pocket accounts
Loading

Possibly related PRs

Suggested reviewers: revanthkumarj

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the pull request's main objective: resolving pocket repository conflicts and related data-layer synchronization issues.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (3)
feature/pocket/src/commonMain/kotlin/org/mifos/mobile/feature/pocket/pocketDashboard/PocketDashboardViewModel.kt (1)

43-46: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Resetting the cache on every init defeats the repository cache.

resetPocketCache() clears detailedPocketCache and cachedClientId each time the ViewModel is created. loadPocketData() then calls getDetailedPocketAccounts(clientId, forceRefresh = false), which now always misses the cache. Every navigation to the dashboard triggers syncPocketsWithServer(), a client-accounts fetch, and one request per share account.

If the goal is fresh data on entry, call loadPocketData(forceRefresh = true) instead. That keeps the cache useful for other consumers such as getAvailableAccountsToLink, which reads detailedPocketCache directly.

♻️ Proposed change
     init {
-        viewModelScope.launch {
-            pocketRepository.resetPocketCache()
-            loadPocketData()
-        }
+        loadPocketData(forceRefresh = true)
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@feature/pocket/src/commonMain/kotlin/org/mifos/mobile/feature/pocket/pocketDashboard/PocketDashboardViewModel.kt`
around lines 43 - 46, Remove the pocketRepository.resetPocketCache() call from
the ViewModel initialization flow and invoke loadPocketData with forceRefresh =
true instead. Update loadPocketData and its call to getDetailedPocketAccounts as
needed to propagate the flag, preserving detailedPocketCache for other consumers
such as getAvailableAccountsToLink.
core/data/src/nonJsCommonMain/kotlin/org/mifos/mobile/core/data/repositoryImpl/PocketRepositoryImp.kt (1)

314-320: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

syncPockets in the else branch runs a full network reload inside a delink.

When detailedPocketCache holds no success value, line 319 triggers syncPockets(clientId, forceRefresh = true). That call performs syncPocketsWithServer() plus a client-accounts fetch and per-share-account requests, before the server delink at line 326 has run. The reconciliation therefore observes the pocket as server-only and deletes it as a side effect, which duplicates the explicit delete below.

Move the server delink before the cache refresh, so the refresh observes the final state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@core/data/src/nonJsCommonMain/kotlin/org/mifos/mobile/core/data/repositoryImpl/PocketRepositoryImp.kt`
around lines 314 - 320, The delink flow refreshes the pocket cache before
applying the server-side delink, causing syncPockets to observe stale server
state and perform duplicate deletion. In the surrounding delink method, move the
server delink operation before the detailedPocketCache handling and its
syncPockets(clientId, forceRefresh = true) fallback, while preserving the
existing cache filtering behavior afterward.
core/database/src/commonMain/kotlin/org/mifos/mobile/core/database/dao/PocketAccountDao.kt (1)

28-30: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Consider adding a transactional replace helper.

deleteAll() is called immediately before linkPocketAccounts() in PocketRepositoryImp (nonJsCommonMain), in syncPocketsWithServer() and in linkAccounts(). Without a transaction, a failure between the two calls leaves the pockets table empty. A @Transaction method in the DAO makes the replace atomic for every caller.

♻️ Proposed transactional replace
     `@Query`("DELETE FROM pockets")
     suspend fun deleteAll()
+
+    `@Transaction`
+    suspend fun replaceAllPocketAccounts(pockets: List<PocketAccountEntity>) {
+        deleteAll()
+        linkPocketAccounts(pockets)
+    }
 }

Add import androidx.room.Transaction.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@core/database/src/commonMain/kotlin/org/mifos/mobile/core/database/dao/PocketAccountDao.kt`
around lines 28 - 30, Update PocketAccountDao by adding a `@Transaction` replace
helper that performs deleteAll() followed by linkPocketAccounts() atomically,
and have PocketRepositoryImp callers such as syncPocketsWithServer() and
linkAccounts() use this helper instead of invoking the two DAO methods
separately. Add the required androidx.room.Transaction import while preserving
existing linking behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@core/data/src/nonJsCommonMain/kotlin/org/mifos/mobile/core/data/repositoryImpl/PocketRepositoryImp.kt`:
- Around line 273-306: Update the linkAccounts flow in PocketRepositoryImp so
server-side rejection errors from dataManager.pocketApi.linkAccounts are
propagated to the caller instead of being swallowed by the catch block. Preserve
the existing successful synchronization and offline/transport-failure behavior,
allowing retries for transient failures while distinguishing and surfacing
validation or 4xx-style server rejections before reporting success.
- Around line 61-78: Update syncPocketsWithServer() so accountsToDelink is not
derived from server pockets absent in localBasicPockets; until explicit local
deletion intent exists, skip server delinking entirely. Preserve server pockets
missing locally by merging/inserting them into the local database, and only
reintroduce deletion after using a pending-delete flag or equivalent outbox
state.
- Around line 240-249: Update the temporary ID generation in linkPocketAccounts
so separate link operations cannot produce overlapping negative ID ranges;
define and use a companion-object ID_BLOCK_SIZE larger than the maximum batch
size when combining the epoch-millisecond value with the item index, while
preserving unique per-account IDs within each operation.
- Around line 72-76: Update every silent catch block in PocketRepositoryImp,
including those in the delink and sync operations, to rethrow
CancellationException before handling other failures. Log non-cancellation
exceptions with the repository’s existing logging mechanism instead of ignoring
them, while preserving the current recovery behavior after logging.

In
`@core/database/src/commonMain/kotlin/org/mifos/mobile/core/database/dao/PocketAccountDao.kt`:
- Around line 28-30: The pocket table replacement is non-atomic, allowing
deletion to persist if reinsertion fails or the process stops. In
core/database/src/commonMain/kotlin/org/mifos/mobile/core/database/dao/PocketAccountDao.kt:28-30,
import androidx.room.Transaction and add transactional
replaceAllPocketAccounts(pockets), calling deleteAll() followed by
linkPocketAccounts(pockets). In
core/data/src/nonJsCommonMain/kotlin/org/mifos/mobile/core/data/repositoryImpl/PocketRepositoryImp.kt:116-117,
update syncPocketsWithServer() to call replaceAllPocketAccounts with the mapped
localBasicPockets; at :257-258, update linkAccounts() to call it with the mapped
allLocalPockets.

In
`@feature/pocket/src/commonMain/kotlin/org/mifos/mobile/feature/pocket/pocketDashboard/PocketDashboardViewModel.kt`:
- Around line 175-184: Update the fallback in PocketDashboardViewModel’s
formattedTotal calculation to retain the previous totalBalance value when
CurrencyFormatter.format returns an empty string, particularly when no account
has a currency code. Preserve the zero-balance formatting for valid currency
data and ensure the resulting state never replaces a usable default or previous
value with an empty string.

---

Nitpick comments:
In
`@core/data/src/nonJsCommonMain/kotlin/org/mifos/mobile/core/data/repositoryImpl/PocketRepositoryImp.kt`:
- Around line 314-320: The delink flow refreshes the pocket cache before
applying the server-side delink, causing syncPockets to observe stale server
state and perform duplicate deletion. In the surrounding delink method, move the
server delink operation before the detailedPocketCache handling and its
syncPockets(clientId, forceRefresh = true) fallback, while preserving the
existing cache filtering behavior afterward.

In
`@core/database/src/commonMain/kotlin/org/mifos/mobile/core/database/dao/PocketAccountDao.kt`:
- Around line 28-30: Update PocketAccountDao by adding a `@Transaction` replace
helper that performs deleteAll() followed by linkPocketAccounts() atomically,
and have PocketRepositoryImp callers such as syncPocketsWithServer() and
linkAccounts() use this helper instead of invoking the two DAO methods
separately. Add the required androidx.room.Transaction import while preserving
existing linking behavior.

In
`@feature/pocket/src/commonMain/kotlin/org/mifos/mobile/feature/pocket/pocketDashboard/PocketDashboardViewModel.kt`:
- Around line 43-46: Remove the pocketRepository.resetPocketCache() call from
the ViewModel initialization flow and invoke loadPocketData with forceRefresh =
true instead. Update loadPocketData and its call to getDetailedPocketAccounts as
needed to propagate the flag, preserving detailedPocketCache for other consumers
such as getAvailableAccountsToLink.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 38c7ceb4-b059-4d00-a4e0-380c49b71151

📥 Commits

Reviewing files that changed from the base of the PR and between 04766d9 and 3a1eecc.

📒 Files selected for processing (9)
  • core/common/src/nativeMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.native.kt
  • core/data/src/commonMain/kotlin/org/mifos/mobile/core/data/repository/PocketRepository.kt
  • core/data/src/jsCommonMain/kotlin/org/mifos/mobile/core/data/repositoryImpl/PocketRepositoryImp.kt
  • core/data/src/nonJsCommonMain/kotlin/org/mifos/mobile/core/data/repositoryImpl/PocketRepositoryImp.kt
  • core/database/src/commonMain/kotlin/org/mifos/mobile/core/database/dao/PocketAccountDao.kt
  • core/database/src/desktopMain/kotlin/org/mifos/mobile/core/database/di/DatabaseModule.desktop.kt
  • core/database/src/nativeMain/kotlin/org/mifos/mobile/core/database/di/DatabaseModule.native.kt
  • feature/pocket/src/commonMain/composeResources/values/strings.xml
  • feature/pocket/src/commonMain/kotlin/org/mifos/mobile/feature/pocket/pocketDashboard/PocketDashboardViewModel.kt

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Pocket feature’s data layer to better handle local/server pocket-linking conflicts by syncing local pocket mappings to the server, resetting pocket caches on dashboard load, and improving multi-currency balance presentation.

Changes:

  • Added a local↔server reconciliation step for pocket mappings (link/delink) and updated repository caching behavior.
  • Updated pocket dashboard total balance logic to display totals per currency.
  • Adjusted Room database wiring (typed dispatcher injection) and added a DAO helper for clearing pocket mappings.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
feature/pocket/src/commonMain/kotlin/org/mifos/mobile/feature/pocket/pocketDashboard/PocketDashboardViewModel.kt Resets pocket cache on init; changes total-balance formatting to multi-currency totals.
feature/pocket/src/commonMain/composeResources/values/strings.xml Updates search hint copy.
core/database/src/nativeMain/kotlin/org/mifos/mobile/core/database/di/DatabaseModule.native.kt Uses typed CoroutineDispatcher resolution for Room query context.
core/database/src/desktopMain/kotlin/org/mifos/mobile/core/database/di/DatabaseModule.desktop.kt Uses typed CoroutineDispatcher resolution for Room query context.
core/database/src/commonMain/kotlin/org/mifos/mobile/core/database/dao/PocketAccountDao.kt Adds deleteAll() for pocket mappings (used by new sync flow).
core/data/src/nonJsCommonMain/kotlin/org/mifos/mobile/core/data/repositoryImpl/PocketRepositoryImp.kt Implements reconciliation logic, local-first updates for link/delink, and adds resetPocketCache().
core/data/src/jsCommonMain/kotlin/org/mifos/mobile/core/data/repositoryImpl/PocketRepositoryImp.kt Adds no-op resetPocketCache() for JS target.
core/data/src/commonMain/kotlin/org/mifos/mobile/core/data/repository/PocketRepository.kt Adds resetPocketCache() to repository interface.
core/common/src/nativeMain/kotlin/org/mifos/mobile/core/common/CurrencyFormatter.native.kt Updates native currency formatter configuration to set currencyCode.
Suppressed comments (1)

core/data/src/nonJsCommonMain/kotlin/org/mifos/mobile/core/data/repositoryImpl/PocketRepositoryImp.kt:258

  • Same non-atomic pattern here: deleteAll() followed by linkPocketAccounts() can leave the table empty if the insert throws. Use the transactional DAO helper (e.g., replaceAll(...)) to make the update atomic.
            pocketAccountDao.deleteAll()
            pocketAccountDao.linkPocketAccounts(allLocalPockets.map { it.toEntity() })

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants