Chrono is a multi-tenant time-tracking and leave-management application — a Laravel API behind a Vue single-page app where teams log hours against activities, request and approve leave, and let the system do the date arithmetic. The hard parts aren't the forms; they're the rules underneath: counting real working days, keeping every leave balance reconcilable from signed transactions, and locking finished timesheet periods so the past stays the past.
Chrono started from a simple frustration: timesheet and leave tools either store balances as mutable counters that quietly drift, or treat “five days off” as five calendar days regardless of weekends and holidays. So the design choice underneath the whole app is that the interesting numbers are never stored — they’re computed. A leave balance is an allocation plus the signed sum of a transaction ledger. A leave duration is a walk over a date range that skips weekends and the right set of non-working days. Both can be re-derived from source at any time, which makes them auditable instead of mysterious.
Above that domain core sits a fairly ordinary, deliberately boring shape: a Laravel 13 API secured with Sanctum, and a Vue 3 TypeScript SPA that talks to it through TanStack Query, packaged as an installable PWA. It’s multi-tenant from the first migration — organisations scope everything, roles gate access, and people are onboarded through tokenised, expiring invitations. The genuinely opinionated parts — working-day math, the balance and carryover engine, period locking, and an idempotent hand-off to a separate ticketing service — are isolated in a thin services layer so the controllers stay thin and the rules stay testable.
The live spine — time entry, the leave workflow, balances and the working-day calendar — is built and working end to end. The period-locking layer is modelled and migrated, with enforcement still being wired into the write path.
Each organisation picks how its people record time — start/end ranges or plain durations — and the entry form and validation adapt to that mode. A batch endpoint accepts a whole week of entries in one request so the client can save a grid in a single round trip.
Members file leave against a leave type for a date span; admins approve or reject from an org-wide queue. On approval the system expands the span into actual working days and records the deduction, so the request and the balance never disagree.
A user's balance for a leave type in a year is the year's allocation plus the signed total of every leave transaction — accruals positive, usage negative. Nothing is mutated in place, so any balance can be re-derived and audited from its transaction history.
The day calculator walks a date range skipping weekends and any non-working day that applies to the user — their organisation's custom closures plus their country's public holidays, resolved by country code. The same map drives both deductions and the calendar UI.
Every record is scoped to an organisation. Access is role-based, onboarding runs through tokenised invitations with an expiry, and period locks per org-and-month exist to freeze submitted timesheets — with explicit unlock records when a closed period legitimately needs reopening.
Feedback submitted in Chrono is posted to a separate ticketing app (TickIt) over an authenticated HTTP call. The request carries a client-supplied Idempotency-Key, so a retried submission can never create a duplicate ticket across the two systems.
"How many days is this leave?" has no fixed answer — it depends on weekends, the organisation's own closures, and the public holidays of the country that organisation operates in. Getting it wrong silently corrupts every balance downstream.
A single LeaveDaysCalculator resolves the applicable non-working days once — org-specific plus country-code-matched public holidays — then walks the range skipping weekends and that set. The exact same map feeds the calendar UI, so the number a user sees and the number deducted are computed the same way.
A stored "days remaining" counter drifts the moment an approval, adjustment or correction is missed, and there's no way to explain how it got there.
Balances are never stored. Each is computed as the year's allocation plus the signed sum of every leave transaction for that user and type, with decimal amounts for half-days. The ledger is the source of truth, so any balance can be re-derived and audited from its history.
Days carried over from last year shouldn't live forever — they expire on a date, and only the days not yet used should still count.
Carryover is held on the allocation with an expiry date; the service returns zero once that date passes, and otherwise nets the carried amount against the year's usage so only genuinely unspent days remain — clamped so usage can never push it negative.
Chrono's feedback feeds a separate ticketing service over the network. A dropped response or a user double-click could otherwise file the same complaint twice, in a system Chrono doesn't own.
The feedback endpoint requires an Idempotency-Key (validated to a strict format), then forwards it as a header on the Bearer-authenticated POST to TickIt's integration API. Retries reuse the key, so the receiving side can collapse duplicates; connection failures and 5xx responses are raised as distinct, retryable errors rather than swallowed.