Booking POST is a plain INSERT — reschedule re-fires EA-<id> and 500s on PK conflict #1
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
backoffice/app/app.py:205(add_entity) does an unconditionalINSERT INTO <entity> .... Easy!Appointments firesappointment_saveagain on reschedule with the samebooking_id(EA-<id>), so the second POST hits a primary-key conflict and returns 500 instead of updating the row.Fix: add
ON CONFLICT (<pk>) DO UPDATE SET ...forbookings(mirror the pattern already inbackoffice/app/import_from_sheets.py:35). Consider doing the same for all keyed entities so re-delivered webhooks are idempotent.Acceptance: reschedule an existing appointment → the existing
bookingsrow is updated (start/end/status), no 500, activity logged as update.Superseded by the booking-module replacement (#14): bookings will be written directly through the new schema/API (#15, #16) with a Postgres EXCLUDE constraint instead of synced in from Easy!Appointments, so this failure mode cannot occur once cutover lands. Left open until #25 (decommission EA) actually ships and verifies/closes this.
Fixed in
1b8f8c1.add_entity(backoffice/app/app.py) now doesINSERT ... ON CONFLICT (pk) DO UPDATE SET col = EXCLUDED.col ...for every entity exceptactivity_log(no client-supplied pk there), mirroring the oldimport_from_sheets.pyupsert pattern. Insert vs. update is detected viaRETURNING (xmax = 0), logged toactivity_logasadd <entity>/update <entity>accordingly, and the response is{"added": pk}/201 or{"updated": pk}/200.Extended to all keyed entities (bookings, invoices, leads, clients, credentials, projects) per "consider doing the same for all keyed entities" — with one fix along the way: a redelivered
leads/clients/credentialsPOST that omitscreated_at/received_atno longer clobbers the original creation timestamp on update (excluded from theDO UPDATE SETvia a newINSERT_ONLY_COLSmap).Tests added in
backoffice/app/tests/test_app_api.py: reschedule-repost updates the existing booking row (start/end/status) with no 500 and an "update bookings" activity-log entry; cancel-repost updates status; invoice repost updates instead of erroring; activity_log stays plain-insert (no client pk); leads/clients repost preserves the originalreceived_at/created_at. Full suite: 155 passed.Acceptance criteria met: reschedule updates the existing row, no 500, activity logged as update.