Skip to content

feat(loan): implement assign loan officer screen#2664

Open
Anshjn1411 wants to merge 21 commits into
openMF:devfrom
Anshjn1411:ticket-680
Open

feat(loan): implement assign loan officer screen#2664
Anshjn1411 wants to merge 21 commits into
openMF:devfrom
Anshjn1411:ticket-680

Conversation

@Anshjn1411

@Anshjn1411 Anshjn1411 commented Apr 5, 2026

Copy link
Copy Markdown

…Loan Officer screen for loan accounts in Approved status, accessible from the Loan Account Actions screen.

Fixes - Jira-#680
Screenshot 2026-04-05 at 18 55 17
Screenshot 2026-04-05 at 18 55 27
Screenshot 2026-04-05 at 19 07 23

mobile mobile Screensort and recording

ticket-680-mobile-video.mp4
image

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.

Summary by CodeRabbit

  • New Features
    • Assign or reassign loan officers from the loan profile (approved loans): select a new officer, pick an assignment date, view current officer info, and submit. Includes validation (required fields, no officers available, same-officer guard), progress indicators, success/error snackbars, and navigation to the new assignment screen.

…Loan Officer screen for loan accounts in Approved status, accessible from the Loan Account Actions screen.
@Anshjn1411 Anshjn1411 requested a review from a team April 5, 2026 13:59
@coderabbitai

coderabbitai Bot commented Apr 5, 2026

Copy link
Copy Markdown

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

Adds an "Assign Loan Officer" feature: repository APIs and implementation to list/assign officers, network endpoint and request model, data mappers and domain models, Compose UI screen + ViewModel/state, navigation integration and profile-screen hooks, resources, and DI registration.

Changes

Cohort / File(s) Summary
Repository & Impl
core/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanAccountSummaryRepository.kt, core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/LoanAccountSummaryRepositoryImp.kt
Added flows to fetch loan-for-assignment and office staff/options, added assignLoanOfficer suspend API and implementation; introduced DataManagerStaff and ioDispatcher, applied flowOn, added cancellation/error handling.
Network & Service
core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerLoan.kt, core/network/src/commonMain/kotlin/com/mifos/core/network/services/LoanService.kt
Added assignLoanOfficer service endpoint and DataManager method that POSTs command and deserializes GenericResponse, erroring on non-success responses.
Models & Mappers
core/model/src/commonMain/kotlin/com/mifos/core/model/objects/account/loan/AssignLoanOfficerRequest.kt, core/model/src/commonMain/kotlin/com/mifos/core/model/entity/accounts/loan/LoanForAssignOfficer.kt, core/model/src/commonMain/kotlin/com/mifos/core/model/entity/accounts/loan/StaffOption.kt, core/data/src/commonMain/kotlin/com/mifos/core/data/mapper/loan/LoanAssignOfficerMapper.kt
Added request payload AssignLoanOfficerRequest, domain models LoanForAssignOfficer and StaffOption, plus mappers from DB entities to these models.
Feature UI & State
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerScreen.kt, .../AssignLoanOfficerUiState.kt, .../AssignLoanOfficerViewModel.kt
New Compose screen and form, UI state/effects/actions, and ViewModel handling load flows, officer options, date picking, validation, submit flow and effects.
Navigation & Integration
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerScreenRoute.kt, feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/navigation/LoanNavigation.kt, feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileNavigation.kt
Added AssignLoanOfficer route, navigation helpers, registered screen destination, and wired new navigation callbacks into loan profile flow.
Profile screen & ViewModel changes
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileScreen.kt, .../LoanAccountProfileViewModel.kt
Added LoanProfileStatus.APPROVED, eligibility check canAssignLoanOfficerForProfile, new assign-officer action/event, updated screen to surface assign-officer button and emit navigation event.
Resources & DI
feature/loan/src/commonMain/composeResources/values/strings.xml, feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/di/LoanModule.kt
Added UI strings for assign-officer flow and registered AssignLoanOfficerViewModel in Koin module.

Sequence Diagram

