Dentolize · Accounting Module Walkthrough
On this pageBlocking — feature doesn't work at allHigh — silently wrong or misleading behaviorMedium — platform parity gaps (test both platforms separately)Low — unused scaffolding, dead code, minor inconsistenciesSuggested test focus areas

For Quality

This page is the itemized list of everything our code review turned up as broken, inconsistent, unfinished, or platform-asymmetric — organized by severity so test planning can prioritize. Every item was verified against the actual code at the file/line level (referenced in the relevant Feature breakdown page), not inferred from the PR description.

Blocking — feature doesn't work at all

  • Hourly leave requests cannot be submitted. Leave types marked hourly: true (Permission, Mission) require start/end times server-side, but the mobile request form has no time fields and the client mutation doesn't send them. A clinic can configure the leave type but no one can request it. See HR & Leave Management.

High — silently wrong or misleading behavior

  • Bank reconciliation "matched" checkbox does nothing. Ticking a statement line as matched never affects whether the reconciliation is marked RECONCILED/DISCREPANCY — only the frozen statement-balance-vs-book-balance comparison does. A reconciliation can complete with zero lines matched. Test that this doesn't get relied on as a real control. See Cash, Banking & Payables.
  • Fixed assets always book to Accounts Payable, never Cash, on acquisition. Neither the mobile nor web "add asset" form collects treasuryId, so every acquisition posts as if bought on credit, even if paid cash on the spot. Disposal proceeds have the same gap. See Fixed Assets & Depreciation.
  • Tax Codes don't affect any real posting. JournalLine.taxCodeId is never set by any resolver, and no UI lets a user pick a tax code on a line. VAT actually posts through the separate account-role system. Editing a tax code's linked accounts is currently cosmetic. See Tax, Inventory Valuation & Healthcare Reports.
  • Audit Log covers a fraction of what it should. Only journal-entry lifecycle actions, fiscal-period locks, and (newly) expense-claim actions are recorded. Chart-of-accounts edits, account-role remapping, tax code changes, responsibility-center/responsibility-center-mapping/allocation-rule changes, currency changes, and automation-setting changes leave zero trace — despite the data model's own comment naming Account and TaxCode as tracked entities. See Setup & Foundations.
  • Expense claim approval has no self-approval guard, unlike manual journal approval — a user with the right permission can approve their own submitted claim. See Cash, Banking & Payables.
  • Allocation Rules have no guard against a self-referencing target. addAllocationRule/editAllocationRule don't check whether a target responsibility center is the same as the rule's own source center — worth testing what happens if someone tries to allocate a cost center's overhead back onto itself. See Setup & Foundations.
  • The "no delete, only deactivate" pattern is inconsistent across the three responsibility-center-adjacent models. ResponsibilityCenter itself can only be deactivated, never deleted — but ResponsibilityCenterMapping and AllocationRule both genuinely hard-delete. Worth confirming this asymmetry is intentional before treating "no delete" as a universal module convention in test cases.

Medium — platform parity gaps (test both platforms separately)

  • Mobile has no manual-journal draft/submit-for-approval/reject UI. Only direct posting works on mobile; the full lifecycle is web-only. Mobile's "approve" action on an entry only covers post-hoc sign-off of an already-posted entry, never the pending-approval → posted transition.
  • Mobile mislabels pending-approval journal entries as "Draft" in the list view (its status map has no PENDING_APPROVAL entry, and there's no tab to filter for it), even though the details screen shows the correct status.
  • Report export is web-only — no export capability exists anywhere in the mobile accounting module, for any report.
  • Trial Balance, Statement of Account, and Journal Entries cannot be exported on any platform — no backend case exists for these report types even via a direct API call.
  • Currency management is web-only (addCurrency); editCurrency has a client mutation file but is unused by any screen on either platform — a currency can never be renamed or deactivated through the UI once added.
  • Company-wide accounting controls (enforceOpenPeriodPosting, GL drift alert settings, inventory costing method) are only editable on web; mobile's Automations screen only exposes the per-event auto-post toggles.
  • periodCloseChecklist (a pre-close warning about draft/pending journals in the period) is checked on web before closing a period, but never queried on mobile — a mobile user gets no warning.
  • Fixed Assets nav/permission mismatch is now mobile-only. Web gates the "Fixed Assets" tab on viewReports, matching the read permission; mobile still gates its menu entry on manageFixedAssets only, so a mobile view-only reporting user can't reach a screen they're authorized to read.

Low — unused scaffolding, dead code, minor inconsistencies

  • DebitNote.appliedAmount and ExpenseClaim.receiptUrl are modeled and exposed via GraphQL but never written by any resolver or UI.
  • Attaching a file to a journal entry has no UI anywhere on either platform, though the backend mutations and a client file exist; only already-attached files render (read-only).
  • JournalStatus has no APPROVED value in the schema, but both apps carry a dead "Approved" status-map entry, filter tab, and (web) an unreachable code branch referencing it.
  • packages/server/src/accounting/periodGuard.js is a fully-built, unit-tested, but entirely unused duplicate of the period-lock logic that actually runs elsewhere (posting/engine.js). Not a functional risk, but confusing if someone debugs period-lock behavior by reading the wrong file.
  • PayrollSettings.accrueBaseSalary and AccountingSettings.glDriftAlertsEnabled/glDriftToleranceMinor exist but nothing reads/branches on some of them meaningfully in the shipped flows — verify current behavior before testing around these settings.
  • Mobile's account-creation form can't create a CONTRA_ASSET/CONTRA_LIABILITY/CONTRA_REVENUE account, or an EXPENSE account with sub-type INTEREST_EXPENSE — both exist in the schema but are missing from the form's option lists.
  • editResponsibilityCenter only rejects a direct self-parent, not a longer parent-child cycle (unlike editAccount, which walks the full ancestor chain) — a multi-step responsibility-center cycle is theoretically possible to create.
  • The payments/expensePayments list filters were not migrated to the new Category system in this PR (still read the legacy company.paymentTypes JSON) — an intentional partial migration, not a regression, but worth knowing when testing filter consistency across list screens.
  • CategoryType values TAX, USER, ASSET, PATIENT, ROOM exist in the schema with no reachable UI on either platform — don't file "missing category type" as a new bug, it's known scope-for-later.

Suggested test focus areas

  1. Debit/credit balance enforcement end-to-end: try to post an unbalanced manual entry on both platforms, confirm both reject it; confirm the balance re-check at approval time catches drift between submission and approval.
  2. Fiscal period locking: attempt to post into a soft-closed period without isAdjustment, into a hard-closed period, and confirm a hard-closed period genuinely cannot be reopened.
  3. Segregation of duties: blockSelfApproval is off by default, so out of the box a user can approve their own manual journal — confirm that's the actual default behavior, then turn the setting on and confirm it genuinely blocks self-approval on a manual journal. Either way, confirm expense-claim approval never blocks self-approval — there's no equivalent setting for it at all (see above).
  4. Idempotency of automated postings: re-run the same operational action (payment, recurring journal occurrence, doctor payout inside a payroll run) and confirm it never double-posts — this is a stated design goal throughout the posting engine and worth spot-checking rather than trusting blindly.
  5. Cross-platform parity: for every screen you test on mobile, check whether the equivalent web screen has more capability (very often it does, per the list above) and vice versa.