feat(loan): implement reject loan screen - #2702
Conversation
Summary by CodeRabbit
WalkthroughAdds an end-to-end loan rejection flow: API contracts, repository and use case wiring, rejection form state and UI, navigation from loan actions, success handling, and loan profile refresh. ChangesThe PR follows one functional path from the loan account action to the rejection API and refreshed profile. Loan rejection implementation
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant LoanAccountActions
participant LoanRejectScreen
participant LoanRejectViewModel
participant RejectLoanUseCase
participant LoanService
participant LoanAccountProfile
LoanAccountActions->>LoanRejectScreen: navigate with loanId
LoanRejectScreen->>LoanRejectViewModel: submit date and note
LoanRejectViewModel->>RejectLoanUseCase: rejectLoan(loanId, input)
RejectLoanUseCase->>LoanService: POST rejection request
LoanService-->>LoanRejectViewModel: DataState<Unit>
LoanRejectViewModel-->>LoanRejectScreen: show success dialog
LoanRejectScreen->>LoanAccountProfile: reload profile after dismissal
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectState.kt`:
- Around line 21-22: Update the LoanRejectState constructor defaults for
rejectedOnDate and rejectedOnDateText so the current default rejection date is
evaluated for each state instance, and ensure rejectedOnDateText is derived from
that instance’s actual rejectedOnDate value rather than a class-load-time
constant.
🪄 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 Plus
Run ID: 37245ee0-5091-4044-867c-50258c5c985e
📒 Files selected for processing (21)
core/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.ktcore/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanRejectRepository.ktcore/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/loan/LoanRejectRepositoryImpl.ktcore/domain/src/commonMain/kotlin/com/mifos/core/domain/di/UseCaseModule.ktcore/domain/src/commonMain/kotlin/com/mifos/core/domain/useCases/loanReject/RejectLoanUseCase.ktcore/model/src/commonMain/kotlin/com/mifos/core/model/objects/account/loan/RejectLoanInput.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerLoan.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/dto/loans/RejectLoanRequestDto.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/dto/loans/RejectLoanResponseDto.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/services/LoanService.ktfeature/loan/src/commonMain/composeResources/values/strings.xmlfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/di/LoanModule.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountAction/LoanAccountActionNavigation.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileNavigation.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanChargeOff/LoanChargeOffScreen.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanDisburse/LoanDisburseScreen.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectScreen.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectScreenRoute.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectState.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectViewModel.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/navigation/LoanNavigation.kt
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
- GitHub Check: PR Checks / Static Analysis Check
🧰 Additional context used
📓 Path-based instructions (10)
**/network/**/*.kt
⚙️ CodeRabbit configuration file
**/network/**/*.kt: Network Layer API Guidelines:
- One-shot API calls MUST use
suspend, return raw responseT, and MUST NOT use Flow or DataState.- Streaming APIs MUST return
Flow<T>and MUST NOT usesuspendor DataState.
Files:
core/network/src/commonMain/kotlin/com/mifos/core/network/dto/loans/RejectLoanRequestDto.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/dto/loans/RejectLoanResponseDto.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerLoan.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/services/LoanService.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/loans/RejectLoanRequestDto.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/dto/loans/RejectLoanResponseDto.kt
**/{core-base,core}/**/*.kt
⚙️ CodeRabbit configuration file
**/{core-base,core}/**/*.kt: Critical Module Change Detection:
- Changes in
core-basemodule must be treated as high-impact.- Flag any PR that modifies files inside
core-basefor careful review.- Verify that modifications in
core-baseare necessary and minimal.
Output:- Clearly highlight that
core-baseis a shared foundational module and requires extra review attention.
Files:
core/network/src/commonMain/kotlin/com/mifos/core/network/dto/loans/RejectLoanRequestDto.ktcore/domain/src/commonMain/kotlin/com/mifos/core/domain/di/UseCaseModule.ktcore/domain/src/commonMain/kotlin/com/mifos/core/domain/useCases/loanReject/RejectLoanUseCase.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/dto/loans/RejectLoanResponseDto.ktcore/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanRejectRepository.ktcore/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.ktcore/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/loan/LoanRejectRepositoryImpl.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerLoan.ktcore/model/src/commonMain/kotlin/com/mifos/core/model/objects/account/loan/RejectLoanInput.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/services/LoanService.kt
**/*.kt
⚙️ CodeRabbit configuration file
**/*.kt: Additional Code Review Guidelines:
- 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
- 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)
- 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
- Compose & Navigation Best Practices
- NEVER trigger navigation functions or side-effects directly during composition
- Always wrap navigation calls inside
LaunchedEffectorEventsEffectto 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.
- UI Structure
- Dialogs must be separated into their own composables
- Do not embed dialogs inline within complex main screens
- Localization Consistency
- Ensure all supported languages are updated consistently across modules
- Verify translations exist for newly added UI strings
- Code Cleanliness
- Avoid unnecessary inline comments unless critical
- Remove leftover debug or commented code
- Focus on correctness, readability, and maintainability over cosmetic nitpicks.
- Avoid reviewing README, config, or asset files.
- Prioritize identifying bugs, performance issues, and architectural concerns.
- Naming & Intent Rules:
- Follow the official Kotlin Coding Conventions:
https://kotlinlang.org/docs/coding-conventions.html- Use self-explanatory ...
Files:
core/network/src/commonMain/kotlin/com/mifos/core/network/dto/loans/RejectLoanRequestDto.ktcore/domain/src/commonMain/kotlin/com/mifos/core/domain/di/UseCaseModule.ktcore/domain/src/commonMain/kotlin/com/mifos/core/domain/useCases/loanReject/RejectLoanUseCase.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/dto/loans/RejectLoanResponseDto.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileNavigation.ktcore/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanRejectRepository.ktcore/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/navigation/LoanNavigation.ktcore/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/loan/LoanRejectRepositoryImpl.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerLoan.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanDisburse/LoanDisburseScreen.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectScreenRoute.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/di/LoanModule.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountAction/LoanAccountActionNavigation.ktcore/model/src/commonMain/kotlin/com/mifos/core/model/objects/account/loan/RejectLoanInput.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/services/LoanService.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanChargeOff/LoanChargeOffScreen.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectState.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectScreen.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectViewModel.kt
**/data/repository/**/*.kt
⚙️ CodeRabbit configuration file
**/data/repository/**/*.kt: Repository Interface Guidelines:
- One-shot operations MUST use
suspend funand returnDataState<T>. MUST NOT use Flow.- Streaming operations MUST return
Flow<DataState<T>>and MUST NOT usesuspend.- Repository interfaces must consistently return Domain models (from :core:model) wrapped in
DataStateorFlow.- Flag: Any interface method that returns a DTO or an Entity.
Files:
core/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanRejectRepository.kt
**/data/repositoryImp/**/*.kt
⚙️ CodeRabbit configuration file
**/data/repositoryImp/**/*.kt: Repository Implementation Guidelines:
- MUST convert
T->DataState<T>for one-shot APIs, andFlow<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/loan/LoanRejectRepositoryImpl.kt
**/*Screen.kt
⚙️ CodeRabbit configuration file
**/*Screen.kt: Screen architecture rules:Each screen must follow a 2-layer structure:
Layer 1 (Entry/Route Composable):
- Function Name: Typically
*ScreenRouteor the entry-point composable.- Responsibilities: Inject ViewModel (e.g.,
koinViewModel), take navigation lambdas, collect theStateFlow, and handle ViewModel events.- Logic: Should only handle ViewModel interaction, state collection, and triggering navigation in response to ViewModel events.
Layer 2 (Stateful/Content Composable —
*Screenor*ScreenContent):
- Parameters: MUST only take
state(the UI state object) and a singleonActionlambda (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
onActionrule.- They may accept specific, focused lambdas (e.g.,
onRetry: () -> Unit) or a narrowedonActionas 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 singleonAction- 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/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanDisburse/LoanDisburseScreen.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanChargeOff/LoanChargeOffScreen.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectScreen.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/account/loan/RejectLoanInput.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 → UIInternal 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 usingsendAction(...).
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:
- async collection layer
- internal action dispatching
- 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 chainingFlag:
- 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/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectViewModel.kt
**/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/loan/src/commonMain/composeResources/values/strings.xml
🧠 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:
core/network/src/commonMain/kotlin/com/mifos/core/network/dto/loans/RejectLoanRequestDto.ktcore/domain/src/commonMain/kotlin/com/mifos/core/domain/di/UseCaseModule.ktcore/domain/src/commonMain/kotlin/com/mifos/core/domain/useCases/loanReject/RejectLoanUseCase.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/dto/loans/RejectLoanResponseDto.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileNavigation.ktcore/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanRejectRepository.ktcore/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/navigation/LoanNavigation.ktcore/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/loan/LoanRejectRepositoryImpl.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerLoan.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanDisburse/LoanDisburseScreen.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectScreenRoute.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/di/LoanModule.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountAction/LoanAccountActionNavigation.ktcore/model/src/commonMain/kotlin/com/mifos/core/model/objects/account/loan/RejectLoanInput.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/services/LoanService.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanChargeOff/LoanChargeOffScreen.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectState.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectScreen.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectViewModel.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/loans/RejectLoanRequestDto.ktcore/domain/src/commonMain/kotlin/com/mifos/core/domain/di/UseCaseModule.ktcore/domain/src/commonMain/kotlin/com/mifos/core/domain/useCases/loanReject/RejectLoanUseCase.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/dto/loans/RejectLoanResponseDto.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileNavigation.ktcore/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanRejectRepository.ktcore/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/navigation/LoanNavigation.ktcore/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/loan/LoanRejectRepositoryImpl.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerLoan.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanDisburse/LoanDisburseScreen.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectScreenRoute.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/di/LoanModule.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountAction/LoanAccountActionNavigation.ktcore/model/src/commonMain/kotlin/com/mifos/core/model/objects/account/loan/RejectLoanInput.ktcore/network/src/commonMain/kotlin/com/mifos/core/network/services/LoanService.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanChargeOff/LoanChargeOffScreen.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectState.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectScreen.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectViewModel.kt
📚 Learning: 2026-07-08T13:55:15.322Z
Learnt from: sahilshivekar
Repo: openMF/mifos-x-field-officer-app PR: 2698
File: feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/createGuarantor/CreateGuarantorViewModel.kt:366-397
Timestamp: 2026-07-08T13:55:15.322Z
Learning: In Kotlin ViewModels (e.g., using `mutableStateFlow` + `collect {}` to handle async results), it’s acceptable to directly update a single piece of state (and call `sendEvent`) inside the `collect {}` block for simple cases (e.g., updating one property like `searchedClientOptions`). Reserve the internal-action/reducer pattern (`sendAction(...)` + `handleAction`) for async results that affect multiple properties and/or require more complex state transitions, to avoid unnecessary ceremony for trivial cases.
Applied to files:
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileNavigation.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/navigation/LoanNavigation.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanDisburse/LoanDisburseScreen.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectScreenRoute.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/di/LoanModule.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountAction/LoanAccountActionNavigation.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanChargeOff/LoanChargeOffScreen.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectState.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectScreen.ktfeature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectViewModel.kt
📚 Learning: 2026-03-16T11:58:32.496Z
Learnt from: sahilshivekar
Repo: openMF/mifos-x-field-officer-app PR: 2647
File: feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/navigation/LoanNavigation.kt:96-96
Timestamp: 2026-03-16T11:58:32.496Z
Learning: In feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/navigation/LoanNavigation.kt (Kotlin/Compose Multiplatform), navigateToDashboard uses a direct method reference navController::navigateToLoanDashboardScreen without an ID guard. This is intentional: LoanAccountProfileScreen always provides a valid loanId, and LoanDashboardScreen handles invalid/missing data by showing a 'Failed to fetch Loan details' error state. Do not flag the absence of an ID guard here in future reviews.
Applied to files:
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/navigation/LoanNavigation.kt
🔇 Additional comments (20)
core/model/src/commonMain/kotlin/com/mifos/core/model/objects/account/loan/RejectLoanInput.kt (1)
12-19: LGTM!core/network/src/commonMain/kotlin/com/mifos/core/network/dto/loans/RejectLoanRequestDto.kt (1)
10-19: LGTM!core/network/src/commonMain/kotlin/com/mifos/core/network/dto/loans/RejectLoanResponseDto.kt (1)
10-19: LGTM!core/network/src/commonMain/kotlin/com/mifos/core/network/services/LoanService.kt (1)
29-30: LGTM!Also applies to: 93-97
core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerLoan.kt (1)
29-30: LGTM!Also applies to: 161-166
core/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanRejectRepository.kt (1)
12-17: LGTM!core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/loan/LoanRejectRepositoryImpl.kt (1)
12-44: LGTM!core/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.kt (1)
43-43: LGTM!Also applies to: 156-156, 211-211
core/domain/src/commonMain/kotlin/com/mifos/core/domain/useCases/loanReject/RejectLoanUseCase.kt (1)
12-23: LGTM!core/domain/src/commonMain/kotlin/com/mifos/core/domain/di/UseCaseModule.kt (1)
101-101: LGTM!Also applies to: 193-193
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectViewModel.kt (1)
26-119: LGTM!feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectScreen.kt (1)
58-202: LGTM!feature/loan/src/commonMain/composeResources/values/strings.xml (1)
15-15: LGTM!Also applies to: 154-158
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanChargeOff/LoanChargeOffScreen.kt (1)
23-23: LGTM!Also applies to: 224-224
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanDisburse/LoanDisburseScreen.kt (1)
27-27: LGTM!Also applies to: 281-281
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/di/LoanModule.kt (1)
27-27: LGTM!Also applies to: 43-43
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanReject/LoanRejectScreenRoute.kt (1)
17-34: LGTM!feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileNavigation.kt (1)
65-72: LGTM!feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountAction/LoanAccountActionNavigation.kt (1)
23-89: LGTM!feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/navigation/LoanNavigation.kt (1)
102-105: LGTM!Also applies to: 142-142



Fixes - Jira-#703
Before (Reject option doesn't open any screen)
After
reject_loan_success.mp4