Skip to content

feature(standingInstructions) : Implemented new module and network layer implementation for the standing instructions endpoint. - #2694

Open
techsavvy185 wants to merge 17 commits into
openMF:devfrom
techsavvy185:loanStandingInstructions
Open

feature(standingInstructions) : Implemented new module and network layer implementation for the standing instructions endpoint.#2694
techsavvy185 wants to merge 17 commits into
openMF:devfrom
techsavvy185:loanStandingInstructions

Conversation

@techsavvy185

@techsavvy185 techsavvy185 commented Jun 1, 2026

Copy link
Copy Markdown
Member

Fixes - Jira-#672

With Empty response in case no SI exist:

Screen_recording_20260610_104525.webm

WIth Mock Data:(The delete isn't working since the data is populated locally)

Screen_recording_20260620_001943.webm

Please make sure these boxes are checked before submitting your pull request - thanks!

  • Run the static analysis check ./gradlew check or ci-prepush.sh to make sure you didn't break anything

  • If you have multiple commits please combine them into one commit by squashing them.

…yer implementation for the standing instructions endpoint.
@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR adds a complete "Standing Instructions" feature enabling users to view, edit, and delete recurring payment instructions linked to client accounts. The implementation spans domain and DTO models, network service integration with Ktorfit, repository layer with mappers, MVVM state management with Compose UI, navigation routing, and seamless savings account screen integration with localized string resources.

Changes

Feature implementation layers (click to expand)

Standing Instructions Feature

Layer / File(s) Summary
Domain, entity, and DTO data models
core/model/src/.../standingInstructions/StandingInstruction.kt, core/model/src/.../standingInstructions/StandingInstructionClient.kt, core/model/src/.../standingInstructions/StandingInstructionAccount.kt, core/model/src/.../standingInstructions/StandingInstructionUpdate.kt, core/database/src/.../entities/standingInstructions/*, core/network/src/.../dto/standingInstruction/StandingInstructionDto.kt
Data models across domain, entity, and DTO layers define standing instruction structure with client/account references, amounts, validity dates, and update payload types with full serialization support.
Network service and API endpoint wiring
core/database/src/.../APIEndPoint.kt, core/network/src/.../services/StandingInstructionService.kt, core/network/src/.../BaseApiManager.kt
Adds STANDING_INSTRUCTIONS API endpoint constant; defines Ktorfit service with @GET for list retrieval and @PUT for update/delete operations with query parameters and request body; exposes service via BaseApiManager.
DataManager and network DI wiring
core/network/src/.../datamanager/DataManagerStandingInstructions.kt, core/network/src/.../di/DataMangerModule.kt
Wraps HTTP service endpoints in DataManager for convenient list, update, and delete delegation; registers DataManager as Koin singleton.
Update request and response DTOs
core/network/src/.../dto/standingInstruction/UpdateStandingInstructionDto.kt, core/network/src/.../dto/standingInstruction/StandingInstructionUpdateResponseDto.kt
Defines request DTO for update/delete payloads with configurable amount, date, locale, and format fields; response DTO with optional resource ID and field change descriptions.
Repository contract and implementation
core/data/src/.../repository/StandingInstructionsRepository.kt, core/data/src/.../repositoryImp/StandingInstructionsRepositoryImp.kt
Repository interface defines list retrieval with pagination and identity/account scoping, plus suspending update and delete operations. Implementation fetches via DataManager, maps DTOs to domain, wraps as DataState, applies network checks via withNetworkCheck, and dispatches on io thread.
DTO and entity mapper extensions
core/data/src/.../mappers/standingInstructions/StandingInstructionsDtoMapper.kt, core/data/src/.../mappers/standingInstructions/StandingInstructionEntityMapper.kt
Maps standing instruction data between network DTOs and domain models with conditional nested client/account conversions; also maps entity layer to domain and constructs update request DTOs from domain models.
ViewModel, state models, and UI actions
feature/standing-instruction/src/.../viewStandingInstructions/ViewStandingInstructionsViewModel.kt
ViewModel bootstraps from route arguments (client ID, account details, currency code), auto-loads paged data on init, maps Page to table rows with currency formatting fallbacks, manages dialog state for options/delete/edit flows, validates amount input (numeric, positive), executes repository mutations on confirmation, and emits one-time navigation events.
Compose UI: screen, dialogs, and table
feature/standing-instruction/src/.../viewStandingInstructions/ViewStandingInstructionsScreen.kt
Entry-point collects ViewModel state with lifecycle awareness and handles navigation events; content layer renders MifosScaffold with back/refresh actions and conditional DataState UI; dialogs render error/delete/edit flows with conditional date picker and amount field validation; table renders sticky header, horizontally scrollable rows with per-row dropdown menus, and empty-state message.
Navigation route and destination registration
feature/standing-instruction/src/.../viewStandingInstructions/ViewStandingInstructionsRoute.kt, feature/standing-instruction/src/.../navigation/StandingInstructionsNavigation.kt
Defines typed @Serializable ViewStandingInstructionsScreenRoute with client, account, and currency code fields; NavController extension navigateToViewStandingInstructionsScreen(...) for typed navigation; NavGraphBuilder extension to register screen destination with back-press handling via popBackStack().
Savings screen navigation and menu integration
feature/savings/src/.../navigation/SavingsNavigation.kt, feature/savings/src/.../savingsAccountSummary/SavingsAccountSummaryScreen.kt, feature/client/src/.../navigation/ClientNavigation.kt, feature/savings/src/.../composeResources/values/*
Threads onViewStandingInstructions callback through entire savings navigation graph with currency code parameter; adds dropdown menu item to savings account summary that invokes callback with client/account/currency context; client navigation registers standing instructions destination; provides localized string resources in English and Spanish.
Dependency injection and Koin module setup
core/data/src/.../di/RepositoryModule.kt, cmp-navigation/.../KoinModules.kt, feature/standing-instruction/.../di/StandingInstructionsModule.kt
Registers StandingInstructionsRepositoryImp binding in RepositoryModule; creates StandingInstructionsModule with viewModelOf binding for ViewModel; includes module in featureModules aggregate for automatic Koin discovery.
Gradle build configuration and project setup
feature/standing-instruction/build.gradle.kts, feature/standing-instruction/.gitignore, cmp-navigation/build.gradle.kts, feature/client/build.gradle.kts, feature/savings/build.gradle.kts, settings.gradle.kts, feature/standing-instruction/src/.../composeResources/values/strings.xml
Adds standing-instruction feature module to build configuration in client and savings modules; creates feature module build script with convention plugin, Kotlin serialization, Android namespace, and Compose/core dependencies; registers module in settings; provides UI string resources for table headers, section title, empty state, and edit/delete/action labels.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SavingsUI as Savings Screen
  participant NavCtrl as NavController
  participant ViewModel
  participant Repo as Repository
  participant DataMgr as DataManager
  participant Service as HTTP Service

  User->>SavingsUI: Tap "View Standing Instructions"
  SavingsUI->>NavCtrl: navigate to instructions screen
  NavCtrl->>ViewModel: init with route args
  activate ViewModel
  ViewModel->>Repo: getStandingInstructionList()
  activate Repo
  Repo->>DataMgr: retrieveListStandingInstructions()
  activate DataMgr
  DataMgr->>Service: GET standinginstructions
  activate Service
  Service-->>DataMgr: Flow of DTOs
  deactivate Service
  DataMgr-->>Repo: Flow of DTOs
  deactivate DataMgr
  Repo->>Repo: map to domain
  Repo-->>ViewModel: DataState.Success
  deactivate Repo
  ViewModel->>ViewModel: build table data
  ViewModel-->>SavingsUI: emit state
  deactivate ViewModel
  SavingsUI->>SavingsUI: render table

  User->>SavingsUI: click Edit row
  SavingsUI->>ViewModel: OnEditClick action
  activate ViewModel
  ViewModel->>ViewModel: show edit dialog
  ViewModel-->>SavingsUI: emit state
  deactivate ViewModel

  User->>SavingsUI: confirm edit
  SavingsUI->>ViewModel: OnConfirmEdit action
  activate ViewModel
  ViewModel->>ViewModel: set loading
  ViewModel->>Repo: updateStandingInstruction()
  activate Repo
  Repo->>DataMgr: updateStandingInstruction()
  activate DataMgr
  DataMgr->>Service: PUT standinginstructions
  activate Service
  Service-->>DataMgr: response
  deactivate Service
  DataMgr-->>Repo: response
  deactivate DataMgr
  Repo-->>ViewModel: DataState.Success
  deactivate Repo
  ViewModel->>ViewModel: reload list
  ViewModel-->>SavingsUI: emit state
  deactivate ViewModel
  SavingsUI->>SavingsUI: refresh table
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Jira Link And Before/After Sections ❌ Error PR description has valid Jira link but lacks required "Before:" and "After:" sections with media. Only generic video labels and no before-state shown. Add "Before:" section with a screenshot/video showing the state before changes, and restructure existing videos under an explicit "After:" section header.
Docstring Coverage ⚠️ Warning Docstring coverage is 7.32% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title includes specific module scope, describes the main change (new module and network layer for standing instructions), and follows the commit message format with type and scope.
Description check ✅ Passed The PR description is related to the changeset, referencing Jira ticket MIFOSAC-672, including screen recordings demonstrating the feature, and mentioning verification of static analysis checks.
✨ 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.

@techsavvy185
techsavvy185 marked this pull request as ready for review June 10, 2026 05:35
@techsavvy185
techsavvy185 requested a review from a team June 10, 2026 05:35

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 5

🧹 Nitpick comments (1)
feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsScreen.kt (1)

206-209: ⚡ Quick win

MifosEmptyCard msg and title are set to the same string resource.

Both parameters use Res.string.feature_standing_instructions_no_standing_instructions_found_message. Typically, a title and message should convey different information (e.g., title: "No Standing Instructions", message: "No standing instructions found for this account"). Consider using separate string resources or omitting one parameter.

🤖 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/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsScreen.kt`
around lines 206 - 209, MifosEmptyCard is being passed the same string for both
msg and title; update the call in ViewStandingInstructionsScreen to use distinct
text for title and message by replacing the title argument with an appropriate
title resource (e.g.,
Res.string.feature_standing_instructions_no_standing_instructions_found_title)
via stringResource(...) or remove the title parameter if a title is not
required; ensure the change targets the MifosEmptyCard(...) invocation so msg
and title convey different information.
🤖 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/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstruction.kt`:
- Around line 14-15: The data classes StandingInstruction,
StandingInstructionAccount, and StandingInstructionClient are annotated with
`@Serializable` which couples the core model to networking; remove the
`@Serializable` annotations from these classes so they remain pure domain models,
and if serialization is needed for DTOs or network layers create separate
serializable DTOs or mapper functions to convert between
StandingInstruction/StandingInstructionAccount/StandingInstructionClient and
their serializable counterparts; update any call sites that relied on direct
serialization to use the new DTOs or mappers.

In
`@core/network/src/commonMain/kotlin/com/mifos/core/network/services/StandingInstructionService.kt`:
- Around line 19-40: Update the KDoc for
StandingInstructionService.getStandingInstructions to document all parameters
and the return type: add descriptions for clientId, clientName, fromAccountId,
fromAccountType, locale, dateFormat, limit, and offset, and note the returned
Flow<Page<StandingInstructionDto>> and possible responses (200 OK); ensure each
`@param` tag matches the function parameter names exactly and briefly describes
purpose and type, and keep existing general description of the method.

In
`@feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountSummary/SavingsAccountSummaryScreen.kt`:
- Around line 144-154: The onViewStandingInstructions lambda currently falls
back to 0L for a null savingsAccountWithAssociations.clientId and passes a magic
number 1 for account type; update the handler in SavingsAccountSummaryScreen so
that when uiState is SavingsAccountSummaryUiState.ShowSavingAccount you
explicitly guard against a null clientId (e.g., skip navigation or show an
error/placeholder) instead of using 0L, and replace the hardcoded 1 with a named
constant or enum (e.g., ACCOUNT_TYPE_SAVINGS or AccountType.SAVINGS) when
calling onViewStandingInstructions with accountId.toLong().

In
`@feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt`:
- Line 122: The fallback string for validity is inconsistent: replace the
trailing-space variant "-- " with the standard "--" wherever validity is
assigned (in ViewStandingInstructionsViewModel, specifically the mapping that
sets validity = instruction.validFrom ?: "-- "), so it matches the other fields
(lines using "--") and avoids uneven column alignment or visual artifacts.
- Around line 75-110: The collect block in repository.getStandingInstructionList
currently calls mutableStateFlow.update directly for DataState.Error, Loading,
and Success; instead create internal actions (e.g.,
StandingInstructionsLoadError(dataState), StandingInstructionsLoading,
StandingInstructionsLoadSuccess(dataState)) and from the collect { } call only
invoke sendAction(...) with the appropriate action (use
mapToTableData(dataState.data) inside the action payload if needed), then
implement handling of these actions inside handleAction(...) to perform all
mutableStateFlow.update mutations (setting dataState, tableData, etc.) so that
repository results flow: repository → sendAction(...) → handleAction(...) →
mutableStateFlow.update.

---

Nitpick comments:
In
`@feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsScreen.kt`:
- Around line 206-209: MifosEmptyCard is being passed the same string for both
msg and title; update the call in ViewStandingInstructionsScreen to use distinct
text for title and message by replacing the title argument with an appropriate
title resource (e.g.,
Res.string.feature_standing_instructions_no_standing_instructions_found_title)
via stringResource(...) or remove the title parameter if a title is not
required; ensure the change targets the MifosEmptyCard(...) invocation so msg
and title convey different information.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 36011c5a-dd7b-40b2-80ad-fd0908fe30c1

📥 Commits

Reviewing files that changed from the base of the PR and between ca68ef2 and b618637.

📒 Files selected for processing (33)
  • cmp-navigation/build.gradle.kts
  • cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/mappers/standingInstructions/StandingInstructionEntityMapper.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/mappers/standingInstructions/StandingInstructionsDtoMapper.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repository/StandingInstructionsRepository.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/StandingInstructionsRepositoryImp.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/basemodel/APIEndPoint.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionAccountEntity.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionClientEntity.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionEntity.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstruction.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstructionAccount.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstructionClient.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/BaseApiManager.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerStandingInstructions.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/StandingInstructionDto.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/services/StandingInstructionService.kt
  • feature/client/build.gradle.kts
  • feature/client/src/commonMain/kotlin/com/mifos/feature/client/navigation/ClientNavigation.kt
  • feature/savings/build.gradle.kts
  • feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/navigation/SavingsNavigation.kt
  • feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountSummary/SavingsAccountSummaryScreen.kt
  • feature/standing-instruction/.gitignore
  • feature/standing-instruction/build.gradle.kts
  • feature/standing-instruction/src/commonMain/composeResources/values/strings.xml
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/di/StandingInstructionsModule.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/navigation/StandingInstructionsNavigation.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsRoute.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsScreen.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt
  • settings.gradle.kts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: PR Checks / Static Analysis Check
🧰 Additional context used
📓 Path-based instructions (11)
**/composeResources/values*/strings.xml

⚙️ CodeRabbit configuration file

**/composeResources/values*/strings.xml: String resource conventions:

Naming:

  • All keys must follow:
    feature_{feature_name}_{ui_text_in_snake_case}

Examples:
"In Advance" → feature_loan_in_advance
"Outstanding" → feature_loan_outstanding

  • Avoid generic names like: title, text1, label
  • The suffix should be a short, readable representation of the UI text
  • Avoid multiple keys representing the same UI text
  • Keys must be lowercase and use snake_case

Flag:

  • Incorrect naming pattern
  • Generic or unclear key names
  • Duplicate keys for same UI text

Files:

  • feature/standing-instruction/src/commonMain/composeResources/values/strings.xml
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: Additional Code Review Guidelines:

  1. Null Safety & Stability
  • Avoid using !! operator
  • Handle null cases explicitly using safe calls or proper state handling
  • Do not assume values are always non-null without guarantees
  1. Architecture Boundaries
  • ViewModel must not depend on specific network/library implementations
  • Ensure proper separation between data, domain, and presentation layers
  • Do not format data (currency, dates, calculations) inside the UI layer
  • All formatting must be handled in the ViewModel and exposed via state (e.g., StateFlow)
  1. Performance Considerations
  • Avoid unnecessary recompositions in Compose
  • Do not attach heavy logic to frequently changing states (e.g., scrollState)
  • Prefer lifting state up instead of recomputing in child composables
  1. Compose & Navigation Best Practices
  • NEVER trigger navigation functions or side-effects directly during composition
  • Always wrap navigation calls inside LaunchedEffect or EventsEffect to avoid repeated execution on recomposition
  • Avoid triggering intensive side-effects during recomposition
  • Navigation routes must be type-safe.
  • Ensure all route classes or objects used for navigation are annotated with @Serializable.
  1. UI Structure
  • Dialogs must be separated into their own composables
  • Do not embed dialogs inline within complex main screens
  1. Localization Consistency
  • Ensure all supported languages are updated consistently across modules
  • Verify translations exist for newly added UI strings
  1. Code Cleanliness
  • Avoid unnecessary inline comments unless critical
  • Remove leftover debug or commented code
  1. Focus on correctness, readability, and maintainability over cosmetic nitpicks.
  • Avoid reviewing README, config, or asset files.
  • Prioritize identifying bugs, performance issues, and architectural concerns.
  1. Naming & Intent Rules:

Files:

  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/di/StandingInstructionsModule.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/navigation/StandingInstructionsNavigation.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/StandingInstructionDto.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionClientEntity.kt
  • cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionEntity.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstructionClient.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionAccountEntity.kt
  • feature/client/src/commonMain/kotlin/com/mifos/feature/client/navigation/ClientNavigation.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/StandingInstructionsRepositoryImp.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/basemodel/APIEndPoint.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstruction.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsRoute.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerStandingInstructions.kt
  • feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountSummary/SavingsAccountSummaryScreen.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/mappers/standingInstructions/StandingInstructionEntityMapper.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repository/StandingInstructionsRepository.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/mappers/standingInstructions/StandingInstructionsDtoMapper.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstructionAccount.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/BaseApiManager.kt
  • feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/navigation/SavingsNavigation.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/services/StandingInstructionService.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsScreen.kt
**/network/**/*.kt

⚙️ CodeRabbit configuration file

**/network/**/*.kt: Network Layer API Guidelines:

  • One-shot API calls MUST use suspend, return raw response T, and MUST NOT use Flow or DataState.
  • Streaming APIs MUST return Flow<T> and MUST NOT use suspend or DataState.

Files:

  • core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/StandingInstructionDto.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerStandingInstructions.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/BaseApiManager.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/services/StandingInstructionService.kt
**/{core-base,core}/**/*.kt

⚙️ CodeRabbit configuration file

**/{core-base,core}/**/*.kt: Critical Module Change Detection:

  • Changes in core-base module must be treated as high-impact.
  • Flag any PR that modifies files inside core-base for careful review.
  • Verify that modifications in core-base are necessary and minimal.
    Output:
  • Clearly highlight that core-base is a shared foundational module and requires extra review attention.

Files:

  • core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/StandingInstructionDto.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionClientEntity.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionEntity.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstructionClient.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionAccountEntity.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/StandingInstructionsRepositoryImp.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/basemodel/APIEndPoint.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstruction.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerStandingInstructions.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/mappers/standingInstructions/StandingInstructionEntityMapper.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repository/StandingInstructionsRepository.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/mappers/standingInstructions/StandingInstructionsDtoMapper.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstructionAccount.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/BaseApiManager.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/services/StandingInstructionService.kt
**/network/dto/**/*.kt

⚙️ CodeRabbit configuration file

**/network/dto/**/*.kt: DTO Rules (:core:network:dto):

  • Classes here are Data Transfer Objects (DTOs) used strictly for API communication.
  • Rule: DTOs MUST NOT be used in ViewModels, Screens, or the Domain layer.
  • Flag: If a DTO is returned directly by a Repository interface or used in UI state.

Files:

  • core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/StandingInstructionDto.kt
**/core/model/**/*.kt

⚙️ CodeRabbit configuration file

**/core/model/**/*.kt: Domain Model Rules (:core:model):

  • Classes defined in this module are strictly Domain Models.
  • They must be plain Kotlin data classes.
  • They MUST NOT contain network-specific annotations (e.g., @Serializable) or database-specific annotations (e.g., @Entity).
  • Rule: These are the ONLY models that should be passed to or used within ViewModels and UI Screens.
  • Flag: If a Domain model is used directly as an API request/response body or a Room database table.

Files:

  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstructionClient.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstruction.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstructionAccount.kt
**/data/repositoryImp/**/*.kt

⚙️ CodeRabbit configuration file

**/data/repositoryImp/**/*.kt: Repository Implementation Guidelines:

  • MUST convert T -> DataState<T> for one-shot APIs, and Flow<T> -> Flow<DataState<T>> using .asDataStateFlow().
  • MUST perform network availability checks, handle exceptions, and apply appropriate dispatchers.
  • Repositories act as the boundary translator. They MUST use mappers to convert DTOs (from network) and Entities (from database) into Domain Models (from :core:model) before returning data.
  • Flag: If a repository implementation returns a raw DTO or Entity directly to the caller instead of a Domain model.

Files:

  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/StandingInstructionsRepositoryImp.kt
**/*Screen.kt

⚙️ CodeRabbit configuration file

**/*Screen.kt: Screen architecture rules:

Each screen must follow a 2-layer structure:

  1. Layer 1 (Entry/Route Composable):

    • Function Name: Typically *ScreenRoute or the entry-point composable.
    • Responsibilities: Inject ViewModel (e.g., koinViewModel), take navigation lambdas, collect the StateFlow, and handle ViewModel events.
    • Logic: Should only handle ViewModel interaction, state collection, and triggering navigation in response to ViewModel events.
  2. Layer 2 (Stateful/Content Composable — *Screen or *ScreenContent):

    • Parameters: MUST only take state (the UI state object) and a single onAction lambda (e.g., onAction: (FeatureAction) -> Unit).
    • Responsibilities: Render the UI based strictly on the provided state.
    • Rule: MUST NOT pass multiple separate lambda functions for different UI interactions; consolidate them into the single onAction.
    • Constraint: Must NOT contain any business logic or ViewModel/Navigation references.

Internal/private helper composables (e.g., dialogs, sections, sub-components):

  • These are NOT subject to the single onAction rule.
  • They may accept specific, focused lambdas (e.g., onRetry: () -> Unit) or a narrowed onAction as appropriate.
  • However, they must NOT be passed the ViewModel or navigation controllers directly.

UI consistency:

  • Avoid hardcoded values (dp, sp, padding, fontSize, colors)
  • Use DesignToken, KptTheme, AppColors, MifosTypography for spacing, typography, and colors

Code quality:

  • Keep Composables small and readable
  • Avoid deeply nested UI

Flag:

  • Missing Layer 2 (*Screen/*ScreenContent) separation from the entry-point composable
  • Layer 2 (*Screen/*ScreenContent) receiving multiple separate lambdas instead of a single onAction
  • UI logic inside the entry-point composable
  • Business logic inside any Composable
  • Hardcoded strings instead of using string resources
  • Hardcoded dp/sp values
  • Direct styling instead of using DesignToken or KptTheme

Files:

  • feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountSummary/SavingsAccountSummaryScreen.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsScreen.kt
**/data/mappers/**/*.kt

⚙️ CodeRabbit configuration file

**/data/mappers/**/*.kt: Mapper Consistency Rules (:core:data:mappers):

  • All mapping logic MUST be centralized in this directory.
  • Allowed mappings:
    1. DTO -> Domain Model
    2. Entity <-> Domain Model
  • Flag: Any mapper functions found in any module outside of :core:data:mappers.

Files:

  • core/data/src/commonMain/kotlin/com/mifos/core/data/mappers/standingInstructions/StandingInstructionEntityMapper.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/mappers/standingInstructions/StandingInstructionsDtoMapper.kt
**/data/repository/**/*.kt

⚙️ CodeRabbit configuration file

**/data/repository/**/*.kt: Repository Interface Guidelines:

  • One-shot operations MUST use suspend fun and return DataState<T>. MUST NOT use Flow.
  • Streaming operations MUST return Flow<DataState<T>> and MUST NOT use suspend.
  • Repository interfaces must consistently return Domain models (from :core:model) wrapped in DataState or Flow.
  • Flag: Any interface method that returns a DTO or an Entity.

Files:

  • core/data/src/commonMain/kotlin/com/mifos/core/data/repository/StandingInstructionsRepository.kt
**/*ViewModel.kt

⚙️ CodeRabbit configuration file

**/*ViewModel.kt: MVI architecture rules:

  • All new features must follow MVI
  • ViewModel must extend BaseViewModel
  • The ViewModel MUST maintain a single UI state (e.g., a single StateFlow)
    instead of multiple separate state variables.

Required:

  • Use *State, *Event, *Action
  • Naming must be consistent (FeatureViewModel, FeatureState, FeatureEvent, FeatureAction)
  • Follow unidirectional flow:
    Action → ViewModel → State → UI

Internal reducer/action architecture rules:

  • Async operations MUST NOT directly mutate UI state repeatedly inside
    Flow collectors, suspend callbacks, or repository result handlers.

  • Repository/network/database results MUST be converted into internal
    actions using sendAction(...).

  • handleAction(...) must act as the primary reducer responsible for:

    • state mutation
    • reducer-style state transitions
    • triggering follow-up actions
  • Large async methods must be split into:

    1. async collection layer
    2. internal action dispatching
    3. reducer/state handling
  • Avoid directly calling another business/data-loading method from
    repository collectors or async callbacks.
    Prefer dispatching follow-up internal actions instead.

Preferred pattern:
repository result
-> sendAction(...)
-> handleAction(...)
-> mutableStateFlow.update { ... }

Anti-pattern examples:
mutableStateFlow.update { ... } inside collect { }
fetchX() -> directly calls fetchY() inside async collector
large methods mixing:
- collection
- state mutation
- navigation
- business chaining

Flag:

  • Multiple mutableStateFlow.update {} calls inside collect { }
  • Direct state mutation inside async repository callbacks
  • Async methods performing both collection and reducer logic
  • Direct business-flow chaining from async collectors
  • Missing internal reducer actions for async results
  • Missing *State / *Event / *Action
  • ViewModel not...

Files:

  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt
🧠 Learnings (4)
📚 Learning: 2026-02-06T13:15:16.968Z
Learnt from: sahilshivekar
Repo: openMF/android-client PR: 2603
File: feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsViewModel.kt:43-106
Timestamp: 2026-02-06T13:15:16.968Z
Learning: Guideline: When a Kotlin function parameter is nullable (e.g., balance: Double?, currencyCode: String?, maximumFractionDigits: Int?) and downstream calls require a non-null value, add null-safety handling in all implementations. Specifically, avoid calling Currency.getInstance(currencyCode) with a possibly null currencyCode; provide a safe default (e.g., currencyCode ?: "$") or validate before use. Ensure all platform targets (Android/Desktop/Native) follow consistent null handling, and consider centralizing currencyCode normalization in the common layer if feasible. Add tests covering null currencyCode to prevent NPEs.

Applied to files:

  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/di/StandingInstructionsModule.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/navigation/StandingInstructionsNavigation.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/StandingInstructionDto.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionClientEntity.kt
  • cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionEntity.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstructionClient.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionAccountEntity.kt
  • feature/client/src/commonMain/kotlin/com/mifos/feature/client/navigation/ClientNavigation.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/StandingInstructionsRepositoryImp.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/basemodel/APIEndPoint.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstruction.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsRoute.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerStandingInstructions.kt
  • feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountSummary/SavingsAccountSummaryScreen.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/mappers/standingInstructions/StandingInstructionEntityMapper.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repository/StandingInstructionsRepository.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/mappers/standingInstructions/StandingInstructionsDtoMapper.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstructionAccount.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/BaseApiManager.kt
  • feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/navigation/SavingsNavigation.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/services/StandingInstructionService.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsScreen.kt
📚 Learning: 2026-04-01T05:03:14.323Z
Learnt from: kartikey004
Repo: openMF/mifos-x-field-officer-app PR: 2659
File: feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanDisbursement/LoanAccountDisbursementScreen.kt:190-195
Timestamp: 2026-04-01T05:03:14.323Z
Learning: In this repo, existing `SelectableDates` / `SelectableDates.isSelectableDate(utcTimeMillis: Long)` implementations use `Clock.System.now().toEpochMilliseconds()` (UTC epoch millis) for date-boundary checks. During PR reviews, do not flag these checks for not using a timezone-aware `LocalDate` start-of-day approach; treat it as an established project-wide pattern. If a change is desired, handle it as a coordinated project-wide improvement rather than as a per-PR review issue.

Applied to files:

  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/di/StandingInstructionsModule.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/navigation/StandingInstructionsNavigation.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/StandingInstructionDto.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionClientEntity.kt
  • cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionEntity.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstructionClient.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionAccountEntity.kt
  • feature/client/src/commonMain/kotlin/com/mifos/feature/client/navigation/ClientNavigation.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/StandingInstructionsRepositoryImp.kt
  • core/database/src/commonMain/kotlin/com/mifos/room/basemodel/APIEndPoint.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstruction.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsRoute.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerStandingInstructions.kt
  • feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountSummary/SavingsAccountSummaryScreen.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/mappers/standingInstructions/StandingInstructionEntityMapper.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repository/StandingInstructionsRepository.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/mappers/standingInstructions/StandingInstructionsDtoMapper.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstructionAccount.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/BaseApiManager.kt
  • feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/navigation/SavingsNavigation.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/services/StandingInstructionService.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsScreen.kt
📚 Learning: 2026-02-16T08:37:28.351Z
Learnt from: kartikey004
Repo: openMF/android-client PR: 2610
File: feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientIdentifiersList/ClientIdentifiersListScreen.kt:60-60
Timestamp: 2026-02-16T08:37:28.351Z
Learning: In Kotlin files under the android-client module, replace MaterialTheme references with the established KptTheme import: import template.core.base.designsystem.theme.KptTheme. This should be applied consistently across files where KptTheme is used, replacing any MaterialTheme imports with the correct KptTheme import path.

Applied to files:

  • feature/client/src/commonMain/kotlin/com/mifos/feature/client/navigation/ClientNavigation.kt
📚 Learning: 2026-02-13T08:12:37.519Z
Learnt from: amanna13
Repo: openMF/android-client PR: 2604
File: feature/groups/build.gradle.kts:42-42
Timestamp: 2026-02-13T08:12:37.519Z
Learning: In Android projects using Jetpack Compose icons, include androidx.compose.material:material-icons-core whenever you import androidx.compose.material.icons.* (Icons.Default, Icons.Filled, Icons.Outlined, Icons.Rounded). Only add androidx.compose.material:material-icons-extended if you actually use extended icons, since it increases artifact size (~36MB) and brings in material-icons-core transitively. Apply this guideline to all Gradle Kotlin scripts (*.gradle.kts) across modules to ensure dependency scope is minimized.

Applied to files:

  • feature/standing-instruction/build.gradle.kts
  • feature/savings/build.gradle.kts
  • cmp-navigation/build.gradle.kts
  • feature/client/build.gradle.kts
  • settings.gradle.kts
🔇 Additional comments (31)
core/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.kt (1)

72-73: LGTM!

Also applies to: 143-144, 214-216

cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt (1)

41-42: LGTM!

Also applies to: 98-99

feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/di/StandingInstructionsModule.kt (1)

12-18: LGTM!

cmp-navigation/build.gradle.kts (1)

54-55: LGTM!

feature/client/build.gradle.kts (1)

34-35: LGTM!

feature/savings/build.gradle.kts (1)

28-29: LGTM!

feature/standing-instruction/build.gradle.kts (1)

10-13: LGTM!

Also applies to: 15-17, 19-36

feature/standing-instruction/.gitignore (1)

1-1: LGTM!

feature/standing-instruction/src/commonMain/composeResources/values/strings.xml (1)

12-20: LGTM!

feature/client/src/commonMain/kotlin/com/mifos/feature/client/navigation/ClientNavigation.kt (1)

114-115: LGTM!

Also applies to: 406-411

feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt (1)

40-44: LGTM!

Also applies to: 46-62, 64-68

feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsScreen.kt (3)

66-89: LGTM!

Also applies to: 92-136


139-155: LGTM!


216-251: LGTM!

Also applies to: 254-287

feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsRoute.kt (1)

18-50: LGTM!

feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/navigation/StandingInstructionsNavigation.kt (1)

16-24: LGTM!

feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/navigation/SavingsNavigation.kt (1)

34-34: LGTM!

Also applies to: 69-69, 113-113, 129-129

feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountSummary/SavingsAccountSummaryScreen.kt (2)

164-164: LGTM!

Also applies to: 205-211


700-700: LGTM!

core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionEntity.kt (1)

18-43: LGTM!

core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionClientEntity.kt (1)

16-21: LGTM!

core/database/src/commonMain/kotlin/com/mifos/room/entities/standingInstructions/StandingInstructionAccountEntity.kt (1)

16-22: LGTM!

core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/StandingInstructionDto.kt (1)

14-73: LGTM!

core/data/src/commonMain/kotlin/com/mifos/core/data/mappers/standingInstructions/StandingInstructionsDtoMapper.kt (1)

19-44: LGTM!

core/database/src/commonMain/kotlin/com/mifos/room/basemodel/APIEndPoint.kt (1)

48-48: LGTM!

core/network/src/commonMain/kotlin/com/mifos/core/network/BaseApiManager.kt (1)

47-47: LGTM!

Also applies to: 68-68, 98-98

core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerStandingInstructions.kt (1)

17-41: LGTM!

core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt (1)

34-34: LGTM!

Also applies to: 61-61

core/data/src/commonMain/kotlin/com/mifos/core/data/repository/StandingInstructionsRepository.kt (1)

17-26: LGTM!

core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/StandingInstructionsRepositoryImp.kt (1)

26-59: LGTM!

core/data/src/commonMain/kotlin/com/mifos/core/data/mappers/standingInstructions/StandingInstructionEntityMapper.kt (1)

20-63: LGTM!

@sahilshivekar

Copy link
Copy Markdown
Contributor

@techsavvy185 Why row level actions are not implemented ?

@techsavvy185

Copy link
Copy Markdown
Member Author

@sahilshivekar Row level actions are not implemented since there is no implementation for edit or delete for standing instructions in the app as of yet, and the jira ticket only specified creating the View SI screen as a requirement.

@sahilshivekar

Copy link
Copy Markdown
Contributor

@sahilshivekar Row level actions are not implemented since there is no implementation for edit or delete for standing instructions in the app as of yet, and the jira ticket only specified creating the View SI screen as a requirement.

@techsavvy185 I see it's mentioned in the ticket to implement row level actions. Also there is a API on swagger UI for it.
image
image

@techsavvy185

Copy link
Copy Markdown
Member Author

@sahilshivekar Oh, I missed that one. I will implement those and push the commits asap.

@biplab1

biplab1 commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

@techsavvy185 Please address the CodeRabbitAI suggestions relevant to your PR.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
core/network/src/commonMain/kotlin/com/mifos/core/network/services/StandingInstructionService.kt (1)

38-48: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Use a one-shot network contract for this endpoint.

getStandingInstructions is modeled as Flow, but this endpoint is a paged request/response call and should be a one-shot suspend API returning raw Page<StandingInstructionDto>. Keeping it as Flow forces downstream repository contracts into streaming shapes unnecessarily.

As per coding guidelines: in **/network/**/*.kt, one-shot API calls must use suspend and must not use Flow.

🤖 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/network/src/commonMain/kotlin/com/mifos/core/network/services/StandingInstructionService.kt`
around lines 38 - 48, The getStandingInstructions method in
StandingInstructionService is currently returning
Flow<Page<StandingInstructionDto>> as a one-shot paged endpoint, but per the
network module coding guidelines, one-shot API calls must use suspend functions
and not Flow. Change the return type from Flow<Page<StandingInstructionDto>> to
Page<StandingInstructionDto> and add the suspend keyword before the function
signature in the getStandingInstructions method declaration.

Source: Coding guidelines

🧹 Nitpick comments (1)
feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsScreen.kt (1)

279-283: ⚡ Quick win

Scope table inputs to reduce recompositions in ViewStandingInstructionsScreen.kt.

ViewStandingInstructionsData takes the whole dialogState (Line 281), so edits in the dialog can recompose the entire table. Pass only a minimal value (e.g., selected options row id) needed for dropdown visibility.

As per coding guidelines: “Avoid unnecessary recompositions in Compose.”

Also applies to: 324-332

🤖 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/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsScreen.kt`
around lines 279 - 283, The ViewStandingInstructionsData composable function
accepts the entire dialogState parameter, which causes the table to recompose
unnecessarily whenever any part of the dialog state changes. Replace the
dialogState parameter with a more granular parameter that only includes the
minimal information needed for the dropdown visibility (such as a selected row
id). Update the function signature to remove the dialogState parameter and add
the new minimal parameter, then update all call sites of
ViewStandingInstructionsData (including the one around line 324-332) to pass
only the required minimal value instead of the entire dialogState object.

Source: Coding guidelines

🤖 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
`@feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt`:
- Around line 76-83: The OnEditClick action handler in
ViewStandingInstructionsViewModel is initializing edit fields with display
placeholder values (such as "-- " for validity) instead of actual data values.
These placeholders can then flow into the updateStandingInstruction API call,
sending invalid data to the backend. To fix this, separate the display values
from the actual data values: use real data fields (not display placeholders like
"-- ") when initializing the Edit dialog state in the OnEditClick handler, and
add validation in the updateStandingInstruction method to reject or sanitize
placeholder values before sending them to the API.

---

Outside diff comments:
In
`@core/network/src/commonMain/kotlin/com/mifos/core/network/services/StandingInstructionService.kt`:
- Around line 38-48: The getStandingInstructions method in
StandingInstructionService is currently returning
Flow<Page<StandingInstructionDto>> as a one-shot paged endpoint, but per the
network module coding guidelines, one-shot API calls must use suspend functions
and not Flow. Change the return type from Flow<Page<StandingInstructionDto>> to
Page<StandingInstructionDto> and add the suspend keyword before the function
signature in the getStandingInstructions method declaration.

---

Nitpick comments:
In
`@feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsScreen.kt`:
- Around line 279-283: The ViewStandingInstructionsData composable function
accepts the entire dialogState parameter, which causes the table to recompose
unnecessarily whenever any part of the dialog state changes. Replace the
dialogState parameter with a more granular parameter that only includes the
minimal information needed for the dropdown visibility (such as a selected row
id). Update the function signature to remove the dialogState parameter and add
the new minimal parameter, then update all call sites of
ViewStandingInstructionsData (including the one around line 324-332) to pass
only the required minimal value instead of the entire dialogState object.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 52a86d06-7756-4e1c-9ab7-4607ad8b1561

📥 Commits

Reviewing files that changed from the base of the PR and between 16d1ed9 and 77fd653.

📒 Files selected for processing (13)
  • cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/mappers/standingInstructions/StandingInstructionsDtoMapper.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repository/StandingInstructionsRepository.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/StandingInstructionsRepositoryImp.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstructionUpdate.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerStandingInstructions.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/StandingInstructionUpdateResponseDto.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/UpdateStandingInstructionDto.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/services/StandingInstructionService.kt
  • feature/standing-instruction/src/commonMain/composeResources/values/strings.xml
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/di/StandingInstructionsModule.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsScreen.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt
✅ Files skipped from review due to trivial changes (3)
  • feature/standing-instruction/src/commonMain/composeResources/values/strings.xml
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/standingInstructions/StandingInstructionUpdate.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/StandingInstructionUpdateResponseDto.kt
🚧 Files skipped from review as they are similar to previous changes (3)
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/di/StandingInstructionsModule.kt
  • cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/mappers/standingInstructions/StandingInstructionsDtoMapper.kt
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: PR Checks / Static Analysis Check
🧰 Additional context used
📓 Path-based instructions (8)
**/network/**/*.kt

⚙️ CodeRabbit configuration file

**/network/**/*.kt: Network Layer API Guidelines:

  • One-shot API calls MUST use suspend, return raw response T, and MUST NOT use Flow or DataState.
  • Streaming APIs MUST return Flow<T> and MUST NOT use suspend or DataState.

Files:

  • core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/UpdateStandingInstructionDto.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/services/StandingInstructionService.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerStandingInstructions.kt
**/network/dto/**/*.kt

⚙️ CodeRabbit configuration file

**/network/dto/**/*.kt: DTO Rules (:core:network:dto):

  • Classes here are Data Transfer Objects (DTOs) used strictly for API communication.
  • Rule: DTOs MUST NOT be used in ViewModels, Screens, or the Domain layer.
  • Flag: If a DTO is returned directly by a Repository interface or used in UI state.

Files:

  • core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/UpdateStandingInstructionDto.kt
**/{core-base,core}/**/*.kt

⚙️ CodeRabbit configuration file

**/{core-base,core}/**/*.kt: Critical Module Change Detection:

  • Changes in core-base module must be treated as high-impact.
  • Flag any PR that modifies files inside core-base for careful review.
  • Verify that modifications in core-base are necessary and minimal.
    Output:
  • Clearly highlight that core-base is a shared foundational module and requires extra review attention.

Files:

  • core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/UpdateStandingInstructionDto.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repository/StandingInstructionsRepository.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/services/StandingInstructionService.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerStandingInstructions.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/StandingInstructionsRepositoryImp.kt
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: Additional Code Review Guidelines:

  1. Null Safety & Stability
  • Avoid using !! operator
  • Handle null cases explicitly using safe calls or proper state handling
  • Do not assume values are always non-null without guarantees
  1. Architecture Boundaries
  • ViewModel must not depend on specific network/library implementations
  • Ensure proper separation between data, domain, and presentation layers
  • Do not format data (currency, dates, calculations) inside the UI layer
  • All formatting must be handled in the ViewModel and exposed via state (e.g., StateFlow)
  1. Performance Considerations
  • Avoid unnecessary recompositions in Compose
  • Do not attach heavy logic to frequently changing states (e.g., scrollState)
  • Prefer lifting state up instead of recomputing in child composables
  1. Compose & Navigation Best Practices
  • NEVER trigger navigation functions or side-effects directly during composition
  • Always wrap navigation calls inside LaunchedEffect or EventsEffect to avoid repeated execution on recomposition
  • Avoid triggering intensive side-effects during recomposition
  • Navigation routes must be type-safe.
  • Ensure all route classes or objects used for navigation are annotated with @Serializable.
  1. UI Structure
  • Dialogs must be separated into their own composables
  • Do not embed dialogs inline within complex main screens
  1. Localization Consistency
  • Ensure all supported languages are updated consistently across modules
  • Verify translations exist for newly added UI strings
  1. Code Cleanliness
  • Avoid unnecessary inline comments unless critical
  • Remove leftover debug or commented code
  1. Focus on correctness, readability, and maintainability over cosmetic nitpicks.
  • Avoid reviewing README, config, or asset files.
  • Prioritize identifying bugs, performance issues, and architectural concerns.
  1. Naming & Intent Rules:

Files:

  • core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/UpdateStandingInstructionDto.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repository/StandingInstructionsRepository.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/services/StandingInstructionService.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerStandingInstructions.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/StandingInstructionsRepositoryImp.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsScreen.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt
**/data/repository/**/*.kt

⚙️ CodeRabbit configuration file

**/data/repository/**/*.kt: Repository Interface Guidelines:

  • One-shot operations MUST use suspend fun and return DataState<T>. MUST NOT use Flow.
  • Streaming operations MUST return Flow<DataState<T>> and MUST NOT use suspend.
  • Repository interfaces must consistently return Domain models (from :core:model) wrapped in DataState or Flow.
  • Flag: Any interface method that returns a DTO or an Entity.

Files:

  • core/data/src/commonMain/kotlin/com/mifos/core/data/repository/StandingInstructionsRepository.kt
**/data/repositoryImp/**/*.kt

⚙️ CodeRabbit configuration file

**/data/repositoryImp/**/*.kt: Repository Implementation Guidelines:

  • MUST convert T -> DataState<T> for one-shot APIs, and Flow<T> -> Flow<DataState<T>> using .asDataStateFlow().
  • MUST perform network availability checks, handle exceptions, and apply appropriate dispatchers.
  • Repositories act as the boundary translator. They MUST use mappers to convert DTOs (from network) and Entities (from database) into Domain Models (from :core:model) before returning data.
  • Flag: If a repository implementation returns a raw DTO or Entity directly to the caller instead of a Domain model.

Files:

  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/StandingInstructionsRepositoryImp.kt
**/*Screen.kt

⚙️ CodeRabbit configuration file

**/*Screen.kt: Screen architecture rules:

Each screen must follow a 2-layer structure:

  1. Layer 1 (Entry/Route Composable):

    • Function Name: Typically *ScreenRoute or the entry-point composable.
    • Responsibilities: Inject ViewModel (e.g., koinViewModel), take navigation lambdas, collect the StateFlow, and handle ViewModel events.
    • Logic: Should only handle ViewModel interaction, state collection, and triggering navigation in response to ViewModel events.
  2. Layer 2 (Stateful/Content Composable — *Screen or *ScreenContent):

    • Parameters: MUST only take state (the UI state object) and a single onAction lambda (e.g., onAction: (FeatureAction) -> Unit).
    • Responsibilities: Render the UI based strictly on the provided state.
    • Rule: MUST NOT pass multiple separate lambda functions for different UI interactions; consolidate them into the single onAction.
    • Constraint: Must NOT contain any business logic or ViewModel/Navigation references.

Internal/private helper composables (e.g., dialogs, sections, sub-components):

  • These are NOT subject to the single onAction rule.
  • They may accept specific, focused lambdas (e.g., onRetry: () -> Unit) or a narrowed onAction as appropriate.
  • However, they must NOT be passed the ViewModel or navigation controllers directly.

UI consistency:

  • Avoid hardcoded values (dp, sp, padding, fontSize, colors)
  • Use DesignToken, KptTheme, AppColors, MifosTypography for spacing, typography, and colors

Code quality:

  • Keep Composables small and readable
  • Avoid deeply nested UI

Flag:

  • Missing Layer 2 (*Screen/*ScreenContent) separation from the entry-point composable
  • Layer 2 (*Screen/*ScreenContent) receiving multiple separate lambdas instead of a single onAction
  • UI logic inside the entry-point composable
  • Business logic inside any Composable
  • Hardcoded strings instead of using string resources
  • Hardcoded dp/sp values
  • Direct styling instead of using DesignToken or KptTheme

Files:

  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsScreen.kt
**/*ViewModel.kt

⚙️ CodeRabbit configuration file

**/*ViewModel.kt: MVI architecture rules:

  • All new features must follow MVI
  • ViewModel must extend BaseViewModel
  • The ViewModel MUST maintain a single UI state (e.g., a single StateFlow)
    instead of multiple separate state variables.

Required:

  • Use *State, *Event, *Action
  • Naming must be consistent (FeatureViewModel, FeatureState, FeatureEvent, FeatureAction)
  • Follow unidirectional flow:
    Action → ViewModel → State → UI

Internal reducer/action architecture rules:

  • Async operations MUST NOT directly mutate UI state repeatedly inside
    Flow collectors, suspend callbacks, or repository result handlers.

  • Repository/network/database results MUST be converted into internal
    actions using sendAction(...).

  • handleAction(...) must act as the primary reducer responsible for:

    • state mutation
    • reducer-style state transitions
    • triggering follow-up actions
  • Large async methods must be split into:

    1. async collection layer
    2. internal action dispatching
    3. reducer/state handling
  • Avoid directly calling another business/data-loading method from
    repository collectors or async callbacks.
    Prefer dispatching follow-up internal actions instead.

Preferred pattern:
repository result
-> sendAction(...)
-> handleAction(...)
-> mutableStateFlow.update { ... }

Anti-pattern examples:
mutableStateFlow.update { ... } inside collect { }
fetchX() -> directly calls fetchY() inside async collector
large methods mixing:
- collection
- state mutation
- navigation
- business chaining

Flag:

  • Multiple mutableStateFlow.update {} calls inside collect { }
  • Direct state mutation inside async repository callbacks
  • Async methods performing both collection and reducer logic
  • Direct business-flow chaining from async collectors
  • Missing internal reducer actions for async results
  • Missing *State / *Event / *Action
  • ViewModel not...

Files:

  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt
🧠 Learnings (2)
📚 Learning: 2026-02-06T13:15:16.968Z
Learnt from: sahilshivekar
Repo: openMF/android-client PR: 2603
File: feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsViewModel.kt:43-106
Timestamp: 2026-02-06T13:15:16.968Z
Learning: Guideline: When a Kotlin function parameter is nullable (e.g., balance: Double?, currencyCode: String?, maximumFractionDigits: Int?) and downstream calls require a non-null value, add null-safety handling in all implementations. Specifically, avoid calling Currency.getInstance(currencyCode) with a possibly null currencyCode; provide a safe default (e.g., currencyCode ?: "$") or validate before use. Ensure all platform targets (Android/Desktop/Native) follow consistent null handling, and consider centralizing currencyCode normalization in the common layer if feasible. Add tests covering null currencyCode to prevent NPEs.

Applied to files:

  • core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/UpdateStandingInstructionDto.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repository/StandingInstructionsRepository.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/services/StandingInstructionService.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerStandingInstructions.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/StandingInstructionsRepositoryImp.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsScreen.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt
📚 Learning: 2026-04-01T05:03:14.323Z
Learnt from: kartikey004
Repo: openMF/mifos-x-field-officer-app PR: 2659
File: feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanDisbursement/LoanAccountDisbursementScreen.kt:190-195
Timestamp: 2026-04-01T05:03:14.323Z
Learning: In this repo, existing `SelectableDates` / `SelectableDates.isSelectableDate(utcTimeMillis: Long)` implementations use `Clock.System.now().toEpochMilliseconds()` (UTC epoch millis) for date-boundary checks. During PR reviews, do not flag these checks for not using a timezone-aware `LocalDate` start-of-day approach; treat it as an established project-wide pattern. If a change is desired, handle it as a coordinated project-wide improvement rather than as a per-PR review issue.

Applied to files:

  • core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/UpdateStandingInstructionDto.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repository/StandingInstructionsRepository.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/services/StandingInstructionService.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerStandingInstructions.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/StandingInstructionsRepositoryImp.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsScreen.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt
🔇 Additional comments (3)
core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerStandingInstructions.kt (1)

22-61: LGTM!

core/network/src/commonMain/kotlin/com/mifos/core/network/dto/standingInstruction/UpdateStandingInstructionDto.kt (1)

14-30: LGTM!

core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/StandingInstructionsRepositoryImp.kt (1)

63-89: LGTM!

Comment on lines +76 to +83
is ViewStandingInstructionsAction.OnEditClick -> {
mutableStateFlow.update {
it.copy(
dialogState = ViewStandingInstructionsState.DialogState.Edit(
rowData = action.rowData,
amount = action.rowData.amount,
validFrom = action.rowData.validity,
beneficiary = action.rowData.beneficiary,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Display placeholders are leaking into update payloads in ViewStandingInstructionsViewModel.kt.

OnEditClick initializes edit fields from table row display values (Line 81-82), and validity fallback is "-- " (Line 263). That value can flow into updateStandingInstruction(...) (Line 171-179), producing invalid API data when users confirm without correcting placeholders.

Also applies to: 171-179, 263-263

🤖 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/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt`
around lines 76 - 83, The OnEditClick action handler in
ViewStandingInstructionsViewModel is initializing edit fields with display
placeholder values (such as "-- " for validity) instead of actual data values.
These placeholders can then flow into the updateStandingInstruction API call,
sending invalid data to the backend. To fix this, separate the display values
from the actual data values: use real data fields (not display placeholders like
"-- ") when initializing the Edit dialog state in the OnEditClick handler, and
add validation in the updateStandingInstruction method to reject or sanitize
placeholder values before sending them to the API.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt (1)

98-104: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Potential exception when parsing amount validation.

Line 99 calls action.value.toDoubleOrNull() to check for null, but then immediately calls action.value.toDouble() which can throw NumberFormatException if the value is not a valid double. This creates a redundant and potentially unsafe check.

🔧 Simplified validation fix
 EditField.AMOUNT -> {
-    val amountError = if (action.value.toDoubleOrNull() == null || action.value.toDouble() <= 0) {
+    val parsedAmount = action.value.toDoubleOrNull()
+    val amountError = if (parsedAmount == null || parsedAmount <= 0) {
         "Invalid amount"
     } else {
         null
     }
     currentEdit.copy(amount = action.value, amountError = amountError)
 }
🤖 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/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt`
around lines 98 - 104, The amount validation in the EditField.AMOUNT case has a
potential NumberFormatException issue. Currently, the code calls
action.value.toDoubleOrNull() to check for null, then calls
action.value.toDouble() again which can throw an exception if the value is
invalid. Refactor this by calling toDoubleOrNull() once and storing the result
in a variable, then use that single variable to check both for null and whether
the parsed value is greater than 0. This eliminates the redundant conversion and
prevents the potential exception when creating the amountError in the
currentEdit.copy() call.
🤖 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
`@feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt`:
- Around line 264-272: The CurrencyFormatter.format() call in the
ViewStandingInstructionsViewModel is receiving a nullable Double value
(instruction.amount) without null checking when state.currencyCode is not empty.
Add an explicit null check for instruction.amount before passing it to
CurrencyFormatter.format(), and return "--" as the fallback value if
instruction.amount is null, similar to the else branch handling.

---

Outside diff comments:
In
`@feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt`:
- Around line 98-104: The amount validation in the EditField.AMOUNT case has a
potential NumberFormatException issue. Currently, the code calls
action.value.toDoubleOrNull() to check for null, then calls
action.value.toDouble() again which can throw an exception if the value is
invalid. Refactor this by calling toDoubleOrNull() once and storing the result
in a variable, then use that single variable to check both for null and whether
the parsed value is greater than 0. This eliminates the redundant conversion and
prevents the potential exception when creating the amountError in the
currentEdit.copy() call.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a5a101cc-014e-4ca9-9334-328680607435

📥 Commits

Reviewing files that changed from the base of the PR and between 77fd653 and 9d86236.

📒 Files selected for processing (4)
  • feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/navigation/SavingsNavigation.kt
  • feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountSummary/SavingsAccountSummaryScreen.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsRoute.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/navigation/SavingsNavigation.kt
  • feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountSummary/SavingsAccountSummaryScreen.kt
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: Additional Code Review Guidelines:

  1. Null Safety & Stability
  • Avoid using !! operator
  • Handle null cases explicitly using safe calls or proper state handling
  • Do not assume values are always non-null without guarantees
  1. Architecture Boundaries
  • ViewModel must not depend on specific network/library implementations
  • Ensure proper separation between data, domain, and presentation layers
  • Do not format data (currency, dates, calculations) inside the UI layer
  • All formatting must be handled in the ViewModel and exposed via state (e.g., StateFlow)
  1. Performance Considerations
  • Avoid unnecessary recompositions in Compose
  • Do not attach heavy logic to frequently changing states (e.g., scrollState)
  • Prefer lifting state up instead of recomputing in child composables
  1. Compose & Navigation Best Practices
  • NEVER trigger navigation functions or side-effects directly during composition
  • Always wrap navigation calls inside LaunchedEffect or EventsEffect to avoid repeated execution on recomposition
  • Avoid triggering intensive side-effects during recomposition
  • Navigation routes must be type-safe.
  • Ensure all route classes or objects used for navigation are annotated with @Serializable.
  1. UI Structure
  • Dialogs must be separated into their own composables
  • Do not embed dialogs inline within complex main screens
  1. Localization Consistency
  • Ensure all supported languages are updated consistently across modules
  • Verify translations exist for newly added UI strings
  1. Code Cleanliness
  • Avoid unnecessary inline comments unless critical
  • Remove leftover debug or commented code
  1. Focus on correctness, readability, and maintainability over cosmetic nitpicks.
  • Avoid reviewing README, config, or asset files.
  • Prioritize identifying bugs, performance issues, and architectural concerns.
  1. Naming & Intent Rules:

Files:

  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsRoute.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt
**/*ViewModel.kt

⚙️ CodeRabbit configuration file

**/*ViewModel.kt: MVI architecture rules:

  • All new features must follow MVI
  • ViewModel must extend BaseViewModel
  • The ViewModel MUST maintain a single UI state (e.g., a single StateFlow)
    instead of multiple separate state variables.

Required:

  • Use *State, *Event, *Action
  • Naming must be consistent (FeatureViewModel, FeatureState, FeatureEvent, FeatureAction)
  • Follow unidirectional flow:
    Action → ViewModel → State → UI

Internal reducer/action architecture rules:

  • Async operations MUST NOT directly mutate UI state repeatedly inside
    Flow collectors, suspend callbacks, or repository result handlers.

  • Repository/network/database results MUST be converted into internal
    actions using sendAction(...).

  • handleAction(...) must act as the primary reducer responsible for:

    • state mutation
    • reducer-style state transitions
    • triggering follow-up actions
  • Large async methods must be split into:

    1. async collection layer
    2. internal action dispatching
    3. reducer/state handling
  • Avoid directly calling another business/data-loading method from
    repository collectors or async callbacks.
    Prefer dispatching follow-up internal actions instead.

Preferred pattern:
repository result
-> sendAction(...)
-> handleAction(...)
-> mutableStateFlow.update { ... }

Anti-pattern examples:
mutableStateFlow.update { ... } inside collect { }
fetchX() -> directly calls fetchY() inside async collector
large methods mixing:
- collection
- state mutation
- navigation
- business chaining

Flag:

  • Multiple mutableStateFlow.update {} calls inside collect { }
  • Direct state mutation inside async repository callbacks
  • Async methods performing both collection and reducer logic
  • Direct business-flow chaining from async collectors
  • Missing internal reducer actions for async results
  • Missing *State / *Event / *Action
  • ViewModel not...

Files:

  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt
🧠 Learnings (2)
📚 Learning: 2026-02-06T13:15:16.968Z
Learnt from: sahilshivekar
Repo: openMF/android-client PR: 2603
File: feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsViewModel.kt:43-106
Timestamp: 2026-02-06T13:15:16.968Z
Learning: Guideline: When a Kotlin function parameter is nullable (e.g., balance: Double?, currencyCode: String?, maximumFractionDigits: Int?) and downstream calls require a non-null value, add null-safety handling in all implementations. Specifically, avoid calling Currency.getInstance(currencyCode) with a possibly null currencyCode; provide a safe default (e.g., currencyCode ?: "$") or validate before use. Ensure all platform targets (Android/Desktop/Native) follow consistent null handling, and consider centralizing currencyCode normalization in the common layer if feasible. Add tests covering null currencyCode to prevent NPEs.

Applied to files:

  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsRoute.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt
📚 Learning: 2026-04-01T05:03:14.323Z
Learnt from: kartikey004
Repo: openMF/mifos-x-field-officer-app PR: 2659
File: feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanDisbursement/LoanAccountDisbursementScreen.kt:190-195
Timestamp: 2026-04-01T05:03:14.323Z
Learning: In this repo, existing `SelectableDates` / `SelectableDates.isSelectableDate(utcTimeMillis: Long)` implementations use `Clock.System.now().toEpochMilliseconds()` (UTC epoch millis) for date-boundary checks. During PR reviews, do not flag these checks for not using a timezone-aware `LocalDate` start-of-day approach; treat it as an established project-wide pattern. If a change is desired, handle it as a coordinated project-wide improvement rather than as a per-PR review issue.

Applied to files:

  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsRoute.kt
  • feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt
🔇 Additional comments (5)
feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsViewModel.kt (4)

273-273: Trailing space in validity fallback is still present.

This was flagged in a previous review - the "-- " fallback has a trailing space inconsistent with other fields using "--".


78-91: Edit dialog initialization from display values was previously flagged.

The past review comment correctly identified that initializing edit fields from formatted display values (like amount and validity) can leak placeholders into API payloads.


218-253: MVI reducer pattern violation was previously flagged.

Direct state mutations inside collect {} block violates the MVI architecture guidelines. The past review comment provides a comprehensive refactor to dispatch internal actions instead.


280-306: LGTM!

feature/standing-instruction/src/commonMain/kotlin/com/mifos/feature/standingInstruction/viewStandingInstructions/ViewStandingInstructionsRoute.kt (1)

18-43: LGTM!

@sahilshivekar

Copy link
Copy Markdown
Contributor

@techsavvy185 It's showing 500 amount even though you edited it to 5005
Also for showing the empty UI you can use the MifosErrorComponent

@techsavvy185

Copy link
Copy Markdown
Member Author

@sahilshivekar It's because I populated this list manually to show how it is going to look like. While the edit functionality is pinging to change the value using the API, for which the account does not exist obviously.

@techsavvy185

Copy link
Copy Markdown
Member Author

And, I've used MifosEmptyCard for the current empty state.

@sahilshivekar

Copy link
Copy Markdown
Contributor

@sahilshivekar It's because I populated this list manually to show how it is going to look like. While the edit functionality is pinging to change the value using the API, for which the account does not exist obviously.

Understood. But it should be tested with real account for which SI exists, if there is no such account then try to create one please (you can take help from victor for it).

@techsavvy185

Copy link
Copy Markdown
Member Author

@sahilshivekar well that's the issue with this functionality, it is not really working and @biplab1 was following up on this i believe.

@niyajali

Copy link
Copy Markdown
Collaborator

@sahilshivekar @biplab1 @revanthkumarJ could you guys review this PR

@revanthkumarJ

Copy link
Copy Markdown
Contributor

@techsavvy185 are the apis working ?.
If they are working please upload the latest video.

@techsavvy185

Copy link
Copy Markdown
Member Author

@revanthkumarJ The fetch and update APIs by themselves are functional, however the create API, I've tested it a few times using various dummy data to test my portion, but it is not working, also the web app create SI part is broken as I have highlighted previously as well. I am sorry for the delayed response, I have been busy with some stuff.
I am attaching a video highlighting the fact that the APIs are functional and the data that I am testing on is hardcoded so it gives a 404 in the update command.

Screen.Recording.2026-07-26.at.1.14.18.AM.mov

@mifos-cla-check

Copy link
Copy Markdown

👋 Hi @techsavvy185 — thank you for your pull request.

This PR is currently blocked because we do not have a Contributor License Agreement (CLA) on file for your GitHub account.

To get unblocked:

  1. Complete the form at https://mifos.org/about-us/financial-legal/mifos-contributor-agreement
  2. Complete the CLA signing process
  3. Once verified you will be added to the approved contributors list and this PR check will be cleared

@mifos-cla-check mifos-cla-check Bot added the cla-required CLA signature required before this PR can be merged label Jul 25, 2026
@mifos-cla-check

Copy link
Copy Markdown

👋 Hi @techsavvy185 — thank you for your pull request.

This PR is currently blocked because we do not have a Contributor License Agreement (CLA) on file for your GitHub account.

To get unblocked:

  1. Complete the form at https://mifos.org/about-us/financial-legal/mifos-contributor-agreement
  2. Complete the CLA signing process
  3. Once verified you will be added to the approved contributors list and this PR check will be cleared

@techsavvy185
techsavvy185 marked this pull request as draft July 26, 2026 18:14
@techsavvy185
techsavvy185 marked this pull request as ready for review July 26, 2026 18:14
@techsavvy185
techsavvy185 marked this pull request as draft July 27, 2026 06:53
@techsavvy185
techsavvy185 marked this pull request as ready for review July 27, 2026 06:53
@mifos-cla-check mifos-cla-check Bot removed the cla-required CLA signature required before this PR can be merged label Jul 27, 2026
@sonarqubecloud

sonarqubecloud Bot commented Aug 1, 2026

Copy link
Copy Markdown

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.

5 participants