Aman-Mittal opened a new issue, #358:
URL: https://github.com/apache/fineract-backoffice-ui/issues/358
## Business value
An officer opens a dialog, accepts the date the application already filled
in, clicks Confirm — and the platform refuses it. The date they never touched
is wrong, and nothing on screen says so.
For roughly five and a half hours of every day, a branch working in a
timezone ahead of the tenant's sees actions fail for no reason it can act on.
The refusals are the confusing kind — *"Submitted on date cannot be after the
activation date"* — naming a date the user did not enter and cannot see. The
workaround is to open the picker and choose yesterday, which no one will
discover, and which is indistinguishable from data entry error when it shows up
later in an audit.
It also silently damages the ledger's meaning. Where the platform *accepts*
the browser's date, the record is stamped with a day the institution was not
working — a client activated "tomorrow", a transaction dated a day out. That is
worse than a refusal, because nothing fails.
This is not hypothetical. It reproduces on demand, and it is currently the
cause of an intermittent CI failure (see below).
## What is wrong
Fineract stamps and validates dates using the **tenant's** timezone —
`m_tenants.timezone_id`, which its seed data sets to `Asia/Kolkata`. The
application fills date fields from the **browser's** clock:
```ts
// e.g. src/app/features/centers/center-action-dialog.component.ts
date = new Date().toISOString();
// src/app/core/utils/date-formatter.ts — local components, deliberately
const day = String(d.getDate()).padStart(2, '0');
const month = MONTHS[d.getMonth()];
const year = d.getFullYear();
```
Between **18:30 and 24:00 UTC** a UTC browser and an `Asia/Kolkata` tenant
are a day apart, so the two disagree about what "today" is.
## Reproduction
With the e2e stack up and the wall clock between 18:30 and 24:00 UTC:
```bash
TZ=UTC npx playwright test e2e/center-servicing.spec.ts --project=backend -g
"activated"
```
The centre is created (Fineract stamps `submittedOnDate` as the *tenant's*
today), the UI offers the *browser's* today as the activation date, and the
platform answers:
```json
{
"httpStatusCode": "400",
"errors": [{
"userMessageGlobalisationCode":
"error.msg.group.submittedOnDate.after.activation.date",
"defaultUserMessage": "Submitted on date cannot be after the activation
date",
"parameterName": "submittedOnDate",
"args": [{ "value": "2026-08-16" }]
}]
}
```
The same run passes from a machine already in `Asia/Kolkata`, and passes at
any hour outside that window — which is exactly what makes it hard to see.
## Scope of the problem
This is not specific to centres. Every dialog that seeds a date from `new
Date()` is affected — activation, closure, transaction, disbursal, repayment
and the rest. Centres are simply where it surfaced, because activation is
validated against a date the platform stamped moments earlier.
## What to build
Default date fields from the **platform's** notion of today rather than the
browser's.
The application already displays a business date in the header, so the
platform's date is available to the client — that is the value these pickers
should open on. `GET /v1/businessdate` returns the configured business date
when the tenant has one; note it answers `[]` when none is configured, so a
fallback is still needed and it should be the tenant's current date, not the
browser's.
Suggested shape:
- A single service that answers "what is today, according to the platform",
with the browser's clock as a last resort.
- Date dialogs seed from it instead of `new Date()`.
- `formatDateToFineract` keeps formatting local components — the fix belongs
at the source of the value, not in the formatter, so that a date a user
genuinely picked is still sent as picked.
### Two things to get right
1. **Do not simply switch the formatter to UTC.** That trades one wrong
answer for another: a user who explicitly picks a date would then have it
shifted a day. The defect is in what the field is *seeded* with.
2. **A refused date must say which date was refused.** Today the dialog
closes and the record silently stays in its previous state; the platform's
message names a date the user never saw. Surfacing the platform's error is part
of this fix.
## Testing
- **Unit:** a dialog seeds its date from the platform date service, not `new
Date()`.
- **Unit:** with the platform a day ahead of the browser, the submitted
payload carries the platform's date.
- **Backend e2e:** the `TZ=UTC` reproduction above should pass on its own
merits, without the harness pinning the browser timezone.
## Relationship to the CI harness
`playwright.config.ts` pins the browser timezone to the tenant's so the
suite stops depending on the hour it runs at. **That makes the harness
deterministic; it does not fix this.** When this issue is resolved, that pin
should be removed — and its removal is a good confirmation that the fix is real.
## Getting started
- `src/app/core/utils/date-formatter.ts` — `formatDateToFineract`, and the
`FINERACT_DATE_FORMAT`/`FINERACT_LOCALE` constants
- `src/app/features/centers/center-action-dialog.component.ts` — a small,
clear example of the seeding pattern
- The business-date indicator in the application header, for where the
platform date is already read
- `npm test`, `npm run lint`, `npm run build`
--
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]