Skip to content

Add timesheetTotals endpoint for hours by day or week - #15

Draft
turegjorup wants to merge 4 commits into
developfrom
feature/timesheet-hours-totals
Draft

Add timesheetTotals endpoint for hours by day or week#15
turegjorup wants to merge 4 commits into
developfrom
feature/timesheet-hours-totals

Conversation

@turegjorup

@turegjorup turegjorup commented Jun 30, 2026

Copy link
Copy Markdown

Link to ticket

https://leantime.itkdev.dk/#/tickets/showTicket/7893

Description

Adds a sanity-check endpoint so API consumers can detect when their synced timesheet data has drifted from the source.

GET/POST /apidata/api/timesheetTotals returns SUM(hours) grouped by day or week:

  • groupBy: day (default) or week (ISO-8601, Monday based).
  • from / to: optional unix timestamps bounding workDate.
  • workYear / workMonth: optional. Select a year or month on workDate, resolved to a half-open range (>= start AND < end). If only workMonth is given, the current year is assumed.
  • projectIds: optional, mirrors the timesheets endpoint's filter.

It applies the same row filters as the timesheets endpoint (entries without hours excluded, projectIds via the ticket join), so the totals reconcile against the synced data. Each result is {period, hours, count}hours is rounded to 2 decimals and period is YYYY-MM-DD (day) or YYYY-Www (week). The consumer sums its own synced hours per period and compares; a mismatch flags a period to re-pull.

SUM(hours) is the metric that catches value drift (e.g. an hours change that did not sync), which a row count cannot. Broader reconciliation (content checksums, other entity types) is intentionally left for later.

Implemented as a plain grouped SQL aggregate (cheap — single scan, intended for ~daily off-peak calls). The workYear/workMonth filter uses a half-open range rather than LIKE/YEAR()/MONTH() so it doesn't wrap the column in a function and stays index-friendly.

Steps to reproduce

Not a bug fix. To verify:

  1. Call the endpoint: curl '.../apidata/api/timesheetTotals' -H 'x-api-key: lt_...' -d '{"groupBy":"day","workYear":2025,"workMonth":6}'.
  2. Cross-check against SELECT DATE(workDate), ROUND(SUM(hours),2), COUNT(*) FROM zp_timesheets WHERE hours IS NOT NULL AND workDate >= '2025-06-01' AND workDate < '2025-07-01' GROUP BY DATE(workDate) — output matches row for row.
  3. Confirm {"workMonth":6} alone scopes to June of the current year.

Checklist

  • My code is covered by test cases.
  • My code passes our test (all our tests).
  • My code passes our static analysis suite.
  • My code passes our continuous integration process.

New /apidata/api/timesheetTotals endpoint returns SUM(hours) grouped by day
or week (groupBy), filterable by workDate range (from/to) and projectIds. It
applies the same filters as the timesheets endpoint so consumers can sum their
synced hours per period and reconcile against the source to detect drift.
@turegjorup turegjorup self-assigned this Jun 30, 2026
@turegjorup
turegjorup requested a review from tuj June 30, 2026 20:22
@turegjorup
turegjorup marked this pull request as draft June 30, 2026 20:22
Accepts workDate as "2026" or "2026-06" and matches timesheet.workDate by
prefix, so consumers can scope totals to a year or month without computing
unix timestamps. Invalid values are ignored.
Filter timesheetTotals by workYear and/or workMonth, resolved to a half-open
workDate range (>= start AND < end). When only workMonth is given the current
year is assumed. The range form avoids wrapping workDate in a function and is
index-friendly if a workDate index is ever added.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants