Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
15 KiB
15 KiB
Scratchpad — Time Entry Delegation
Decisions (confirmed)
- Billing/system admins should be able to enter time + approve/reopen as needed (RBAC-driven).
- Managers should be limited to team membership (only users in teams they manage).
- Do not allow adding/editing time for already invoiced periods/entries.
Current-State Findings (key file paths)
- Time entry type shape:
server/src/interfaces/timeEntry.interfaces.tspackages/scheduling/src/schemas/timeSheet.schemas.ts(timeEntrySchema)
- Time entry CRUD server actions (Next.js server actions used by UI):
packages/scheduling/src/actions/timeEntryCrudActions.ts- Notable issue:
saveTimeEntrycurrently forcesuser_idto the actor, preventing on-behalf and risking ownership corruption on update.
- Time sheet / period actions:
packages/scheduling/src/actions/timeSheetOperations.ts(fetchTimePeriods,fetchOrCreateTimeSheet)- These take a
userIdparam and do not currently enforce “self vs delegate” rules.
- Timesheet approvals:
packages/scheduling/src/actions/timeSheetActions.ts- Notable issue:
reverseTimeSheetApprovalcurrently sets status toSUBMITTED, but the UI is editable only forDRAFT/CHANGES_REQUESTED.
- Time Entry UI:
/msp/time-entrylist:packages/scheduling/src/components/time-management/time-entry/TimeTracking.tsx- Time period table:
packages/scheduling/src/components/time-management/time-entry/TimePeriodList.tsx - Timesheet UI:
packages/scheduling/src/components/time-management/time-entry/time-sheet/TimeSheet.tsx(+TimeSheetHeader.tsx)
- Billing usage of time entries (approved + not invoiced):
server/src/lib/billing/billingEngine.ts
Notes / Risks
- There appear to be parallel implementations for some timesheet flows:
- Next.js server actions in
packages/scheduling/src/actions/** - Service layer in
server/src/lib/api/services/TimeSheetService.ts - Plan: implement delegation in the server actions used by the UI first; avoid accidental divergence where possible.
- Next.js server actions in
- Manager approval dashboard currently accepts
teamIdsfrom the client; for manager-scoped access we must validate scope server-side (do not trust user-supplied team ids).
Commands
- Validate plan folder JSON shape:
python3 scripts/validate_plan.py ee/docs/plans/2026-01-31-edit-other-users-time-entries
Implementation sketch (high level)
- Add audit columns to
time_entries(created_by,updated_by) + types/schema updates. - Add a centralized authorization helper for “actor can access/modify subject’s time”.
- Harden all time entry/time sheet server actions to enforce owner-or-delegate and invoiced constraints.
- Add a subject user selector UI on
/msp/time-entrybacked by a server action returning eligible subject users. - Add “Reopen for edits” for approved, non-invoiced timesheets and make it transition to an editable status.
Progress log
- 2026-01-31: F001 — Added
time_entries.created_by+time_entries.updated_bycolumns via migrationserver/migrations/20260131120000_add_time_entries_actor_audit_columns.cjs(FK constraints added in F002). - 2026-01-31: F002 — Added tenant-scoped FK constraints for
time_entries.(created_by,updated_by)→users(user_id)via migrationserver/migrations/20260131120500_add_time_entries_actor_audit_fks.cjs. - 2026-01-31: F003 — Extended
ITimeEntrytypes to include optionalcreated_by/updated_byinserver/src/interfaces/timeEntry.interfaces.tsandpackages/types/src/interfaces/timeEntry.interfaces.ts. - 2026-01-31: F004 — Extended
timeEntrySchemato include optionalcreated_by/updated_byinpackages/scheduling/src/schemas/timeSheet.schemas.ts. - 2026-01-31: F005 — Added shared delegation helper
packages/scheduling/src/actions/timeEntryDelegationAuth.ts(assertCanActOnBehalf) to authorize self vs tenant-wide vs manager-of-subject access. - 2026-01-31: F006 — Implemented server-side manager-of-subject check via
teams.manager_id+team_membersjoin (exportedisManagerOfSubject). - 2026-01-31: F007 — Encoded delegation policy in
assertCanActOnBehalf(self ORapprove+ (read_allOR manager-of-subject)). - 2026-01-31: F008 — Added server action
fetchEligibleTimeEntrySubjectsinpackages/scheduling/src/actions/timeEntryDelegationActions.ts(self-only unless delegate; team-scoped for managers; tenant-wide forread_all). - 2026-01-31: F009 — Hardened
fetchTimePeriods(userId)inpackages/scheduling/src/actions/timeSheetOperations.tsto enforce delegation policy viaassertCanActOnBehalf. - 2026-01-31: F010 — Hardened
fetchOrCreateTimeSheet(userId, periodId)inpackages/scheduling/src/actions/timeSheetOperations.tsto enforce delegation policy viaassertCanActOnBehalf. - 2026-01-31: F011 — Hardened
fetchTimeSheet(timeSheetId)inpackages/scheduling/src/actions/timeSheetActions.tsby enforcing owner-or-delegate access viaassertCanActOnBehalf(prevents ID-guessing). - 2026-01-31: F012 — Hardened
fetchTimeEntriesForTimeSheet(timeSheetId)inpackages/scheduling/src/actions/timeSheetActions.tsby resolving the timesheet owner and enforcingassertCanActOnBehalf. - 2026-01-31: F013 — Hardened
fetchWorkItemsForTimeSheet(timeSheetId)inpackages/scheduling/src/actions/timeEntryWorkItemActions.tsby enforcing owner-or-delegate viaassertCanActOnBehalf. - 2026-01-31: F014 — Updated
saveTimeEntryinpackages/scheduling/src/actions/timeEntryCrudActions.tsto persisttime_entries.user_idfrom the requested subject (no longer forced to actor). - 2026-01-31: F015 — Prevented
saveTimeEntryupdates from changingtime_entries.user_idownership (canonical owner comes from existing row;user_idis removed from UPDATE payload). - 2026-01-31: F016 — Enforced delegation policy in
saveTimeEntryviaassertCanActOnBehalf(blocks creating/updating entries for unauthorized subjects). - 2026-01-31: F017 —
saveTimeEntrynow computeswork_date/work_timezoneusing the subject user’s timezone (resolveUserTimeZone(..., timeEntryUserId)). - 2026-01-31: F018 —
saveTimeEntrynow writes audit columns (updated_byon every save;created_byon insert) to capture the actor. - 2026-01-31: F019 — Added server-side time period boundary validation in
saveTimeEntry(requires entry start/end to fall within the associated timesheet’s time period; also enforces timesheet owner matches entry user). - 2026-01-31: F020 — Blocked updates to invoiced time entries in
saveTimeEntry(throws a clear error whentime_entries.invoiced = true). Deletes enforced indeleteTimeEntry(F022). - 2026-01-31: F021 — Reopen/reverse approval is blocked when any related
time_entries.invoiced = true(seereverseTimeSheetApprovalinpackages/scheduling/src/actions/timeSheetActions.ts). - 2026-01-31: F022 — Hardened
deleteTimeEntry(entryId)inpackages/scheduling/src/actions/timeEntryCrudActions.ts(enforces delegation viaassertCanActOnBehalfand blocks deletes wheninvoiced=true). - 2026-01-31: F023 — Hardened
submitTimeSheet(timeSheetId)inpackages/scheduling/src/actions/timeSheetOperations.ts(requirestimesheet:submitand enforces delegation viaassertCanActOnBehalfbefore submitting + updating entry statuses). - 2026-01-31: F024 — Hardened approval operations in
packages/scheduling/src/actions/timeSheetActions.ts: approvals now enforce delegation scope viaassertCanActOnBehalf(tenant-wide admins vs managers), and reject spoofedapproverId/managerIdparams. - 2026-01-31: F025 — Updated “reopen/reverse approval” (
reverseTimeSheetApproval) to set the timesheet + entries toCHANGES_REQUESTEDso the UI becomes editable. - 2026-01-31: F026 —
reverseTimeSheetApprovalnow enforces delegation authorization viaassertCanActOnBehalf(and rejects spoofedapproverId). - 2026-01-31: F027 — Added subject user selector to
/msp/time-entry(implemented inpackages/scheduling/src/components/time-management/time-entry/TimeTracking.tsx; defaults to self and only renders when multiple eligible subjects exist). - 2026-01-31: F028 — Switched subject selector to use
UserPickerand populate fromfetchEligibleTimeEntrySubjects. - 2026-01-31: F029 — Time period list now reloads from
fetchTimePeriods(subjectUserId)when the selected subject changes. - 2026-01-31: F030 — Timesheet page refresh after saving entries now re-fetches via the timesheet owner (
fetchOrCreateTimeSheet(timeSheet.user_id, ...)) so subject context is derived from the timesheet itself. - 2026-01-31: F031 — Timesheet header now shows “Time Sheet for {Subject}” and (when delegated) “Edited by {Actor}” via new props plumbed through
TimeSheetClient→TimeSheet→TimeSheetHeader. - 2026-01-31: F032 — Added “Reopen for edits” button to
TimeSheetHeaderfor APPROVED, non-invoiced timesheets when server precomputescanReopenForEdits(seeserver/src/app/msp/time-entry/timesheet/[id]/page.tsx). - 2026-01-31: F033 — Reopen flow now uses
ConfirmationDialogand shows toast success/error (including invoiced-block errors) inTimeSheetClient. - 2026-01-31: F034 — Timesheet client now forces
timeEntry.user_id = timeSheet.user_idwhen saving so delegated entry create/edit targets the subject reliably. - 2026-01-31: F035 — Improved client error handling so server-side “not editable” / “invoiced” constraints surface as user-visible toast messages (see
TimeEntryDialog.tsx). - 2026-01-31: F036 — Removed client-supplied team scoping from
fetchTimeSheetsForApproval; server now computes manager scope via joins (updatedManagerApprovalDashboardcall signature). - 2026-01-31: F037 — Hardened remaining time entry reads to prevent ID-guessing:
fetchTimeEntriesForTimeSheetandgetTimeEntryByIdnow enforce owner-or-delegate viaassertCanActOnBehalfinpackages/scheduling/src/actions/timeEntryCrudActions.ts. - 2026-01-31: F038 — Added unit/static coverage for delegation auth + invoiced constraints (see Test log + new tests under
server/src/test/unit/**). - 2026-01-31: F039 — UX: Bundled tickets cannot accept time entries; update ticket selection UI to show bundled tickets as disabled with a clear explanation (“log time on master ticket”).
Test log
- 2026-01-31: T001 —
python3 scripts/validate_plan.py ee/docs/plans/2026-01-31-edit-other-users-time-entries✅ - 2026-01-31: T010 — Added unit test
server/src/test/unit/migrations/timeEntriesAuditColumns.test.tsverifying migration addscreated_by+updated_bycolumns. - 2026-01-31: T011 — Extended audit migration unit test to verify tenant-scoped FK constraints for
created_by/updated_by. - 2026-01-31: T020 — Added type-level unit test
server/src/test/unit/types/timeEntryAuditFields.test.tsto assertITimeEntryexposes optionalcreated_by/updated_by. - 2026-01-31: T021 — Added schema unit test
server/src/test/unit/validation/timeEntrySchemaAuditFields.test.tsto ensuretimeEntrySchemaaccepts audit fields (present or omitted). - 2026-01-31: T030 — Added unit test
server/src/test/unit/scheduling/delegationAuth.test.tscovering self access for delegation auth. - 2026-01-31: T031 — Extended delegation auth unit test to cover tenant-wide admin access (
approve+read_all). - 2026-01-31: T032 — Extended delegation auth unit test to cover manager-of-subject access (managed team membership).
- 2026-01-31: T033 — Extended delegation auth unit test to ensure managers cannot access users outside their managed teams.
- 2026-01-31: T034 — Extended delegation auth unit test to ensure non-delegates are blocked from accessing other users.
- 2026-01-31: T040 — Added unit test
server/src/test/unit/scheduling/eligibleSubjects.test.tsverifying eligible-subjects returns self-only for non-delegates. - 2026-01-31: T041 — Extended eligible-subjects unit test to cover manager-scoped subject lists (reports in managed teams + self).
- 2026-01-31: T042 — Extended eligible-subjects unit test to cover tenant-wide admin subject lists (all internal users).
- 2026-01-31: T050 — Added static guard-wiring test
server/src/test/unit/scheduling/actionGuardWiring.test.tsassertingfetchTimePeriodscallsassertCanActOnBehalf. - 2026-01-31: T051 — Extended static guard-wiring test to cover
fetchOrCreateTimeSheetdelegation enforcement. - 2026-01-31: T052 — Extended static guard-wiring test to cover
fetchTimeSheetowner-or-delegate enforcement. - 2026-01-31: T053 — Extended static guard-wiring test to cover
fetchTimeEntriesForTimeSheetowner-or-delegate enforcement (bothtimeSheetActions+timeEntryCrudActionsvariants). - 2026-01-31: T054 — Extended static guard-wiring test to cover
fetchWorkItemsForTimeSheetowner-or-delegate enforcement. - 2026-01-31: T060 — Added static test
server/src/test/unit/scheduling/saveTimeEntryBehavior.test.tsassertingsaveTimeEntrypersistsuser_idfrom the subject user. - 2026-01-31: T061 — Extended static
saveTimeEntrytest to assert updates stripuser_idfrom the UPDATE payload. - 2026-01-31: T062 — Extended static
saveTimeEntrytest to assert audit columns are set from the actor (created_byon insert,updated_byon save). - 2026-01-31: T063 — Added unit test
server/src/test/unit/db/workDate.test.tsvalidating timezone-awarework_datecomputation around midnight. - 2026-01-31: T064 — Extended static
saveTimeEntrytest to assert time period boundary validation exists and returns a clear error. - 2026-01-31: T070 — Extended static
saveTimeEntrytest to assert invoiced entries are blocked from editing. - 2026-01-31: T071 — Added static test
server/src/test/unit/scheduling/deleteTimeEntryBehavior.test.tsto assert invoiced entries are blocked from deletion. - 2026-01-31: T072 — Extended static guard-wiring test to assert reopening is blocked when any timesheet entry is invoiced.
- 2026-01-31: T080 — Added static test
server/src/test/unit/scheduling/submitTimeSheetBehavior.test.tsasserting submit transitions timesheet + entries toSUBMITTED. - 2026-01-31: T081 — Added static test
server/src/test/unit/scheduling/approvalBehavior.test.tsassertingapproveTimeSheetenforces delegation and setsAPPROVED. - 2026-01-31: T082 — Extended approval behavior test to assert
bulkApproveTimeSheetsenforces manager scope viaassertCanActOnBehalf(no widening via spoofed managerId). - 2026-01-31: T083 — Extended guard-wiring test to assert reopen transitions to
CHANGES_REQUESTEDand UI treats that status as editable. - 2026-01-31: T090 — Added static UI test
server/src/test/unit/ui/timeTrackingSubjectSelector.test.tsasserting selector defaults to self and hides when no delegation options exist. - 2026-01-31: T091 — Extended static UI test to assert
UserPickeroptions come fromfetchEligibleTimeEntrySubjects. - 2026-01-31: T092 — Extended static UI test to assert subject selection drives
fetchTimePeriods(subjectUserId)+fetchOrCreateTimeSheet(subjectUserId, ...). - 2026-01-31: T093 — Added static UI test
server/src/test/unit/ui/timesheetHeader.test.tsasserting header contains subject + delegated actor messaging. - 2026-01-31: T094 — Added static UI test
server/src/test/unit/ui/reopenForEdits.test.tsasserting “Reopen for edits” gating (server-side) + confirm flow (client-side). - 2026-01-31: T100 — Added static regression test
server/src/test/unit/security/managerScopeRegression.test.tsto ensure manager scope cannot be widened via client-provided team IDs.