Implement ordering MVP domain layer per spec (issue #1)

Scaffolds the TanStack Start + Drizzle/Postgres + Better Auth app and
implements the core ordering domain: menu browsing, guest/account order
placement with online payment, per-Order-Item independent cooking and
cancellation, dynamic Machine-Tag pool assignment, and multi-step recipes
(see docs/adr/0001-0003). Adds a guest-access-token / session-based
ownership check on every read/mutation so an order can't be viewed,
cancelled, or picked up by anyone but its owner.

Covered by 14 tests running the domain layer directly against a real,
ephemeral Postgres (testcontainers) — createServerFn couldn't be called
outside a real request context, so tests exercise the domain functions the
server functions thinly wrap instead of the server functions themselves.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-09 18:46:58 +02:00
co-authored by Claude Sonnet 5
parent e532a95f0b
commit 456c3acda5
38 changed files with 13572 additions and 1 deletions
+75
View File
@@ -0,0 +1,75 @@
CREATE TABLE "brands" (
"id" serial PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "machine_tags" (
"id" serial PRIMARY KEY NOT NULL,
"name" text NOT NULL
);
--> statement-breakpoint
CREATE TABLE "machines" (
"id" serial PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"machine_tag_id" integer NOT NULL
);
--> statement-breakpoint
CREATE TABLE "menu_item_steps" (
"id" serial PRIMARY KEY NOT NULL,
"menu_item_id" integer NOT NULL,
"sequence" integer NOT NULL,
"machine_tag_id" integer NOT NULL,
"estimated_duration_seconds" integer NOT NULL
);
--> statement-breakpoint
CREATE TABLE "menu_items" (
"id" serial PRIMARY KEY NOT NULL,
"brand_id" integer NOT NULL,
"name" text NOT NULL,
"description" text,
"price_cents" integer NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "order_items" (
"id" serial PRIMARY KEY NOT NULL,
"order_id" integer NOT NULL,
"menu_item_id" integer NOT NULL,
"price_cents" integer NOT NULL,
"status" text DEFAULT 'placed' NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "orders" (
"id" serial PRIMARY KEY NOT NULL,
"customer_id" text,
"guest_name" text,
"guest_contact" text,
"status" text DEFAULT 'placed' NOT NULL,
"payment_charge_id" text,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "preparation_step_instances" (
"id" serial PRIMARY KEY NOT NULL,
"order_item_id" integer NOT NULL,
"sequence" integer NOT NULL,
"machine_tag_id" integer NOT NULL,
"estimated_duration_seconds" integer NOT NULL,
"status" text DEFAULT 'queued' NOT NULL,
"machine_id" integer,
"started_at" timestamp,
"done_at" timestamp
);
--> statement-breakpoint
ALTER TABLE "machines" ADD CONSTRAINT "machines_machine_tag_id_machine_tags_id_fk" FOREIGN KEY ("machine_tag_id") REFERENCES "public"."machine_tags"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "menu_item_steps" ADD CONSTRAINT "menu_item_steps_menu_item_id_menu_items_id_fk" FOREIGN KEY ("menu_item_id") REFERENCES "public"."menu_items"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "menu_item_steps" ADD CONSTRAINT "menu_item_steps_machine_tag_id_machine_tags_id_fk" FOREIGN KEY ("machine_tag_id") REFERENCES "public"."machine_tags"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "menu_items" ADD CONSTRAINT "menu_items_brand_id_brands_id_fk" FOREIGN KEY ("brand_id") REFERENCES "public"."brands"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "order_items" ADD CONSTRAINT "order_items_order_id_orders_id_fk" FOREIGN KEY ("order_id") REFERENCES "public"."orders"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "order_items" ADD CONSTRAINT "order_items_menu_item_id_menu_items_id_fk" FOREIGN KEY ("menu_item_id") REFERENCES "public"."menu_items"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "preparation_step_instances" ADD CONSTRAINT "preparation_step_instances_order_item_id_order_items_id_fk" FOREIGN KEY ("order_item_id") REFERENCES "public"."order_items"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "preparation_step_instances" ADD CONSTRAINT "preparation_step_instances_machine_tag_id_machine_tags_id_fk" FOREIGN KEY ("machine_tag_id") REFERENCES "public"."machine_tags"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "preparation_step_instances" ADD CONSTRAINT "preparation_step_instances_machine_id_machines_id_fk" FOREIGN KEY ("machine_id") REFERENCES "public"."machines"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
CREATE UNIQUE INDEX "machine_tags_name_idx" ON "machine_tags" USING btree ("name");