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

   Closes #756, #758, #761, #763.
   
   Four defects that are one subject: what a form sends, and what the screen 
does while it is
   sending or after it fails. Two of them move money to the wrong place. 
Everything below was
   measured against a running `apache/fineract:latest` before it was written, 
and both e2e tests
   were checked to fail without the fix.
   
   ## #756 — a double-click posted two repayments
   
   One double-click on the loan repayment form posted twice, both `200`: the 
borrower's balance and
   the GL each wrong by the payment, and someone had to notice and reverse one. 
Create Client made
   two clients the same way. Approve and disburse escaped only because the 
platform refuses the
   second one once the state has moved; a monetary command has no such accident 
to rely on.
   
   The forms do set an `isSaving` flag and bind it to `[disabled]`, which is 
not a guard — the
   second click, and a second Enter, land before the flag reaches the DOM. 
Ninety-nine components
   carry such a flag and one re-checks it in its handler, so this is fixed 
once, in the HTTP layer:
   
   - **identical mutating requests in flight are collapsed** — the second 
caller gets the first
     request's response and no second request is issued. The window is the 
first request's flight,
     not a timed cache, so two *deliberate* identical submissions stay two 
requests;
   - **every mutating request carries an idempotency key**, which this platform 
honours. Measured:
     two repayments with the same `Idempotency-Key` produced **one** 
transaction and returned the
     same `resourceId` twice, and two keyed `POST /clients` produced one client 
where the keyless
     pair produced two. So a duplicate that does leave the browser cannot 
double-post either.
   
   This is the application's half of what the existing `IDEMPOTENT` context 
token already describes.
   Nothing here opts commands into retrying — that stays a call-site decision.
   
   ## #758 — dates defaulted to the browser's today, and were refused
   
   `PlatformDateService` already existed for this, used by exactly one dialog, 
while five places
   read `GET /businessdate` for themselves. It is now the one source, and right 
about two things it
   had wrong:
   
   - **it asks whether the business date governs.** Those rows persist after 
the feature is switched
     off and stop being maintained — measured, a tenant with it disabled still 
reported
     `BUSINESS_DATE 2026-10-17` on the 18th, and `POST /businessdate` refused 
to move it with
     `business.date.is.not.enabled`. Screens reading it unasked seeded a day in 
the past; the header
     announced a business date that governed nothing;
   - **it no longer invents a timezone.** The fallback assumed `Asia/Kolkata` 
when none was
     configured — a date up to a day away from the platform's for any other 
tenant. Today now comes
     from the platform's own clock, read off the `Date` header every response 
already carries, with a
     configured `tenantTimezone` applied to *that* instant rather than the 
browser's.
   
   Worth recording: `GET /loans/template` reports `expectedDisbursementDate` as 
the business date,
   while `GET /clients/template` reports `activationDate` as the **system** 
date — which under a
   business date of the 10th is the 18th, and is refused. So the client form 
cannot simply trust its
   own template, and the shared source is consulted either way.
   
   Seven forms now seed from it (client, loan, savings account, both deposit 
accounts, group,
   centre), and four screens read the business date through it instead of 
fetching it themselves.
   
   ## #761 — every savings account was opened at 0%
   
   The Interest Rate field started at `0` and never moved when a product was 
chosen, and the form
   sent it on every submit. Fineract reads a rate in the request as an 
account-level **override**,
   so a 5% product opened an account paying nothing and nobody finds out until 
interest posting.
   
   The rate now comes from the product the user picked, out of the `GET 
/savingsproducts` list the
   form already loads — measured, a product configured at 3.75% reports 3.75 
there, so no extra
   request is needed. With no rate known the field is **omitted**, which is how 
the platform is
   asked to apply the product's own rate, and a rate the user types still 
stands, including a
   deliberate 0%.
   
   (The deposit product lists are the opposite case: they report `0` for every 
product because a
   term deposit's rate lives in its chart. Same field name, opposite meaning.)
   
   ## #763 — a failed savings load rendered an empty page
   
   The view wrapped its template in `@if (account())` and reported failure 
through a toast that had
   already gone, so an outage left the breadcrumb and nothing else. It now uses 
the shared
   load-error state with a Try again that re-reads, a separate branch for a 404 
or 403 where
   retrying cannot succeed, and a spinner while the read is in flight. The 
deposit account view had
   the same gap — it even held a `hasError` boolean no part of its template 
read — and is fixed with
   it.
   
   ## Verification
   
   - **2477 unit tests / 308 files**, green in UTC and in `America/New_York`; 
`test:mfe` green
   - **new backend e2e**: a double-click on the repayment form issues one POST 
and leaves one
     transaction; a client is created once; and with the browser clock a day 
ahead, Create Client
     submits with untouched defaults. Each was run with its fix removed and 
fails then — 2 requests,
     and a form that never leaves `/clients/create`
   - mocked and date-sensitive backend slices green; `lint`, `lint:prune`, 
`prettier --check`,
     `check-api-surface` and the translation check all exit 0
   
   ## Not covered
   
   The remaining ~35 `new Date()` seeds are dialogs, report filters and charge 
due dates rather than
   the create and submit paths; they now have a shared source to move to. 
`client-form.component.test.ts`
   has a pre-existing 10s `beforeEach` budget that its Ionic wizard exceeds 
when the machine is
   busy — it fails on a loaded machine on `main` as well as here, and passes 
idle; worth its own
   issue.


-- 
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