Aman-Mittal opened a new pull request, #803:
URL: https://github.com/apache/fineract-backoffice-ui/pull/803

   Closes #754, #778, #779.
   
   Three issues from the loan servicing backlog, all the same shape: a command 
the platform accepts, with no caller anywhere in `src/app`. Each was measured 
against a running `apache/fineract` before it was written, which is how four of 
the assumptions below turned out to be wrong.
   
   ## What landed
   
   **#778 — undo a re-age or a re-amortization.** Both now sit in the Actions 
menu behind a destructive confirmation that says the schedule reverts. Each 
appears only while its own transaction stands unreversed: the platform reverses 
the most recent one and an undone re-age keeps its row with `manuallyReversed` 
set, so "the loan has a reAge transaction" would keep offering an Undo that can 
only answer 403. The latest is read by id rather than by position — nothing in 
the payload promises an order, and the date is the value date, which a 
back-dated correction can place before an earlier entry.
   
   This also settles `reAmortize`, which `command-permissions.ts` had left 
deliberately unmapped because the first probe read it as "400 at one shape, 403 
at the form's". Re-probed against the endpoint the form posts to, with a role 
holding `REAMORTIZE_LOAN` and nothing else, the platform answers 404 
`error.msg.loan.id.invalid` — past authorisation, failing only on the absent 
loan. Three commands remain unmapped.
   
   **#779 — post and adjust buy-down fees and capitalized income.** Both 
postings are in the Actions menu, shown only when the loan reports the 
capability, because a product that did not enable the feature refuses the 
command outright. The buy-down fee tab gets a row action for the adjustment, 
addressed by the row's `transactionId`.
   
   **#754 — pay, waive and adjust a charge on a loan, savings account or 
client.** Per-row actions on all three charge lists, each gated on its own code 
and on the charge's state. Before this a charge could be added and deleted and 
nothing else: deletion is refused once the charge has been paid or partly paid, 
and it leaves no record that a fee was forgiven.
   
   ## Four places the issues were wrong about the platform
   
   **The deferred-income adjustments are not commands at loan level.** #779 
expected `POST /loans/{id}/transactions?command=buyDownFeeAdjustment`. The 
platform answers `error.msg.query.parameter.value.unsupported` on `command` 
there — the #268 discriminator for a command it does not recognise at all. Both 
are recognised on the per-transaction URL, and naming one switches the 
permission as well as the operation: a role holding `BUYDOWNFEEADJUSTMENT_LOAN` 
is refused the bare POST, while `ADJUST_LOAN` is authorised the bare POST and 
refused the named command. So the adjustment belongs on the transaction detail 
dialog, and the command, its code and its wording travel together as one value.
   
   **A loan charge adjustment takes `amount` and literally nothing else.** 
Sending the `locale` and `dateFormat` that every other command on that endpoint 
requires makes this one a 400. The same quirk as the Working Capital charge 
adjustment, so this is now the second time.
   
   **Pay is narrower than having something outstanding.** The platform accepts 
it only for a charge whose payment mode is account transfer: "This loan charge 
cannot be paid as the charge payment mode is not account transfer". Regular is 
the default, so gating Pay on the outstanding amount alone would have put a 
button that can only fail on nearly every row. The payload carries the 
platform's own answer as `chargePayable`, which is what the row asks.
   
   **Inactivate is narrower still.** Allowed only for recurring charges — 
`charge.inactivation.allowed.only.for.recurring.charges` — so an annual or 
monthly fee and nothing else, and only while the charge is active. The payload 
carries `isActive`, which the generated response type does not declare.
   
   ## One thing that could not be delivered
   
   `PUT /loans/{loanId}/transactions/{transactionId}`, which the API document 
titles **"Undo a Waive Charge Transaction"** and which #754 asks for, **answers 
500 on every call.** Measured with an empty body, with the ids in the body, 
with a transaction date and amount, and with each of `?command=undo`, 
`?command=waive` and `?command=undoWaiveCharge`. The platform log shows 
`java.util.NoSuchElementException: No value present` — an `Optional.get()` on 
an empty optional inside the handler — so the request never reaches a business 
rule.
   
   The code for it is here and tested: the adapter method, the dialog control, 
and the gate on `UNDO_WAIVECHARGE`, which was probed and is authorised by 
neither `ADJUST_LOAN` nor `WAIVE_LOANCHARGE`. The button is not shipped, behind 
`UNDO_WAIVE_CHARGE_AVAILABLE` in `loan-view.component.ts`, because a control 
whose every click ends in an error toast is the dead-end control of #691 and 
#751 reached from a third direction. `e2e/charge-servicing-backend.spec.ts` 
asserts the 500, so the day the platform is fixed a test says so and that 
constant is the one line to flip. **This needs an ASF Jira issue against 
Fineract.**
   
   ## Where the forms live, and why
   
   Two of the three loan charge commands need a value before they can be 
issued, and they are issued from list rows that have no form on them. Rather 
than six near-identical dialogs or three forms, `ConfirmDialogComponent` gained 
optional fields and `DialogService` gained `prompt`.
   
   That is where they had to go. ADR 0005 forbids a new file importing 
`@ionic/angular/standalone`, there is no form primitive in `src/app/ui` yet 
(#287, #684), and `eslint-suppressions.json` may only shrink — so the fields 
went where the buttons already were. A dialog with fields dismisses with the 
values and a cancelled one still with `false`, which is what lets `confirm` 
keep reading `=== true` while `prompt` cannot mistake a cancel for an empty 
answer. Required fields are checked before the request rather than after, so a 
blank amount is a disabled button instead of a validation toast.
   
   This is the second batch to run into the missing form primitives. It is 
worth scheduling #684.
   
   ## RBAC
   
   Eighteen command-to-code pairings, each probed with a role holding exactly 
one permission, and the three portfolio write codes — `UPDATE_LOAN`, 
`UPDATE_SAVINGSACCOUNT`, `UPDATE_CLIENT` — probed against all eighteen. Each 
authorises none of its own entity's commands, which is #691 and #777 one layer 
down. `e2e/loan-servicing-rbac-backend.spec.ts` re-measures the whole diagonal 
on every backend run.
   
   The Actions menu items now bind their codes from `LOAN_COMMAND_PERMISSIONS` 
instead of repeating them as literals, so the button and the route guard read 
the same map and cannot drift.
   
   ## Also worth noting
   
   #506 is already implemented on `main` — refund by cash and charge refund 
both landed, and the stale comment the issue asks about has been settled 
(`chargeRefund` is in `NO_TEMPLATE_TYPES`). It can be closed.
   
   `permission.guard.test.ts` used `reAmortize` as its example of a 
deliberately unmapped command; it is mapped now, so the example moved to one 
whose probe is still inconclusive.
   
   ## Verification
   
   - **2320 unit tests / 301 files**, all passing.
   - **11 backend e2e** against `apache/fineract:latest` across the two new 
specs, in 1 minute: the full permission diagonal, a loan charge waived and read 
back from the ledger, the adjustment, the Pay gating, a savings charge paid, 
and the active flag that gates Inactivate.
   - All repo checks, `eslint` with **no new suppressions**, `prettier 
--check`, `typecheck:e2e`, production build.
   
   Two pre-existing flakes under full-suite parallel load, both passing in 
isolation (`client-form.component.test.ts`, 
`loan-delinquency-actions.spec.ts`), and `loan-schedule-type-gating.spec.ts` 
fails on `main` in the mocked project in this environment — its login does not 
survive the navigation — so no new assertions were added there.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to