diff --git a/.claude/settings.local.json b/.claude/settings.local.json index b97e827..834a4fe 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -265,7 +265,12 @@ "Bash(docker exec teeoff_db psql -U teeoff_admin -d teecup_scratch -c ' *)", "Bash(rm -f /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/scratch.env /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/scratch_app_pw.txt /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/minio_user.txt /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/minio_pass.txt /tmp/lb_cookies.txt)", "Bash(mkdir -p /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/v0-zip-8)", - "Bash(rm -f /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/scratch.env /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/scratch_app_pw.txt /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/minio_user.txt /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/minio_pass.txt /tmp/lb2_cookies.txt)" + "Bash(rm -f /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/scratch.env /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/scratch_app_pw.txt /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/minio_user.txt /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/minio_pass.txt /tmp/lb2_cookies.txt)", + "Bash(rm -f /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/scratch.env /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/scratch_app_pw.txt /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/minio_user.txt /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/minio_pass.txt /tmp/idem_cookies.txt)", + "Bash(docker exec teeoff_db psql -U teeoff_admin -d teecup_db -v ON_ERROR_STOP=1 -c ' *)", + "Bash(python3 -m py_compile /opt/teecup/app/routers/tournaments.py /opt/teecup/app/routers/scoring.py)", + "Bash(rm -f /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/scratch.env /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/scratch_app_pw.txt /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/minio_user.txt /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/minio_pass.txt /tmp/patch_cookies.txt)", + "Bash(awk 'NR==499,/^---$/{print NR\": \"$0}' /opt/teecup/FEATURE_BACKLOG.md)" ], "additionalDirectories": [ "/opt/teeoff/deploy", diff --git a/CLAUDE.md b/CLAUDE.md index 5a0e792..f9a9329 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -965,6 +965,81 @@ Ferdig og verifisert: **Hele "bygg i rekkefølgen ting brukes"-serien for match-play-flyten er dermed komplett:** oppsett (lag/roster) → program → blind draw → scorekort → leaderboard. +- **Offisiell bane-import: idempotent + tydeligere navn, LIVE (2026-07-18), + rapportert av brukeren som faktisk brukte funksjonen på ekte + produksjonsdata:** to reelle problemer, begge funnet ved å lese (kun + lesing) de faktiske dataene for "De Gamle er Eldst" FØR noe ble antatt. + 1. **`POST .../courses/official-import` var IKKE idempotent** — å + importere samme bane på nytt (helt vanlig: flere økter spilles ofte + på samme bane) ga en 409 `DUPLICATE`-feil (migrasjon 010 sin sperre) + i stedet for å bare gi tilbake den allerede importerte banen. Fikset: + sjekker nå `external_course_ref` FØR noe teeoff-kall gjøres — finnes + banen fra før, returneres den eksisterende raden direkte (også + raskere, og robust mot at teeoff er nede akkurat da). + 2. **Lagret navn var kun selve banens navn ("Hovedbanen"), ikke hvilken + klubb** — ubrukelig til å skille baner fra hverandre, siden mange + klubber navngir hovedbanen sin identisk. Fikset: navnet kombineres nå + til "{anlegg} – {bane}" (f.eks. "Tjøme Golfklubb – Hovedbanen") ved + import. + **Reell konsekvens av hull #1 funnet i produksjonsdata:** brukeren hadde, + mens hen forsøkte å søke opp Tjøme via det ØVERSTE banefeltet (som søker + organisasjonens EGNE baner, ikke teeoff), ved et uhell trigget «Opprett + ny bane: «Tj»»-snarveien og fått en tom, søppel `custom`-bane hengende på + Foursome-økten -- en ekte forvekslingsfelle mellom de to adskilte + bane-søkeflatene (eget vs. teeoff), ikke en kodefeil i seg selv. + **Data ryddet opp i EKTE `teecup_db`, bruker bekreftet eksplisitt:** + Foursome-økten pekt om til den allerede importerte, ekte Tjøme-banen + (samme bane som Fourball-økten allerede brukte -- nøyaktig det + organisatoren egentlig ønsket), søppel-«Tj»-banen slettet (bekreftet + ingen matcher/tee-er/hull hang på den FØR sletting), og den ekte banens + navn oppdatert til "Tjøme Golfklubb – Hovedbanen". Verifisert i etterkant + at begge økter nå peker til samme, korrekt navngitte bane. + **Verifisert mot ekte teeoff_api i scratch FØR utrulling:** importerte + Borregaard på nytt to ganger — andre kallet ga nøyaktig samme course-id + (ikke en duplikat-rad), navnet kom ut som "Borregaard Golfklubb – + Hovedbanen". `test_isolation.sql` 12/12. Ingen migrasjon, kun `teecup_api` + redeployet, `teeoff.no` upåvirket. +- **`PATCH`/`DELETE` for økter, LIVE (2026-07-18), samme dag:** brukeren + spurte rett etter opprydningen om det i det hele tatt var mulig å + rette/slette en feiloppsatt økt — det var det ikke (kun `POST`/`GET` + fantes). Ny `PATCH /orgs/{id}/sessions/{id}` (`app/routers/ + tournaments.py`) for enkle felt (navn, klokkeslett/intervall, starthull, + poeng, handicap-brytere) via vanlig `exclude_unset`-mønster, PLUSS en egen + gren for bane-bytte. Ny `DELETE /orgs/{id}/sessions/{id}` — kun tomme + økter (ingen matcher), avviser med 409 ellers (bruk PATCH til å korrigere + i stedet). + **Banebytte-scenarioet brukeren selv reiste** ("5 hull spilt, oppdager + feil bane — slagene er ekte, utregningen er trolig feil") krevde egen + design: `match_participant.tee_id` peker til en tee som HØRER til den + gamle banen. Løst med `_remap_course()` — finner en tee med samme + navn+kjønn på den nye banen for hver allerede tillagte deltaker, flytter + dem dit, og avviser HELE bane-byttet tydelig (400, ingenting skrevet, + bekreftet transaksjonell rollback) hvis den nye banen mangler en + tilsvarende tee. Etter et vellykket bytte: handicap regnes om for alle + berørte deltakere, og matchstatus/poeng regnes om for HVER match i + økten — **bevisst uavhengig av om matchen allerede er avgjort** (brukeren + bekreftet eksplisitt at en bane-korrigering skal kunne endre et allerede + cachet resultat). `recompute_and_cache_match_state` i `app/routers/ + scoring.py` gjort delt (fjernet ledende understrek) for gjenbruk fra + tournaments.py. + **Reelt, urelatert funn underveis i scratch-testingen, IKKE fikset:** + `tee_rating` lages i dag ALLTID kun med `full_18`-omfang (både ved + teeoff-import og manuell tee-opprettelse) — en økt satt til `front_9`/ + `back_9` i `stroke`-modus kan derfor ALDRI få handicap beregnet + (`compute_and_store_side_handicaps` sin `tee_rating`-join finner aldri + noen rad), og dermed aldri avgjøre noen hull. `hole_result`-modus + upåvirket. Flagget til bruker, bevisst latt urørt denne runden. + **Verifisert grundig i scratch:** enkelt feltbytte (kun navn), fullt + banebytte-scenario bygget nøyaktig som brukerens eksempel (10 hull spilt + under feil bane, matchen allerede avgjort 10&8, PATCH til riktig bane → + tee-er ombyttet korrekt, handicap endret fra 7/9 til 10/13 under den nye + banens rating, matchstatus regnet på nytt med UENDREDE rå slagtall), + avvist bane-bytte ved manglende tee-match (bekreftet full rollback, + også av det urelaterte navnefeltet i samme kall), DELETE avvist på økt + med match (409) og godtatt på tom økt (204). `test_isolation.sql` 12/12. + Ingen migrasjon, kun `teecup_api` redeployet, `teeoff.no` upåvirket. + Frontend (rediger-/slett-knapper i program-skjermen) ikke bygget ennå — + kun backend-kapasiteten denne runden. Neste steg: 1. PWA-egenskaper (manifest, service worker, offline-cache) — ikke startet. diff --git a/FEATURE_BACKLOG.md b/FEATURE_BACKLOG.md index 079b6f7..42ce37e 100644 --- a/FEATURE_BACKLOG.md +++ b/FEATURE_BACKLOG.md @@ -564,6 +564,19 @@ andre skjermen når den ble generert i en egen prompt. (`teecup_api`, `teecup_frontend`) bygget og redeployet, live sjekker OK (`/health`, `/dashboard` → 200), `teeoff.no` upåvirket. +**Nytt 2026-07-18, ✅ BACKEND LIVE (frontend gjenstår):** `PATCH`/`DELETE` +for økter (`app/routers/tournaments.py`) — kunne tidligere verken rettes +eller slettes etter opprettelse. `DELETE` kun for tomme økter (409 hvis den +har matcher). `PATCH` dekker enkle felt fritt, pluss en egen, forsiktig +gren for bane-bytte (finner/flytter tilsvarende tee per allerede tillagt +deltaker, regner om handicap+matchstatus for hele økten etterpå — også for +allerede AVGJORTE matcher, bekreftet eksplisitt av bruker). Se +CLAUDE.md-status for det fulle scenarioet (verifisert med et 10-hulls +avgjort-match-eksempel) og et urelatert funn (`front_9`/`back_9` + +`stroke`-modus kan aldri få handicap i dag, siden `tee_rating` alltid kun +lages med `full_18`-omfang). Rediger-/slett-UI i program-skjermen ikke +bygget ennå. + --- ### Offisiell banedata fra teeoff — ✅ BYGGET OG LIVE 2026-07-18 (ADR-019) @@ -610,6 +623,22 @@ vasket bort `?org=...`-parameteren fra URL-en. Fikset ved å fjerne det indre `