openIMIS frontend module for openimis-be-ledger_py's double-entry
accounting/ledger domain, built against the official specs shipped in this
repository under specs/001-ledger-double-entry-accounting/ (spec.md,
plan.md, research.md, data-model.md, contracts/graphql-operations.md,
tasks.md).
- US1 — Browse the General Ledger:
GeneralLedgerPage, Relay-paginatedfetchLedgerEntrieswith FR-001 default-open-period behavior, baseline expandable/collapsibleLedgerEntryGridwith debit/credit/balance subtotals,LedgerFilters. - US2 — Party Sub-Ledger:
PartyLedgerPage,PartyPicker(unified AnalyticValue search), signed balance (utils/balance.js), period statement. - US3 — Funder Activity:
FunderActivityPage,FunderPicker, category breakdown, independent of any party filter. - US4 — Accounting Periods:
AccountingPeriodsPage, open/lock/close/ reopen lifecycle,utils/periodActions.js(chronological-order gating), rejection reasons surfaced verbatim (FR-009). - US5 — Manual Review Queue:
ManualReviewQueuePage(admin-only, even read),ManualReviewResolutionDialog(original entry read-only, FR-012),utils/correctingEntryCandidates.js. - US6 — Period Export:
PeriodExportPage, async export job +pollExportJobpolling loop,ExportJobStatus(provisional/final badge, download link, failure message). - US7 — Deployment Configuration:
DeploymentConfigurationPage(admin-only),ForwardOnlyModeWarningDialog(mandatory acknowledgement before an operating-mode change, FR-018).
All routes, the dedicated "Ledger" main menu, and every picker are wired in
src/index.jsx.
Deliberately not built (per the spec's own Clarifications — explicitly
deferred): the SVAR DataGrid/SVAR Filter enhancement pair. The baseline
expandable-table LedgerEntryGrid and plain LedgerFilters fully satisfy
the spec's baseline requirements on their own.
Unlike a purely-generated skeleton, this module has actually been installed, tested, and built in a sandbox:
npm test→ 55/55 tests passing across 14 suites (reducer, actions, utils, components, and a full-page smoke-test suite rendering all 7 pages against a real Redux store).npx vite build→ builds cleanly todist/index.es.js/dist/index.cjs.js.
Doing this surfaced and fixed 7 real bugs that a syntax-only check would have missed:
.babelrc(copied fromfe-invoice) setmodules: falsefor Vite, which silently broke Jest's ability to parse any file — fixed with a babelenv.testoverride.- A
file:../openimis-fe-core_jsdependency (a leftover from my own local testing) brokenpm installentirely — replaced with a properpeerDependency. fetchFunderActivityReportwas accidentally deleted by an earlier find-and-replace edit and only caught by the real Vite build, not by any test — restored.DEFUALT_DEBOUNCE_TIMEwas referenced byPartyPicker/FunderPickerbut never (re-)exported fromconstants.jsafter a rewrite — restored. 5–7. Missingreact-redux/lodash/redux-thunkdevDependencies, a test assertion that was flaky against MUICollapse's async unmount, and a test that queried a MUISelect'sMenuItembefore opening the dropdown.
An earlier draft of this module (before openimis-fe-ledger_js's real specs
were found on GitHub) was built purely from a hand-written plan, using
openimis-fe-invoice_js conventions (offset pagination, string-interpolated
GraphQL, 6 granular rights, entries joining Invoice's "Legal & Finance" menu).
Once the actual spec-kit specs were located and read in full, they diverged
on nearly every substantive point — see the table below.
| Aspect | First draft (wrong) | Actual spec (implemented) |
|---|---|---|
| Query root fields | ledgerEntry, ledgerEntryLine, partyLedgerReport, ledgerReplicationReviewItem |
ledgerEntries (Relay-paginated, embeds lines as legs), partyLedgerBalance, manualReviewQueue, exportSequences |
| Pagination | offset (formatPageQueryWithCount) |
Relay cursor (edges/node/pageInfo/totalCount) |
| GraphQL call style | string-interpolated (formatMutation) |
typed variables (graphqlWithVariables), per research.md §1 |
| Rights | 6 granular constants invented | 2: RIGHT_LEDGER_REPORTING, RIGHT_LEDGER_ADMIN (still provisional — backend has no RIGHT_ constants yet) |
| Manual review resolution | arbitrary status field | link to an already-posted correcting entry (same party+period) + note |
| Period export | mutation returns a direct download URL | async job (exportJob), polled via exportSequences until complete/failed |
| Main menu | joins Invoice's "Legal & Finance" menu | own top-level "Ledger" menu |
| Missing entirely | — | Deployment Configuration screen (US7), tree-row grid requirement (FR-023), unified party/funder search via AnalyticValue |
Full detail in specs/001-ledger-double-entry-accounting/contracts/graphql-operations.md
and research.md.
- Backend is a stub.
openimis-be-ledger_py's schema files are empty; everything here is built against the backend's own design-contract docs. Expect drift once the backend actually ships — this module cannot be integration-tested until then. - Right IDs are provisional (research.md §3) — coordinate with the backend team before shipping, to avoid colliding with another module's right ID.
- Journal filter is free text, not a picker — no
journalsreference query exists in the contract (only ajournal: Stringargument). - The
analyticValues(search, tagType)search query behindPartyPicker/FunderPickeris an assumption, not a literal contract — research.md §6 establishes the AnalyticValue-backed search exists but doesn't name the query. Adjust once the backend names it. utils/correctingEntryCandidates.js's party/period matching logic is an interpretation:LedgerEntryViewModelhas no flat party/period fields, so "the entry's party" is read as "any of its lines is tagged with that party."utils/periodActions.js's lock/close/reopen ordering rule is an interpretation of "chronological-order enforcement" (oldest-first for lock/close, newest-first for reopen) — a client-side hint only; the backend mutation response is always authoritative.
npm install
npm run build
npm run test