events: cross-event conflict-checked booking + 8 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 31s

ev/book-checked! prevents an attendee double-booking themselves across
different events: consults their persist-derived availability (ev/free-p?) for
the occurrence window, returns :time-conflict on overlap else the normal
ev/book-occ! result. Re-booking the same occurrence stays idempotent
(:already); other actors unaffected. ev/would-time-conflict? predicate.
311/311 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-07 13:59:37 +00:00
parent 1446eaaa47
commit 02b721854e
5 changed files with 101 additions and 7 deletions

View File

@@ -249,3 +249,29 @@
(fn
(b store actor ws we)
(> (len (ev/conflicts-p b store actor ws we)) 0)))
;; ---- conflict-checked booking ----
;; Capacity is per-event, but an attendee should not be double-booked against
;; THEMSELVES across different events. Would booking `actor` into `occ` overlap
;; an existing booking of theirs elsewhere? (Derived from persist availability;
;; an existing booking into `occ` itself is excluded — that's idempotent.)
(define
ev/would-time-conflict?
(fn
(b store actor occ)
(and
(not (ev-actor-booked? b (ev-occ-key occ) actor))
(not (ev/free-p? b store actor (get occ :start) (get occ :end))))))
;; Book `actor` into `occ` only if it doesn't clash with their other bookings.
;; Re-booking the same occurrence is idempotent (:already); a clash returns
;; :time-conflict; otherwise the normal ev/book-occ! result (:booked / :full).
(define
ev/book-checked!
(fn
(b store actor occ)
(cond
((ev-actor-booked? b (ev-occ-key occ) actor) (ev/book-occ! b store actor occ))
((ev/would-time-conflict? b store actor occ)
{:status :time-conflict :actor actor :occ-key (ev-occ-key occ)})
(else (ev/book-occ! b store actor occ)))))