9.6 KiB
Domain model
Schedules vs assignments
A schedule describes when a duty exists and how its initial assignee is chosen. An assignment is the actual duty on a concrete date. Once an assignment exists, it is not recalculated from the schedule.
This distinction is intentional: editing a rotation must not retroactively change an already published month.
Seeded schedules
Seeded names are Portuguese (Noite, Almoço - Sábados, Almoço - Domingo); the slugs stay night, saturday-lunch and sunday-lunch.
Night (Noite)
- Exists every day (
MondaythroughSunday). - Type:
fixed. - Configured once as a repeating monthly pattern: one person per calendar day of the month (1 to 31), configured on
/admin/schedules/<night id>. - Generation fills a missing assignment with the person configured for that day of the month. Days 29 to 31 only apply to months that have them.
- There is no per-month configuration for Night: the same pattern is used every month.
- Existing assignments are never rewritten by a pattern change; they keep the person they already had.
Saturday Lunch (Almoço - Sábados)
- Saturdays only.
- Type:
rotation. - Has its own ordered member list.
- Continues after the previous assigned person's position in the rotation, including across month boundaries. The anchor date is the fallback when there is no usable previous assignee.
Sunday Lunch (Almoço - Domingo)
- Sundays only.
- Type:
rotation. - Has its own ordered member list, independent of Saturday.
The data model supports a schedule matching multiple weekdays even though the seeded lunch schedules each use one day.
Rotation generation
For a rotation schedule, use the last assigned person on an earlier date in that same schedule as the reference. Choose the next active member after that person's position in the current rotation order, wrapping to the beginning. Unassigned dates are skipped when finding the reference. Manual assignments and completed swaps also supply the reference for later dates. Saturday and Sunday never use each other's assignees.
Inactive members are skipped as recipients, but their position can still supply the reference if they remain in the rotation. When no previous assignee exists, or that person has been removed from the rotation, fall back to the number of matching schedule days from the anchor date modulo the active member count. An empty active rotation leaves the shift unassigned.
Generation and explicit month/range application process dates chronologically, so each newly assigned date supplies the reference for the next date. Regenerating one assignment only recalculates that date; later existing assignments remain unchanged until an administrator explicitly applies the rotation.
Only missing assignments are generated. Existing rows are never rewritten.
An administrator can explicitly Apply rotation on a lunch schedule's month page. This recalculates that month's generated assignments using the current active rotation members and fills missing dates for that schedule. Manual assignments and completed swaps are preserved. Simply editing the rotation order still does not rewrite any assignment.
Every assignment on a lunch schedule page has a Regenerate button. An administrator can explicitly regenerate any assignment, including an automatic assignment, manual override, or completed swap. It immediately recalculates that date from the current rotation and its source becomes generated; if there are no active rotation members, it becomes unassigned. These actions preserve assignment IDs, notes, and completed request history. If ownership changes, open or pending-approval change requests for the assignment are cancelled, and assignment-change notifications are attempted after saving.
Generate after date fills missing shifts and recalculates automatic assignments in that schedule strictly after the selected reference date, through an explicit end date (inclusive). The range can span multiple months. Assignments on or before the reference date and after the end date are not edited. Manual assignments and completed swaps within the range are preserved and supply references for later dates. The default range covers the displayed month.
People and logins
emailandphoneare optional.- A username and password are optional, but they are all-or-nothing: a person can either sign in or be rota-only. A username without a password (or the reverse) is not allowed, and a person without credentials can never sign in.
- A username, when set, is unique.
- Rota-only people can be assigned by an administrator, appear in rotations, and can be named as the target of a change request, but cannot accept it; an administrator completes the transfer on the schedule's own configuration page.
- A person cannot remove their own login.
- An administrator can remove a person permanently from
/admin/users, after a confirmation page that lists the impact. A person who still holds any assignment, past ones included, cannot be removed: those shifts must first be handed over, so the record of who worked each date stays intact. SQLite cascades are not enforced by the application, so the removal clears the remaining references explicitly: night-pattern days lose their person, rotation positions are deleted, that person's own change requests are deleted and the ones that merely targeted or accepted by them lose those references, and notification logs keep their history without the person. - Nobody may remove their own account or the last remaining administrator. The people page only shows the remove button for the people who pass these checks; the endpoints enforce them again. Deactivating is the non-destructive alternative.
Access
/is the rota and is the landing page for everyone, signed in or not. Signed-out visitors see the month read-only: no "mine" highlighting, no change-request actions.- Change requests,
/swaps, and every/adminpage still require a signed-in, active account.
Change request state machine
open → pending_approval → approved
Other terminal states: rejected, cancelled, reverted.
- A swap is always one shift for one shift: it is created on
/swaps/new(its own page, so the rota keeps showing only who works each day) and names both shifts. The person giving the first shift is the requester; the person owning the second one is the target. There are no open offers. - The two shifts must belong to the same schedule. The people of a schedule are the ones in its rotation, in its monthly pattern, or already holding one of its shifts.
- When a swap is approved or accepted, both assignments change hands at once: the target takes the requester's shift and the requester takes the target's shift. Both become
swapand both people are notified. - One person can work several days in a month, so each side of the exchange is chosen from a list of the month's future shifts, grouped by schedule: the requester picks "O turno que passo" (their own shifts) and "O turno que quero em troca" (someone else's shifts of that schedule). A member only sees their own shifts on the first list; an administrator sees all of them.
- A shift can only be part of one open request at a time, on either side.
- An administrator can open a request for someone else's shift. The request is still recorded against the person who owns the shift, so the ownership rule below keeps working; unassigned and past shifts cannot be requested.
/admin/assignis the day-by-day picker for administrators: pick a day, then put someone on one of its shifts with Atribuir. This is a direct assignment, never a change request. It sets the assignment source tomanual, cancels any open request that involves that shift, and notifies both the new and the previous person.Sem atribuiçãoclears it. The date must match one of the schedule's weekdays.- The requester cannot accept their own request.
- Only the person the request names can accept it or decline it.
- If the schedule requires manager approval, acceptance moves to
pending_approval. - Nobody can decline their own request; the requester cancels it instead. An administrator may decline any
openorpending_approvalrequest. - An administrator can approve a
pending_approvalrequest (it goes to whoever accepted it) or anopenone, choosing another person of the same schedule when there is no named partner. Approval and decline are available both in/swapsand on the rota page next to the shift. - Approval transfers the assignment and sets its source to
swap. - Only an administrator can revert an approved two-sided swap: both shifts go back to the people who had them, their source becomes
manual(the original source cannot be recovered) and a note records the revert. If either shift changed after the swap, the revert is refused and the day-by-day assignment must be used instead. - If either person no longer owns their own shift, the request must not be applied and is cancelled.
- On a schedule's configuration page, an administrator can complete an
openorpending_approvalrequest on someone's behalf by choosing another person who works that same schedule. This is the only way a rota-only person gives up or receives a shift. The same ownership rule applies, other open requests for that assignment are cancelled, and the requester and the new person are both notified. Past dates are refused.
Notifications
Email is optional per user and per installation.
Events include:
- assignment updated/removed by admin;
- change requested;
- change accepted;
- manager approval required;
- change approved/rejected;
- shift reminder.
An email error is logged but must not undo a scheduling action.