90 Commits

Author SHA1 Message Date
the-dogfather-cto[bot] 7bf9cf9734 Merge pull request #359 from groombook/fix/gro-890-seed-stripe-payment-intent
fix(GRO-890): populate stripePaymentIntentId on paid seed invoices
2026-04-23 22:36:27 +00:00
groombook-engineer[bot] bf159f8b1f fix(GRO-890): populate stripePaymentIntentId on all paid seed invoices
All paid invoices created by the seed script now get a deterministic
stripePaymentIntentId of the form pi_test_seed_NNNNNN, unblocking the
refund button conditional in Invoices.tsx:514 during UAT.

Pending/draft invoices retain null as before.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-23 19:29:45 +00:00
Test User 50e9e70935 feat(gro-609): add Stripe details to invoice modal and fix stats date filter
- Add GET /api/invoices/:id/stripe-details endpoint to fetch card last4 and
  payment status from Stripe
- Add getPaymentIntentDetails() to payment service
- Fix stats summary query to filter by startOfMonth
- Add cardLast4, paymentStatus, stripeRefundId transient fields to Invoice type
- Display Stripe details (card last4, payment status, refund status) in modal
- Add stripeRefundId and paymentFailureReason to Invoice schema (was missing in dev types)

Ref: GRO-609
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-19 01:02:49 +00:00
Test User d59cb1ab1d feat(gro-609): add refund handling and payment stats to admin
- Add stripePaymentIntentId to Invoice schema and types
- Add POST /api/invoices/:id/refund endpoint (Stripe placeholder)
- Add GET /api/invoices/stats/summary for payment analytics
- Add refund button + dialog (full/partial) to InvoiceDetailModal
- Add payment stats cards to Invoices page (revenue, outstanding, refunds, method breakdown)

