Dentolize · Expense & Salary Date Permissions Walkthrough
On this pageCore scenarios to testPermission-groups screenEdge cases worth deliberate coverageRegression watch

For Quality

Core scenarios to test

For each of New Expense, Edit Expense, Add Expense Payment, Edit Expense Payment, Bulk Expense Payment, New Salary Adjustment, Edit Salary Adjustment — on both clinic-web and clinic-mobile:

ScenarioPermissionExpected
Create new recordMissingDate field disabled in UI; saved record is dated today, regardless of any date value sent
Create new recordGrantedDate field enabled; saved record uses the picked date
Edit existing recordMissingDate field disabled in UI; saved record's date is unchanged from before the edit
Edit existing recordGrantedDate field enabled; saved record uses the newly picked date
Owner (DO_ALL)N/AAlways behaves as if granted, regardless of the specific permission checkbox

Permission-groups screen

  • Change Creation Date checkbox only renders for the Expenses and Salary

Hub rows on the Settings tab — confirm it's absent elsewhere.

  • Checkbox is disabled until Add New is checked for that row.
  • Unchecking Add New after Change Creation Date was checked — confirm

the created-at checkbox clears too (don't leave an orphaned permission granted with no add access).

  • Checking Change Creation Date without Add New should be impossible

through the UI — worth a direct-request/API check too, since permission checks live server-side and the UI restriction alone isn't the security boundary.

Edge cases worth deliberate coverage

  • **Editing an unpaid expense and marking it "Paid" in the same edit, without

the permission.** The expense's own date stays unchanged (or server default if it had none), but the new payment row created by that edit falls back to today, not the expense's date — see editExpense.js:161 (createdAt: expenseDate || dayjs().toDate()). Confirm this is the intended behavior, since it means the expense and its just-created payment can end up on different dates in this specific path.

  • Expense payments used to be owner-only (doAll); they're now gated by

EXPENSES_CREATED_AT. Test that a non-owner with only that permission (and not DO_ALL) can now set an expense payment's date — this is a deliberate widening, confirm it isn't accidentally still blocked somewhere, and confirm a non-owner without the permission still can't.

  • Bulk expense payments (addExpensesBulkPayment) share the exact same

permission as single payments — verify the bulk-pay flow respects it too, not just the single-payment dialog.

  • Salary adjustment tied to a lab-order expense: editing an expense that

has a linked salaryAdjustment recalculates the adjustment's amount (unrelated to this PR) — verify that recalculation isn't affected by the date-permission changes, since both paths touch the same resolver.

  • Cross-permission independence: a group with EXPENSES_CREATED_AT but

not SALARY_ADJUSTMENT_CREATED_AT (or vice versa) should show one form enabled and the other disabled — the two permissions must not leak into each other.

  • Server-side enforcement, not just UI: since the UI disables the field

but the permission check is server-side (getPermittedDate / getCreatedAt), verify that a crafted GraphQL request with a date / createdAt argument from an unpermitted user is still ignored server-side — don't rely on manual UI testing alone for this.

  • Translations: the tooltip/help string (app.noCreatedAtPermission) was

added to all locale files (ar, ckb, el, en, fr, it, ku, nl, pl) — spot-check at least the RTL locale (Arabic) renders correctly in the tooltip.

Regression watch

  • Appointments and patient-record "created at" permissions

(APPOINTMENTS_CREATED_AT, PATIENTS_DETAILS_CREATED_AT, etc.) are untouched by this PR but use the same helper pattern — a shared-helper bug here could regress those too. Worth a smoke test on at least one of them.

  • The Group.js tab-to-column mapping was reindexed

(packages/clinic-web/src/components/dashboard/settings/Groups/Group.js:235) — confirm no other tab's column set shifted unexpectedly (the DHS Settings tab column index was untouched — columns[12] — but worth a quick check that every tab still renders its intended columns).