Aman-Mittal opened a new issue, #653:
URL: https://github.com/apache/fineract-backoffice-ui/issues/653
## Summary
The generated OpenAPI client is derived from a spec that under-describes
several endpoints, so a generated type and the payload it describes disagree.
The application absorbs the difference at each call site instead of once, and
the cost is now measurable: **35 `as unknown as number[]` casts and 35
`formatArrayDate(...)` display-time conversions**.
Two distinct disagreements, both verified against a running
`apache/fineract:latest` (`sha256:c1d4aee9`, created 2026-10-01).
### 1. Dates typed `string` arrive as `[year, month, day]`
`GET /offices` declares:
```ts
export interface GetOfficesResponse {
openingDate?: string; // ← spec says string
// …
}
```
and returns:
```json
{"id": 1, "name": "Head Office", "externalId": "1",
"openingDate": [2009, 1, 1], "hierarchy": "."}
```
Nine generated models declare an `openingDate?: string`. The application
already knows the type is wrong — `core/utils/date-formatter.ts` exists
specifically to cope:
```ts
/** Formats a Fineract array date (`[year, month, day]`) into a `YYYY-MM-DD`
string… */
export function formatArrayDate(value: unknown): string
```
Note the parameter is `unknown`, because the declared type cannot be used.
Fixtures cast in the other direction:
```ts
openingDate: [2026, 6, 16] as unknown as number[], //
offices-list.component.test.ts:61
```
So the mismatch is handled, but 35 times, at display time, in a helper that
returns `'-'` on anything unexpected — which means a shape change upstream
degrades to a dash on screen rather than a failure anywhere.
### 2. Fields the payload sends and the type does not declare
`GET /offices` returns `parentId` and `parentName`. `GetOfficesResponse`
declares neither; it has `allowedParents?: Array<GetOfficesResponse>` instead.
`GET /glclosures` returns `allowedOffices`, also undeclared.
A component reading `office.parentId` therefore only compiles where the
value has flowed through an untyped context. That is the same mechanism behind
the `isClosed` defect in #651: the closures list bound `closure.isClosed`
through an untyped `appCellTemplate` context, no such field exists in the
payload, and every closed period rendered as **"Open"** with `strictTemplates`
on and unit tests passing.
## Why this is worth fixing centrally
These are not generator problems, so ADR 0001 does not cover them — the
method names are stable and correct. They are *spec* problems, and the spec is
synced automatically by ADR 0002, so the disagreement arrives without a commit
here. `scripts/check-api-surface.mjs` does not see them either: it records
which operations are *called*, and every operation here exists and works.
What makes the current arrangement expensive is that the generated response
type is the model components bind into templates, so each of the ~470 files
that imports the client carries its own share of the correction. A conversion
that lives in one mapper is tested once and cannot be forgotten at the 36th
call site.
## Suggested fix
Follow the boundary ADR 0006 establishes in #651, one domain at a time,
starting where the leverage is highest:
| Domain | Consumers | What the mapper would settle |
| ------ | --------- | ---------------------------- |
| Offices (`OfficesService`) | 40 files, 17 using `GetOfficesResponse` |
`openingDate` array → ISO string; declare `parentId`/`parentName` |
| Clients | `client-form`, KYC | array dates |
| Loans | `loan-view`, `loan-form`, transactions, guarantors, rescheduling |
array dates |
An `Office` model exposing `openingDate: string | null` removes the cast and
the `formatArrayDate` call from every office screen, and makes an upstream
change to that field a failing mapper test rather than a dash in a table.
## Alternative considered
**Fix the spec upstream** so `openingDate` is typed as it is sent. That is
the right long-term answer and worth raising in FINERACT, but it does not help
today: the spec is upstream, the sync is automatic, and the application still
needs to work against the current one. The mapper is also not wasted if the
spec is later corrected — it becomes a one-line passthrough.
**Keep using `formatArrayDate` at display time.** It works, and 35 call
sites prove it. What it does not do is give the shape a single tested home, or
let a type tell you when it changes.
## Scope
Not urgent — nothing is visibly broken by the dates today, because
`formatArrayDate` handles them. The undeclared-field half is different: that is
how #651's "Open" bug happened, and it is silent.
--
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]