See what a re-sync would change, and who's out, without guessing¶
Finance can now ask what a re-sync would change to an already-sent payslip or invoice and see the old and new side by side before deciding. The nightly earnings pull moved, so a cutoff's last day is no longer read before it has finished. And the leave calendar, the Scheduling Hub and a model's OnlyFans card each stopped making someone work out something the system already knew.
Check what a re-sync would change (Finance)¶
A Sync only touches draft rows. Anything already processed or sent stays as it is — correct, but it means a later change to the underlying earnings leaves a finalized record quietly wrong.
Check affected records on the Employees tab, and Check affected invoices on External Contractors, answer "what would a Sync do to these, if it were allowed?" It runs the real Sync and throws the result away, so nothing is written and you get an exact old → new figure first.
- Click a row for the side-by-side: what the record holds now against what a re-sync would make it, line item by line item.
- A contractor affiliate line also shows the Net OF the commission came from — usually the thing that actually moved.
- Then Apply per row or for a selection, or Ignore. Ignoring is keyed to the exact figures you were shown, so if they move again the row comes back.
Rows tied to a live ArunaPay batch are listed but can't be applied — that money is already moving, so settle or cancel the batch first.
Guide: When something already finalized would change
The re-sync banner is now per tab (Finance)¶
Employees and external contractors are re-materialized by two different Sync buttons. Each tab now shows only the drift its own Sync can clear, and each button clears only what its own banner describes.
A change that affects both — a model's earnings moving, say — raises the banner on both tabs and needs both Syncs. Before this, one tab's Sync could dismiss a warning only the other tab could act on.
Guide: Keeping a period fresh — Sync & Reset
A cutoff's last day is no longer read early¶
The nightly OnlyFans earnings pull ran at a time that asked the upstream for a day that was only two-thirds over. Mid-cutoff that corrected itself on the next night's run. On a cutoff's final day there was no next run, so every month ended on a short figure — and affiliate commission is computed from the whole month, so it inherited the shortfall.
The pull now runs late enough that the day it reads has finished, and every cutoff gets one extra completion pass as a safety net.
This does not correct months already recorded. Every month before this carries the same partial final day; the check above is how you find and fix the invoices that came from them.
Who's out, on one calendar (P&C and managers)¶
The Calendar tab used to show approved leave only; absences lived in their own tab as a table. "Who is out on the 12th?" meant reading both and merging them in your head.
- Both are on the grid now. An absence is the outlined entry, with a mark for whether a manager recorded it or a covered shift did.
- Today is ringed, with a count of how many are out.
- People & Culture → Leaves opens on the calendar rather than the approvals queue — P&C look things up, and deciding leave is the department manager's job. Manager pages still open on Requests.
- Every tab is now in the address bar, so a link to someone's absences can be sent to whoever needs it and survives a refresh.
The Absences tab is unchanged, and recording or removing an absence still happens there.
Guides: Approve leave & record absences · Leave credits & alignment calls
Schedule in any timezone (SOT managers)¶
The Scheduling Hub offered thirteen timezones and drew every session in its own, so two rows on the same grid weren't comparable.
- Viewing in sets the one timezone the whole calendar is read in. It's in the address bar, so a week can be sent to someone "in your time".
- Any IANA timezone can be picked, here and when scheduling. Type a city.
- Creating a session from a day you clicked opens the form in the timezone you were viewing in.
Sessions move across days when you change the viewing timezone, and that is correct — an evening stream in Manila is the same morning in New York. The lens changed, not the session: each one still records the timezone it was scheduled in.
Guide: Reading the calendar in another timezone
Also¶
- A contractor invoice keeps its original number when a re-sync is applied — only the amount changes.
- The affiliate commission calculation is unchanged: same rate, same monthly basis, same cutoff. Only when the earnings are read has moved.
- Absences say who recorded them, including the days nobody typed: an absence written when a shift was covered names the person who booked the cover. Days recorded before this show a dash. (guide)
- Covering an operator who has leave that day now says so, and the mark-absent control is switched off — their approved leave already accounts for the day, so nothing is recorded and nothing is docked. Approving a leave after a cover was recorded now clears that absence too. (guide)
- Refresh profile on a model's OnlyFans card re-reads the profile and restamps Last synced. If OnlyFans can't answer for the profile, you're told the refresh came back partial instead of it looking like a plain success. (guide)
Technical changes¶
For developers and whoever deploys. None of this needs a guide.
Data model
finance.0131addsresync_dismissed_amount(nullable decimal) andresync_dismissed_fingerprint(char, blank default) toExternalContractorPayout, mirroring the employee rail's dismiss-by- fingerprint columns. Additive and reversible; no data migration.
Behaviour
finance/services/sync_preview.pybecomes a package at the same import path: a model-agnostic engine plus oneRaildescriptor per side. The employee rail's public names are unchanged.- The re-sync signal is per rail. Each stored reason records which rails it
affects; each rail's Sync clears only its own share, and
resync_pendingstays true while any reason is outstanding. Reasons written before the split carry norailskey and are read as affecting both — the fail-safe direction.clear_resynctakes a requiredrailwith no default. _finish_ingestre-materializes a cutoff when its earnings ingest drains, rather than waiting for a clock.- The contractor rail gained
finalize_totals, becauseamountis a stored column not every materializer touches.
Deploy
- Schedule change.
models-ingest-open-cutoffs-dailyandmodels-auto-freeze-due-cutoffs-dailyboth move fromcrontab(hour="0")tocrontab(hour="9")— 09:00 PH, 01:00 UTC. The freeze job must move with the ingest: it shares the tick and relies on dispatch order, so leaving it at midnight would seal pairs nine hours before the day's top-up reached them. Anything downstream that assumed a midnight ingest should be checked. ensure-current-period-pulled-dailydeliberately stays at 00:00 PH.INGEST_GRACE_DAYSgoes to 2, so a cutoff gets one more completion pass. This does not extend how long data is treated as live —AUTO_FREEZE_DELAY_DAYSalready said four days.- No environment variables, no workers, no one-off commands.
- No migrations from the Papercuts changes.
Absence.recorded_byalready existed and was simply never populated; no stored timezone is rewritten.
Also in this promotion
GET /api/leaves/calendar/now returns approved leave and absences in one shape with akinddiscriminator, under the same window, department filter and active-employee rule. An absence is sent as a one-day span. The response shape changed, so any consumer other than the app needs regenerating.core.fields.TimeZoneFieldrejects anything outsidezoneinfo.available_timezones()on the stream-session write path and on an announcement'sexpires_timezone. The readers' Manila fallback stays for rows written before it.- The Scheduling Hub fetches its window ±1 day and cuts it in the view zone,
because the list endpoint filters
start_at__datein the server's own zone. - Approving a leave clears coverage-sourced absences in its range and flags the
affected cutoffs for re-sync.
MANUALrows are never touched.