sequenceDiagram
    participant User as User
    participant Screen as AssignLoanOfficerScreen
    participant VM as AssignLoanOfficerViewModel
    participant Repo as LoanAccountSummaryRepository
    participant DM as DataManagerLoan / LoanService

    User->>Screen: Open assign officer (loanId)
    Screen->>VM: init(loanId)
    VM->>Repo: getLoanForAssignOfficer(loanId)
    Repo->>DM: fetch loan details
    DM-->>Repo: loan entity
    Repo-->>VM: DataState.Success(LoanForAssignOfficer)
    VM->>Repo: getLoanOfficerStaffOptions(officeId)
    Repo->>DM: fetch staff for office
    DM-->>Repo: staff list
    Repo-->>VM: DataState.Success(staff options)
    VM-->>Screen: uiState.content(officers)
    User->>Screen: select officer, pick date, submit
    Screen->>VM: submit()
    VM->>Repo: assignLoanOfficer(loanId, AssignLoanOfficerRequest)
    Repo->>DM: POST assignLoanOfficer
    DM-->>Repo: GenericResponse
    Repo-->>VM: DataState.Success(response)
    VM-->>Screen: emit success effect / navigate back
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • revanthkumarJ
  • kartikey004
  • niyajali

Poem

🐰 I hopped through code in a neat little trail,
Picked an officer, set a date without fail.
ViewModel hummed, the repository sprung,
Network said "done" — the success bell was rung.
Hooray! A small rabbit dance, quick as a tail.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.87% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(loan): implement assign loan officer screen' directly and accurately describes the main change—implementing a new Assign Loan Officer screen for loan accounts, which is the core feature across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 and usage tips.

@Kartikey15dem

Kartikey15dem commented Apr 5, 2026

Copy link
Copy Markdown
Contributor

@Anshjn1411
did you fetched the the latest changes before pushing? Also can you add screenshots or video of the mobile UI

@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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/LoanAccountSummaryRepositoryImp.kt`:
- Around line 39-42: The filter in getLoanOfficersForOffice currently keeps
staff with isLoanOfficer == null; change the predicate to require an explicit
true so only confirmed loan officers are returned: update the map/filter on
dataManagerStaff.getStaffInOffice(officeId) to use staff.filter {
it.isLoanOfficer == true } (reference: getLoanOfficersForOffice,
dataManagerStaff.getStaffInOffice, StaffEntity.isLoanOfficer).
- Around line 45-53: In assignLoanOfficer (LoanAccountSummaryRepositoryImp) let
coroutine cancellation propagate by removing the try/catch that swallows
CancellationException and instead rethrow CancellationException while handling
all other exceptions in the existing .catch block; specifically, ensure any
caught CancellationException is immediately thrown (or not caught) and only
non-cancellation exceptions are emitted as DataState.Error in the flow.catch,
referencing assignLoanOfficer, DataState.Error, and the flow.catch operator for
the change.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerViewModel.kt`:
- Around line 131-177: Add a cheap re-entry guard at the start of submit(): read
the current _uiState.value and return immediately if submitInProgress == true or
completed == true to prevent concurrent submissions; keep the existing
validation (selectedOfficerIndex, officer, loan, officerId) but perform the
in-flight/completed check before launching viewModelScope; reference the
submit() method and the _uiState fields (submitInProgress and completed) so
subsequent taps or callers are ignored until the current assignLoanOfficer flow
finishes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a23fd761-2dab-4387-bcb7-e2a4a32c5b94

📥 Commits

Reviewing files that changed from the base of the PR and between 68a8040 and 4ffe2b9.

📒 Files selected for processing (16)
  • core/common/src/commonMain/kotlin/com/mifos/core/common/utils/DateHelper.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanAccountSummaryRepository.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/LoanAccountSummaryRepositoryImp.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/objects/account/loan/AssignLoanOfficerRequest.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerLoan.kt
  • core/network/src/commonMain/kotlin/com/mifos/core/network/services/LoanService.kt
  • feature/loan/src/commonMain/composeResources/values/strings.xml
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerScreen.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerScreenRoute.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerUiState.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerViewModel.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/di/LoanModule.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileNavigation.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileScreen.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileViewModel.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/navigation/LoanNavigation.kt

@Anshjn1411

Copy link
Copy Markdown
Author