Ref: GRO-609
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-19 00:59:18 +00:00
scrubs-mcbarkley-ceo[bot] 8e1e51be59 Merge pull request #318 from groombook/dev
Promote dev → main: GRO-639, GRO-642, GRO-666, GRO-724
2026-04-17 11:43:47 +00:00
lint-roller-qa[bot] 772f4df62f fix(GRO-643): add appointment indexes to schema and S3 error handling (#315)
- Add idx_appointments_client_id, idx_appointments_staff_id,
  idx_appointments_start_time, idx_appointments_status to schema.
  Migration 0029 already handles the DB side; this brings schema.ts
  in sync so drizzle-kit push is clean going forward.
- Wrap deleteObject calls in try/catch (POST /photo/confirm and
  DELETE /:petId/photo endpoints) so S3 failures don't abort the
  DB update — orphaned objects are logged as warnings instead.

Co-authored-by: Test User <test@example.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-04-17 06:42:01 +00:00
Test User eab97b2ebd fix(GRO-666): leave staff.user_id NULL in seed so middleware can auto-link by email
The resolveStaffMiddleware auto-links on first API call when staff.user_id
IS NULL. Setting userId at seed time blocks this path since Better-Auth's
user.id is opaque and unknown pre-auth. Remove userId from all staff inserts
so the middleware can populate it on first authenticated call.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-17 01:32:28 +00:00
Paperclip c786544369 Fix frontend error handling and code quality (GRO-642)
HIGH Priority:
1. SetupWizard.jsx -> SetupWizard.tsx: renamed to .tsx with proper TypeScript types
2. deleteAppt missing error handling: added try/catch, response.ok check, alert on failure
3. GlobalSearch missing error state: added error state with user-visible error message

MEDIUM Priority:
4. CustomerPortal unsafe type cast: fixed 'as any' to proper PortalAppointment type
5. Logo upload XSS risk: sanitized MIME types to png/jpeg/gif/webp only, removed SVG
6. Reports error handling: added ok checks before json() parsing to guard against invalid JSON on error responses

LOW Priority:
8. Modal accessibility: added role='dialog', aria-modal='true', focus trap, Escape key handler, restore focus on close
9. PetPhotoUpload file size: added 50MB max file size check before resize
10. Types package: added photoKey and photoUploadedAt to Pet interface

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-16 22:38:23 +00:00
groombook-cto[bot] a2cfdfef74 Merge branch 'main' into fix/gro-689-oidc-hostname-validation 2026-04-16 05:15:28 +00:00
groombook-cto[bot] 6ba6da08b2 Merge branch 'main' into fix/gro-689-oidc-hostname-validation 2026-04-16 05:08:23 +00:00
Flea Flicker 29a726fa3d feat(GRO-690): add groomer persona seed support via env vars
Extend seed.ts with SEED_UAT_GROOMER_EMAILS and SEED_UAT_GROOMER_NAMES
env vars for persistent groomer personas (sam@sarah). Works in both
SEED_KNOWN_USERS_ONLY=true and full seed modes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-16 05:04:52 +00:00
Flea Flicker 85cff19c59 fix(GRO-666): make migration 0028 idempotent to resolve E2E failure
- Add IF NOT EXISTS to all ADD COLUMN statements (schema already has these columns)
- Use DROP CONSTRAINT IF EXISTS for both possible auto-generated constraint names
- Idempotent: safe to re-run on databases that already have the schema changes

cc @cpfarhood

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-15 15:25:20 +00:00
Flea Flicker da16ac8ac2 Add missing DB indexes, NOT NULL on clients.email, and S3 error handling
- Add 4 indexes on appointments: client_id, staff_id, start_time, status
- Add index on pets.client_id
- Add index on clients.email
- Change clients.email to NOT NULL with backfill migration
- Wrap S3 deleteObject calls in try/catch in pets photo endpoints
- Update POST /clients test to include required email field

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-15 10:09:57 +00:00
Flea Flicker 16dd513521 fix(seed): populate userId for UAT staff and SEED_ADMIN_EMAIL staff
GRO-666: resolveStaffMiddleware returns 403 for UAT users because
staff records have NULL userId after seed. This change populates
userId (and oidcSub) for all staff created via seedKnownUsers()
and the main seed path using the same value as the OIDC sub.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-15 09:37:51 +00:00
Flea Flicker 66a6659ccd feat(GRO-600): extend reminder scheduler to send SMS alongside email
- Add SMS opt-in fields to clients schema (smsOptIn, smsConsentDate, smsOptOutDate, smsConsentText)
- Add channel column to reminderLogs with per-channel idempotency
- Create SMS service with Telnyx SDK integration and E.164 validation
- Update reminders service to conditionally send SMS to opted-in clients
- Add TCPA opt-out text to SMS reminders
- Graceful degradation: catch SMS errors without blocking email
- Fix: use clients.phone instead of non-existent clients.phoneE164
- Update clients route to expose SMS fields in API
- Add telnyx dependency to API package
- Create database migration 0028_sms_reminders

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-15 09:23:24 +00:00
groombook-cto[bot] d433c902b4 fix(GRO-637): invoice status transitions, tip-split validation, refund idempotency, and tip-split response format
* Fix invoice status transitions, tip-split validation, refund idempotency, and tip-split response format

- Add ALLOWED_TRANSITIONS state machine for invoice status changes (GRO-637)
- Replace floating-point tip-split validation with integer basis-points math
- Add idempotency key support to refund endpoint with new refunds table
- Return full invoice shape from POST /:id/tip-splits matching GET response
- All existing tests pass

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(invoices): wrap refund flow in transaction for idempotency safety

- Wrap idempotency check + processRefund() + db.insert() in db.transaction()
- This prevents duplicate Stripe refunds if the DB insert fails after Stripe processes the refund
- Add migration 0027_refunds for the refunds table (was missing)
- Removes out-of-scope changes from PR #278 (csrf.ts, appointmentGroups, appointments, book, groomingLogs, services, stripe-webhooks)

Fixes GRO-637 per CTO review

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(api): wire up CSRF middleware for protected routes

Register csrfMiddleware in the protected API routes after authMiddleware
and resolveStaffMiddleware to protect against CSRF attacks on state-
changing operations (POST, PUT, PATCH, DELETE).

Addresses CTO review feedback on PR #278.

* fix(api): remove CSRF middleware that breaks POST/PUT/PATCH/DELETE

The CSRF middleware requires x-csrf-token header but the frontend never
sends it, which would break all mutating operations with 403 errors.

CSRF protection should be implemented in a separate coordinated PR with
frontend changes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Flea Flicker <flea-flicker@groombook.ai>
2026-04-15 06:04:38 +00:00
groombook-ceo[bot] 4fa4859eaf fix: set Manager 1 as super user in UAT seed to resolve OOBE redirect
Co-authored-by: Flea Flicker <flea-flicker@paperclip.ing>
Co-authored-by: groombook-cto[bot] <269737991+groombook-cto[bot]@users.noreply.github.com>
2026-04-15 00:47:09 +00:00
groombook-cto[bot] c438f5772c feat(GRO-607): Stripe Elements payment UI replacing mock flow
* GRO-605: Stripe SDK integration + payment service

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* GRO-606: Add payment API endpoints (pay invoice, payment methods, refunds)

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* feat(GRO-597): Stripe payment backend — schema, service, API, webhooks

Consolidates GRO-605, GRO-606, GRO-608 into a single clean PR:
- GRO-605: Stripe SDK integration + payment service
- GRO-606: Payment API endpoints (pay invoice, payment methods, refunds)
- GRO-608: Stripe webhook handler

Migration consolidation:
- Single 0026_stripe_payment.sql migration adds stripeCustomerId to clients
  and stripe_payment_intent_id, stripe_refund_id, payment_failure_reason to invoices
- Removed duplicate 0027_stripe_identifiers.sql

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* GRO-607: Install Stripe frontend packages

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* GRO-607: Add /portal/config endpoint + rename date field

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* GRO-607: Replace mock payment flow with real Stripe Elements

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(GRO-607): Stripe Elements payment UI - lint/type fixes

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(GRO-607): remove unused eslint-disable directive in CustomerPortal

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(GRO-607): CTO review fixes — payment security and correctness

- Fix multi-invoice total calculation: use inArray() instead of eq()
  on single ID, sum all invoices not just first
- Add ownership check to payment method deletion: verify the payment
  method belongs to the authenticated Stripe customer before detaching
- Remove duplicate /config endpoint in portal.ts
- Fix webhook Stripe client: use getStripeClient() from payment service
  instead of constructing with WEBHOOK_SECRET
- Remove unnecessary body validator on /invoices/:id/pay route
- Export getStripeClient() for use by stripe-webhooks.ts
- Add inArray import to payment.ts

Co-Authored-By: Paperclip <noreply@paperclip.ing>

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-04-14 08:27:03 +00:00
groombook-cto[bot] 15131b72f0 fix(GRO-574): add rate_limit table migration for Better Auth
Adds the missing rate_limit table that Better Auth v1.5.6 requires when rateLimit.storage is set to 'database'. Without this table, all auth endpoints return HTTP 500.

Also includes GRO-566: SKIP_OOBE env var to bypass setup wizard in dev/test.

cc @cpfarhood
2026-04-12 03:30:45 +00:00
Paperclip c919632aea fix(GRO-395): adjust Puggle image array to 4 available images
Removed reference to tricolor-outdoor image that experienced API timeout.
Seed now uses 4 successfully generated Puggle-specific images for the
first 250 seeded pets, ensuring all referenced images exist in demo-pets.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-11 02:22:05 +00:00
Paperclip 522e5dbf63 feat(GRO-395): ensure at least 250 seeded pets are Puggles with photos
Add Puggle breed to dogBreeds array and modify seed logic to guarantee first 250 pets are assigned Puggle breed. This ensures demo data includes a significant population of Puggle dogs (Pug-Beagle mix) with images for testing grooming workflows with diverse pet breeds.

- Add Puggle to available dog breeds list
- Track pet creation index across all clients
- Assign Puggle breed to first 250 pets regardless of randomization
- Assign Puggle-specific images to first 250 pets
- Remaining pets use general demo image pool

This satisfies requirement: "at least 250 of the 2500 pets are first or second generation puggles with photos"

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-10 21:45:16 +00:00
Paperclip 39f603589b feat(GRO-395): expand demo pet image library with 13 AI-generated diverse dog images
- Generated 13 new diverse dog images using MiniMax (Afghan Hound, Basset Hound, Bichon Frise variants, Boxer, Cavalier, Cocker Spaniel variants, Corgi, Dachshund variants, Pomeranian variants, Schnauzer variants, Setter, Sheepdog)
- Updated seed script to include all 28 dog images in demoPetImages array
- Ensures wider variety of dog breeds and grooming styles in demo seed data
- All images are photorealistic and suitable for pet grooming demo site

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 21:40:09 +00:00
Paperclip 2ab06853e6 fix(GRO-395): add demo pet images to seed data for all profiles
- Add demoPetImages array with 15 available dog images
- Assign random pet images during seed for regular, UAT, and demo profiles
- Ensures demo site displays pet images instead of missing images

The seed script previously only added images for seedKnownUsers mode.
Now all seeded pets get assigned a random demo pet image.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 21:18:10 +00:00
Flea Flicker fafb717e5a feat(GRO-537): add UAT Super User and Staff Groomer to seed script
In seedKnownUsers(), add staff records for UAT Super User
(manager, superuser) and UAT Staff Groomer (groomer) with oidcSub
read from SEED_UAT_SUPER_OIDC_SUB and SEED_UAT_STAFF_OIDC_SUB
env vars. Only creates records when the env vars are present.
Idempotent: skips if email already exists.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 10:48:11 +00:00
groombook-engineer[bot] 1255fd91cd feat: parameterize seed script with SEED_PROFILE env var (GRO-526)
Adds SEED_PROFILE env var accepting 'dev', 'uat', or 'demo' values:

- dev: 4 staff (1 manager, 1 receptionist, 2 groomers), 100 clients,
  7d/30d appointment window, ~1000 invoices, no UAT clients
- uat: 8 staff (1 manager, 1 receptionist, 3 groomers, 3 bathers),
  500 clients, 30d/90d window, ~4000 invoices, includes UAT clients
- demo: same volume as uat

Unset SEED_PROFILE defaults to 'uat' for backwards compatibility.
SEED_KNOWN_USERS_ONLY=true path unchanged.
All appointment dates computed relative to NOW() at seed time.
Supplemental completed appointments generated when profile invoice
target exceeds organic appointment count.

Closes groombook/groombook#247

Co-authored-by: Flea Flicker <flea-flicker@groombook.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-04-10 04:00:37 +00:00
Flea Flicker 63256b8bc0 feat: implement SEED_ADMIN_EMAIL support in seedKnownUsers and full seed
- Add admin upsert to seedKnownUsers() for prod path
- Add admin upsert to seed() for UAT path
- Idempotent: skips if SEED_ADMIN_EMAIL not set

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-09 22:33:15 +00:00
Paperclip a84d5e7b9a fix: set isSuperUser=false for Jordan Lee in full seed
Jordan Lee was being created with isSuperUser=true in the full seed path,
causing GET /api/setup/status to return needsSetup=false after UAT reset.
2026-04-08 02:56:31 +00:00
Flea Flicker 9be6a87105 chore: implement hourly reset CronJob for prod and UAT
- Add ALLOW_RESET env var override to reset.ts safety guard
- Add reset Docker build target to Dockerfile
- Add reset image build step to CI docker job
- Add reset image tag update to CD job dev overlay update

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-07 20:01:56 +00:00
Flea Flicker 0fe10434e1 feat(invoices): add indexes, pagination, and client name enrichment
- Add database migration 0024 with indexes on invoices, invoice_line_items, and invoice_tip_splits
- Update Drizzle schema with index definitions for sync
- Add pagination (limit/offset) to GET /api/invoices with max 200 limit
- Add LEFT JOIN to include clientName in invoice list response
- Return { data: [...], total: N } response shape for pagination

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-07 19:23:03 +00:00
groombook-qa[bot] 9a3d8d1516 Merge branch 'main' into fix/gro-478-auto-link-staff-user 2026-04-05 14:59:21 +00:00
Flea Flicker e39924b236 fix(api): import isNull from @groombook/db instead of drizzle-orm directly
drizzle-orm is not a direct dependency of @groombook/api, causing
TS2307 at typecheck time. Re-export isNull from @groombook/db and
update the import in rbac.ts.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-05 14:39:22 +00:00
Paperclip 78a6758349 fix(db): generate unique random salt per encryptSecret call (GRO-453)
Use a 16-byte random salt per encryption instead of the fixed
"groombook-auth-provider-config" salt. This prevents identical
plaintexts from producing identical ciphertexts, closing the
timing/anagram security gap identified in GRO-452.

New format: salt:iv:ciphertext:authTag (all base64).
Legacy format (iv:ciphertext:authTag) is still accepted for
backward-compatible decryption of existing stored values.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 21:37:30 +00:00
Paperclip 1c7628459f fix(db): use random per-encryption salt in crypto.ts (GRO-453)
Generate a unique 16-byte random salt for each encryptSecret() call
and store it as a prefix in the ciphertext. Format changed from
  iv:ciphertext:authTag → salt:iv:ciphertext:authTag

decryptSecret() detects legacy 3-part format and uses the fixed
package salt for backward compatibility with existing encrypted rows.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 13:14:18 +00:00
Flea Flicker 2453e3a0ae fix(db): remove duplicate encryptSecret/decryptSecret exports
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 00:03:48 +00:00
Flea Flicker 1c502bb165 fix(api): wrap encryptSecret in try/catch to return proper JSON error
PUT /api/admin/auth-provider was returning HTTP 500 with an HTML error page
when BETTER_AUTH_SECRET was missing, because encryptSecret() throws an
unhandled error. This change wraps both the encryption step and the DB
transaction in try/catch blocks to return a proper JSON error response.

Also adds the missing authProviderConfig schema and encryptSecret crypto
helpers from the feat/gro-392-oobe-auth-provider-bootstrap branch.

Fixes: GRO-441

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 00:02:11 +00:00
groombook-engineer[bot] 02bc0d2c1b fix(db): add missing image and logoKey schema fields to match migrations
Adds `image: text("image")` to pets table and `logoKey: text("logo_key")`
to businessSettings table to resolve typecheck failures. These fields
were added by migrations 0021 and 0022 but the schema definitions were
missing, causing TypeScript errors in seed.ts and settings.ts.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-02 17:38:29 +00:00
groombook-engineer[bot] f4acf5be23 feat(db): auth_provider_config table + AES-256-GCM encryption helpers
Renumbered migration 0021 → 0023 to resolve conflict with pet_image and
logo_key migrations that landed on main after this branch was created.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-02 17:16:35 +00:00
groombook-engineer[bot] 71a6623da2 fix(db): add image: null to Pet factory
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-02 12:35:05 +00:00
groombook-engineer[bot] 74571d9f2b feat(demo): expand demo pet images and seed data with diverse breed showcase
Generated 16 diverse pet images for demo site using MiniMax image generation:
- Multiple dog breeds (Golden Retriever, Poodle, Labrador, Shih Tzu, Cocker Spaniel, Schnauzer, Maltese, Dachshund, Pomeranian)
- Professional grooming styles and poses
- Studio lighting for quality showcase

Updated seed.ts to create 9 demo pets with image references:
- Expands from single demo pet to diverse pet portfolio
- Images deployed to apps/web/public/demo-pets/
- Each pet has breed-accurate styling and professional grooming

This completes GRO-395 demo assets expansion using allocated MiniMax credits.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-02 12:15:21 +00:00
groombook-engineer[bot] a867be7d55 fix(db): add image column to pets table for demo pet images
Adds `image` field to pets table schema and creates migration to support storing demo pet image URLs. Resolves TypeScript error in seed.ts where image property was being referenced on insert.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-02 11:56:49 +00:00
groombook-engineer[bot] 3d9021913d feat(branding): add GroomBook logo and demo pet images for demo site
- Generated professional GroomBook logo using brand colors (sage green & warm brown)
- Created 4 realistic test pet images (Golden Retriever, Labrador, Poodle, Mixed Breed)
- Updated demo seed to reference pet image in demo database
- Assets are reloaded with demo data going forward

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-02 11:49:43 +00:00
groombook-engineer[bot] 5a09290d9f fix(db): move impersonation TRUNCATE before staff upsert to avoid FK violation
The TRUNCATE of impersonation_sessions/audit_logs was running after the
staff upsert. When a prior seed left impersonation_sessions rows
referencing staff records, ON CONFLICT DO UPDATE on staff violated the
FK constraint on impersonation_sessions.staff_id.

Moving the TRUNCATE before the staff block ensures all impersonation rows
are cleared before any staff insert/update attempt.

Co-authored-by: groombook-engineer[bot] <3141748+groombook-engineer[bot]@users.noreply.github.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-04-02 01:03:36 +00:00
groombook-engineer[bot] ee803b4376 fix(db): add impersonation_sessions and audit_logs to seed TRUNCATE chain (#200)
Truncate these tables before staff upsert to avoid FK constraint violations
when the dev DB already has impersonation sessions referencing staff rows.

Co-authored-by: groombook-engineer[bot] <3141748+groombook-engineer[bot]@users.noreply.github.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: groombook-ceo[bot] <269735724+groombook-ceo[bot]@users.noreply.github.com>
2026-04-01 21:24:58 +00:00
groombook-ceo[bot] 361567dc3b Merge branch 'main' into fix/gro-360-yq-compound-assignment 2026-04-01 19:56:01 +00:00
groombook-engineer[bot] dff11d02be fix(db): seed staff_id FK fix (GRO-369)
Fixes staff_id FK violation on re-seed: move TRUNCATE before staff upsert and add id to onConflictDoUpdate set clause.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-01 14:19:49 +00:00
groombook-engineer[bot] cfa28c64b6 fix(db): add migration 0020 for UNIQUE(name) + align admin seed ON CONFLICT
Problem:
- Schema change in eacf8ab added .unique() to services.name
- No migration existed to apply this constraint to the database
- Admin seed and seedKnownUsers still used ON CONFLICT (id) with
  a0000001-... IDs, inconsistent with main seed's b0000001-... IDs
  and ON CONFLICT (name)

Fix:
- Migration 0020: clean up existing duplicate services (keep lowest
  id per name), then add UNIQUE constraint on services.name
- Admin seed (apps/api): switch to ON CONFLICT (name) and b0000001
  IDs to match main seed's servicesDef
- seedKnownUsers (packages/db): same alignment — b0000001 IDs and
  ON CONFLICT (name)

GRO-364: ON CONFLICT (name) eliminates the duplicate-row problem
that the old dedup+ON CONFLICT(id) approach could not solve when
existing rows had non-deterministic IDs.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-01 13:54:54 +00:00
groombook-engineer[bot] 7033cd1d5c fix(db): remove dedup DELETE and use ON CONFLICT (name) for idempotent services seed
The dedup DELETE was causing two problems:
1. FK violation (GRO-365) — deleting services referenced by appointments
2. Duplicate services (GRO-301) — MIN(id) per name could delete the wrong row,
   causing ON CONFLICT (id) to create duplicates on re-run

Fix:
- Remove the dedup DELETE entirely
- Keep TRUNCATE of downstream tables (appointments, invoices, etc.) to clear
  stale data from prior runs
- Change ON CONFLICT target from `id` to `name` with a unique constraint on
  name column — deterministic IDs in servicesDef ensure idempotency

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-01 13:54:54 +00:00
groombook-engineer[bot] 07263a89fe fix(db): truncate downstream tables before services dedup to avoid FK violation (#197)
TRUNCATE appointments, invoices, invoice_line_items, invoice_tip_splits,
and grooming_visit_logs CASCADE before the services dedup DELETE to prevent
FK violations from appointments created by previous seed runs.

Fixes: GRO-365

Co-authored-by: groombook-engineer[bot] <3141748+groombook-engineer[bot]@users.noreply.github.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-04-01 13:21:31 +00:00
groombook-engineer[bot] 034b733f74 fix(db): cast uuid to text for MIN() in services dedup query (GRO-364)
Postgres has no built-in MIN() aggregate for UUID type.
Cast to text before aggregating, then cast back to uuid.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-01 12:25:43 +00:00
Barkley Trimsworth 6277b1c427 Merge remote-tracking branch 'origin/main' into fix/gro-309-landing-page-redirect 2026-04-01 03:43:40 +00:00