@Anshjn1411 did you fetched the the latest changes before pushing? Also can you add screenshots or video of the mobile UI

I have Updated the branch to latest commit and also attach the screensort of mobile view . Thank you !

Comment thread core/common/src/commonMain/kotlin/com/mifos/core/common/utils/DateHelper.kt Outdated

@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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerScreen.kt`:
- Around line 91-99: The scaffold/back and Cancel handlers currently allow
navigation while a submit is in progress; update the MifosScaffold onBackPressed
(and any Cancel button click handlers, e.g., the Cancel lambda around lines
250-257) to first check uiState.submitInProgress and only call navigateBack/pop
if uiState.submitInProgress is false (i.e., guard with
!uiState.submitInProgress) so the screen is non-dismissible while
AssignLoanOfficerViewModel.submit is running; keep the existing submit button
disabling behavior unchanged.
- Around line 162-163: The dropdown labels currently use displayName ?:
"${it.firstname} ${it.lastname}".trim(), which treats blank displayName or blank
name parts as valid and can produce empty or "null null" labels; update the
officer label construction in the officerLabels mapping to build the label from
non-blank parts (e.g., filter firstname and lastname for isNullOrBlank, join
with a space) and if that result is still blank, fall back to a stable
identifier such as it.id or it.username so every officer label is meaningful and
never empty; adjust any related usage (officerLabels and the uiState.officers
mapping) accordingly.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerViewModel.kt`:
- Around line 137-155: The current flow in AssignLoanOfficerViewModel allows
building an AssignLoanOfficerRequest where toLoanOfficerId equals the existing
loan.loanOfficerId, producing a no-op reassignment; before constructing the
request in the block that starts viewModelScope.launch, add a guard that
compares officerId to loan.loanOfficerId (only when loan.loanOfficerId > 0) and
if they are equal update _uiState to set submitInProgress = false and
submitError to a validation message (e.g., "Selected officer is the same as
current officer") and return early instead of creating AssignLoanOfficerRequest;
this prevents sending a redundant request and surfaces a clear validation error
to the UI.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9b349c1c-2395-468d-8c57-53c823c11b0e

📥 Commits

Reviewing files that changed from the base of the PR and between 4ffe2b9 and 313d02f.

📒 Files selected for processing (8)
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/LoanAccountSummaryRepositoryImp.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerScreen.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerUiState.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerViewModel.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/di/LoanModule.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileScreen.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileViewModel.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/navigation/LoanNavigation.kt
✅ Files skipped from review due to trivial changes (2)
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/di/LoanModule.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerUiState.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileScreen.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/LoanAccountSummaryRepositoryImp.kt

@Anshjn1411 Anshjn1411 requested a review from Arinyadav1 April 6, 2026 06:38
@biplab1 biplab1 changed the title feat(Loan Account v0.02 / Assign Loan Officer) :Implement the Assign … feat(loan): implement Assign Loan Officer screen Apr 6, 2026

@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: 2

🧹 Nitpick comments (1)
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerScreen.kt (1)

224-239: Defensively ignore officer changes while submit is running.

submit() snapshots the selected officer before the request starts, but this callback can still update selectedOfficerIndex. Guarding it on !uiState.submitInProgress keeps the UI from drifting to a different selection than the one currently being submitted.

Suggested change
-            onOptionSelected = { index, _ -> onOfficerSelected(index) },
+            onOptionSelected = { index, _ ->
+                if (!uiState.submitInProgress) onOfficerSelected(index)
+            },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerScreen.kt`
around lines 224 - 239, The dropdown's onOptionSelected may change
uiState.selectedOfficerIndex while submit() is running; update the
MifosTextFieldDropdown usage so the onOptionSelected callback only invokes
onOfficerSelected(index) when uiState.submitInProgress is false (i.e., guard the
callback with !uiState.submitInProgress) to prevent the selection from drifting
away from the snapshot captured by submit(); reference MifosTextFieldDropdown,
onOfficerSelected, uiState.submitInProgress, and submit() when locating the
change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerViewModel.kt`:
- Around line 105-112: In the DataState.Success branch of
AssignLoanOfficerViewModel (where _uiState.update is called), detect when
dataState.data is empty and treat that as an explicit error/empty state: instead
of only setting officers and isLoading, also set a loadError (or a dedicated
noOfficers flag) and ensure the UIState reflects no options available so the
view can show a friendly message and enable/disable or hide the submit flow
appropriately; modify the update call in the Success branch to set loadError (or
noOfficers) when dataState.data.isEmpty() and leave officers empty and isLoading
false.
- Around line 93-104: In loadOfficers and assignLoanOfficer, handle exceptions
thrown by the repository (not just DataState.Error) by wrapping the suspend
calls to repository.getLoanOfficersForOffice(officeId) and
repository.assignLoanOfficer(...) in try/catch blocks; on catch update _uiState
(for loadOfficers: set isLoading = false and loadError = exception message or a
default, and for assignLoanOfficer: set submitInProgress = false and submitError
= exception message or a default) so the UI never remains stuck in a
loading/submitting state and errors surface; reference the functions
loadOfficers and assignLoanOfficer and the _uiState.update usage to locate where
to add these catch branches.

---

Nitpick comments:
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerScreen.kt`:
- Around line 224-239: The dropdown's onOptionSelected may change
uiState.selectedOfficerIndex while submit() is running; update the
MifosTextFieldDropdown usage so the onOptionSelected callback only invokes
onOfficerSelected(index) when uiState.submitInProgress is false (i.e., guard the
callback with !uiState.submitInProgress) to prevent the selection from drifting
away from the snapshot captured by submit(); reference MifosTextFieldDropdown,
onOfficerSelected, uiState.submitInProgress, and submit() when locating the
change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 209d5273-7f4c-4cb1-81f4-e802c1ce2ee7

📥 Commits

Reviewing files that changed from the base of the PR and between 313d02f and 821ab17.

📒 Files selected for processing (3)
  • feature/loan/src/commonMain/composeResources/values/strings.xml
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerScreen.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerViewModel.kt
✅ Files skipped from review due to trivial changes (1)
  • feature/loan/src/commonMain/composeResources/values/strings.xml

@therajanmaurya therajanmaurya left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

After successful assignment it should redirect to load detail again

override fun assignLoanOfficer(loanId: Int, request: AssignLoanOfficerRequest): Flow<DataState<GenericResponse>> {
return flow {
emit(DataState.Loading)
emit(DataState.Success(dataManagerLoan.assignLoanOfficer(loanId, request)))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we need to listen the response first

val loanAssignmentStatus = dataManagerLoan.assignLoanOfficer(loanId, request)

if true then DataState.Success else DataState.Error

return mBaseApiManager.loanService.disburseLoan(loanId, loanDisbursement)
}

suspend fun assignLoanOfficer(loanId: Int, request: AssignLoanOfficerRequest): GenericResponse {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Wrap with try catch block

…her.io at required lines and also use BaseViewModel .
@Anshjn1411 Anshjn1411 requested a review from niyajali April 10, 2026 13:01

@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

♻️ Duplicate comments (1)
core/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanAccountSummaryRepository.kt (1)

29-30: 🛠️ Refactor suggestion | 🟠 Major

Remove the StaffEntity-returning variant from this public contract.

getLoanOfficerStaffOptions() already gives callers a domain-safe shape for this flow, so getLoanOfficersForOffice() only re-exposes a Room entity and duplicates the same API at a lower abstraction level. Either delete it or promote it to a domain model as well.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@core/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanAccountSummaryRepository.kt`
around lines 29 - 30, The interface exposes a Room entity via
getLoanOfficersForOffice(officeId: Int): Flow<DataState<List<StaffEntity>>>
which duplicates the domain-safe API getLoanOfficerStaffOptions(officeId: Int):
Flow<DataState<List<StaffOption>>>; remove the former from the public contract:
delete the getLoanOfficersForOffice signature from LoanAccountSummaryRepository,
update any implementing classes to stop providing it (or make it
internal/private), and refactor call sites to use getLoanOfficerStaffOptions
instead (or convert the implementation to emit StaffOption if callers require
similar data).
🧹 Nitpick comments (1)
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerUiState.kt (1)

22-29: Prefer a stable selected-officer key in Content.

selectedOfficerIndex ties the form state to the current ordering of officers. If that list refreshes or re-sorts, the same index can resolve to a different StaffOption. Storing selectedOfficerId here and deriving the visible index from the current list is safer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerUiState.kt`
around lines 22 - 29, Replace the unstable selectedOfficerIndex in the Content
data class with a stable selectedOfficerId (nullable, matching StaffOption.id's
type) and keep officers as the source of truth; update any code that read
selectedOfficerIndex to compute the visible index via officers.indexOfFirst {
it.id == selectedOfficerId } (or -1 if not found), and update
constructors/usages of AssignLoanOfficerUiState.Content and any
validation/submit logic (e.g., where officerShowError or submitInProgress depend
on selection) to use selectedOfficerId instead of the index.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerViewModel.kt`:
- Around line 170-175: The ViewModel is emitting
AssignLoanOfficerEffect.ShowMessage followed by
AssignLoanOfficerEffect.NavigateBack which blocks because the screen's snackbar
suspension delays navigation; change the contract so a single atomic effect is
emitted instead: add a new effect like
AssignLoanOfficerEffect.ShowMessageAndNavigateBack (or alternatively emit a
NavigateBack with the message as a navigation argument) and use that from
AssignLoanOfficerViewModel success path and from showLoadError path; then update
the AssignLoanOfficerScreen effect collector to handle the new
ShowMessageAndNavigateBack (or read the nav arg) so the snackbar is shown
without blocking emission of the navigation pop.

---

Duplicate comments:
In
`@core/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanAccountSummaryRepository.kt`:
- Around line 29-30: The interface exposes a Room entity via
getLoanOfficersForOffice(officeId: Int): Flow<DataState<List<StaffEntity>>>
which duplicates the domain-safe API getLoanOfficerStaffOptions(officeId: Int):
Flow<DataState<List<StaffOption>>>; remove the former from the public contract:
delete the getLoanOfficersForOffice signature from LoanAccountSummaryRepository,
update any implementing classes to stop providing it (or make it
internal/private), and refactor call sites to use getLoanOfficerStaffOptions
instead (or convert the implementation to emit StaffOption if callers require
similar data).

---

Nitpick comments:
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerUiState.kt`:
- Around line 22-29: Replace the unstable selectedOfficerIndex in the Content
data class with a stable selectedOfficerId (nullable, matching StaffOption.id's
type) and keep officers as the source of truth; update any code that read
selectedOfficerIndex to compute the visible index via officers.indexOfFirst {
it.id == selectedOfficerId } (or -1 if not found), and update
constructors/usages of AssignLoanOfficerUiState.Content and any
validation/submit logic (e.g., where officerShowError or submitInProgress depend
on selection) to use selectedOfficerId instead of the index.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7bac2723-bb34-483b-a821-e57385ed36c6

📥 Commits

Reviewing files that changed from the base of the PR and between 9c7d926 and ae5c032.

📒 Files selected for processing (8)
  • core/data/src/commonMain/kotlin/com/mifos/core/data/mapper/loan/LoanAssignOfficerMapper.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanAccountSummaryRepository.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/LoanAccountSummaryRepositoryImp.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/entity/accounts/loan/LoanForAssignOfficer.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/entity/accounts/loan/StaffOption.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerScreen.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerUiState.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerViewModel.kt
✅ Files skipped from review due to trivial changes (2)
  • core/model/src/commonMain/kotlin/com/mifos/core/model/entity/accounts/loan/StaffOption.kt
  • core/model/src/commonMain/kotlin/com/mifos/core/model/entity/accounts/loan/LoanForAssignOfficer.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/LoanAccountSummaryRepositoryImp.kt

Comment on lines +170 to +175
sendEvent(
AssignLoanOfficerEffect.ShowMessage(
getString(Res.string.feature_loan_assign_loan_officer_success),
),
)
sendEvent(AssignLoanOfficerEffect.NavigateBack)

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

🧩 Analysis chain

🌐 Web query:

In Jetpack Compose Material3, does SnackbarHostState.showSnackbar suspend until the snackbar is dismissed or an action is performed?

💡 Result:

Web search failed: Server error: no LLM provider could handle the message


🏁 Script executed:

fd -t f "AssignLoanOfficerScreen.kt" --exec cat -n {} \;

Repository: openMF/mifos-x-field-officer-app

Length of output: 12947


🏁 Script executed:

fd -t f "AssignLoanOfficerViewModel.kt" --exec cat -n {} \;

Repository: openMF/mifos-x-field-officer-app

Length of output: 10027


🏁 Script executed:

# Search for SnackbarHostState usage patterns to understand showSnackbar behavior
rg "showSnackbar" -A 3 -B 1

Repository: openMF/mifos-x-field-officer-app

Length of output: 50388


Emit ShowMessage and NavigateBack as a single effect or redesign the collection pattern.

The current effect collector in AssignLoanOfficerScreen.kt lines 72–77 calls showSnackbar(), which suspends until the snackbar dismisses. This blocks the effect stream, delaying the following NavigateBack by ~4 seconds. On load failures, this leaves users on the blank Error screen during this wait; on success, it unnecessarily delays navigation back to the previous screen.

Affected locations
  • Lines 170–175 (success path)
  • Lines 186–193 (error path via showLoadError)

Consolidate these into a single effect (e.g., ShowMessageAndNavigateBack) or pass the message as a navigation argument and pop immediately.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerViewModel.kt`
around lines 170 - 175, The ViewModel is emitting
AssignLoanOfficerEffect.ShowMessage followed by
AssignLoanOfficerEffect.NavigateBack which blocks because the screen's snackbar
suspension delays navigation; change the contract so a single atomic effect is
emitted instead: add a new effect like
AssignLoanOfficerEffect.ShowMessageAndNavigateBack (or alternatively emit a
NavigateBack with the message as a navigation argument) and use that from
AssignLoanOfficerViewModel success path and from showLoadError path; then update
the AssignLoanOfficerScreen effect collector to handle the new
ShowMessageAndNavigateBack (or read the nav arg) so the snackbar is shown
without blocking emission of the navigation pop.

@biplab1 biplab1 changed the title feat(loan): implement Assign Loan Officer screen feat(loan): implement assign loan officer screen Apr 13, 2026
@biplab1

biplab1 commented May 8, 2026

Copy link
Copy Markdown
Contributor

@Anshjn1411 Please put the ui states in the ViewModel itself and get rid of feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerUiState.kt.

@biplab1

biplab1 commented May 8, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 8, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@Anshjn1411

Anshjn1411 commented May 8, 2026

Copy link
Copy Markdown
Author

@Anshjn1411 Please put the ui states in the ViewModel itself and get rid of feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerUiState.kt.

Sir , we can do this but the other feature modules like loan account , repayment are having separate file for UiState ?
so , should make it inside the viewModel or leave like this only ?

- Rename `LoanForAssignOfficer` domain model to `AssignLoanOfficerLoan`.
- Rename `LoanAssignOfficerMapper` to `AssignLoanOfficerMapper` and update mapper function names (`toDomain` to `toAssignLoanOfficerLoan`, `toDomain` to `toStaffOption`).
- Move `AssignLoanOfficerUiState`, `AssignLoanOfficerEffect`, and `AssignLoanOfficerAction` from a standalone file to `AssignLoanOfficerViewModel.kt`.
- Update repository, use case, and view model references to reflect the renamed classes and mapper functions.
@sonarqubecloud

sonarqubecloud Bot commented May 8, 2026

Copy link
Copy Markdown

@Anshjn1411 Anshjn1411 requested a review from biplab1 May 11, 2026 09:06
@biplab1

biplab1 commented May 15, 2026

Copy link
Copy Markdown
Contributor

@Anshjn1411 Please put the ui states in the ViewModel itself and get rid of feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/assignLoanOfficer/AssignLoanOfficerUiState.kt.

Sir , we can do this but the other feature modules like loan account , repayment are having separate file for UiState ? so , should make it inside the viewModel or leave like this only ?

Those could be older implementations. For reference take a look at Client module.

@biplab1

biplab1 commented May 15, 2026

Copy link
Copy Markdown
Contributor

@Anshjn1411 Please align with the latest recommendations made by me in another PR of yours: #2676

@sonarqubecloud

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.

6 participants