feat/checkout-orders #3
|
|
@ -35,6 +35,8 @@ model ProductVariant {
|
|||
attributes Json @default("{}")
|
||||
active Boolean @default(true)
|
||||
stockItems StockItem[]
|
||||
cartLines CartLine[]
|
||||
orderLines OrderLine[]
|
||||
product Product @relation(fields: [productId, organizationId], references: [id, organizationId], onDelete: Restrict)
|
||||
@@unique([organizationId, sku])
|
||||
@@unique([id, organizationId])
|
||||
|
|
@ -63,5 +65,3 @@ model ProductGroup {
|
|||
@@index([groupId, organizationId])
|
||||
@@map("product_groups")
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
enum OrderStatus {
|
||||
PENDING_PAYMENT
|
||||
CANCELLED
|
||||
}
|
||||
enum CouponKind {
|
||||
FIXED
|
||||
PERCENT
|
||||
}
|
||||
model Cart {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
organizationId String @map("organization_id") @db.Uuid
|
||||
userId String @map("user_id") @db.Uuid
|
||||
version Int @default(0)
|
||||
user User @relation(fields: [userId, organizationId], references: [id, organizationId], onDelete: Restrict)
|
||||
lines CartLine[]
|
||||
@@unique([userId, organizationId])
|
||||
@@unique([id, organizationId])
|
||||
@@map("carts")
|
||||
}
|
||||
model CartLine {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
cartId String @map("cart_id") @db.Uuid
|
||||
organizationId String @map("organization_id") @db.Uuid
|
||||
variantId String @map("variant_id") @db.Uuid
|
||||
quantity Int
|
||||
cart Cart @relation(fields: [cartId, organizationId], references: [id, organizationId], onDelete: Cascade)
|
||||
variant ProductVariant @relation(fields: [variantId, organizationId], references: [id, organizationId], onDelete: Restrict)
|
||||
@@unique([cartId, variantId])
|
||||
@@map("cart_lines")
|
||||
}
|
||||
model Coupon {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
organizationId String @map("organization_id") @db.Uuid
|
||||
code String @db.VarChar(40)
|
||||
kind CouponKind
|
||||
currency String @db.Char(3)
|
||||
amount Decimal? @db.Decimal(12,2)
|
||||
percentBps Int? @map("percent_bps")
|
||||
minimumSubtotal Decimal @default(0) @map("minimum_subtotal") @db.Decimal(12,2)
|
||||
maxUses Int @map("max_uses")
|
||||
perUserLimit Int @map("per_user_limit")
|
||||
startsAt DateTime @map("starts_at") @db.Timestamptz(3)
|
||||
endsAt DateTime @map("ends_at") @db.Timestamptz(3)
|
||||
active Boolean @default(true)
|
||||
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Restrict)
|
||||
orders Order[]
|
||||
@@unique([organizationId, code])
|
||||
@@unique([id, organizationId])
|
||||
@@map("coupons")
|
||||
}
|
||||
model Order {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
organizationId String @map("organization_id") @db.Uuid
|
||||
userId String @map("user_id") @db.Uuid
|
||||
status OrderStatus @default(PENDING_PAYMENT)
|
||||
currency String @db.Char(3)
|
||||
subtotal Decimal @db.Decimal(16,2)
|
||||
discount Decimal @db.Decimal(16,2)
|
||||
merchandiseTotal Decimal @map("merchandise_total") @db.Decimal(16,2)
|
||||
addressSnapshot Json @map("address_snapshot")
|
||||
couponSnapshot Json? @map("coupon_snapshot")
|
||||
couponId String? @map("coupon_id") @db.Uuid
|
||||
idempotencyKey String @map("idempotency_key") @db.Uuid
|
||||
requestHash String @map("request_hash") @db.Char(64)
|
||||
expiresAt DateTime @map("expires_at") @db.Timestamptz(3)
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
user User @relation(fields: [userId, organizationId], references: [id, organizationId], onDelete: Restrict)
|
||||
coupon Coupon? @relation(fields: [couponId, organizationId], references: [id, organizationId], onDelete: Restrict)
|
||||
lines OrderLine[]
|
||||
reservations StockReservation[]
|
||||
@@unique([userId, organizationId, idempotencyKey])
|
||||
@@unique([id, organizationId])
|
||||
@@index([organizationId, userId, createdAt, id])
|
||||
@@unique([id, userId, organizationId])
|
||||
@@index([couponId, status, expiresAt])
|
||||
@@map("orders")
|
||||
}
|
||||
model OrderLine {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
orderId String @map("order_id") @db.Uuid
|
||||
organizationId String @map("organization_id") @db.Uuid
|
||||
variantId String @map("variant_id") @db.Uuid
|
||||
sku String @db.VarChar(64)
|
||||
productName String @map("product_name") @db.VarChar(160)
|
||||
variantName String @map("variant_name") @db.VarChar(160)
|
||||
quantity Int
|
||||
unitPrice Decimal @map("unit_price") @db.Decimal(12,2)
|
||||
lineTotal Decimal @map("line_total") @db.Decimal(16,2)
|
||||
order Order @relation(fields: [orderId, organizationId], references: [id, organizationId], onDelete: Restrict)
|
||||
variant ProductVariant @relation(fields: [variantId, organizationId], references: [id, organizationId], onDelete: Restrict)
|
||||
@@unique([orderId, variantId])
|
||||
@@map("order_lines")
|
||||
}
|
||||
|
|
@ -50,6 +50,8 @@ model StockReservation {
|
|||
organizationId String @map("organization_id") @db.Uuid
|
||||
userId String @map("user_id") @db.Uuid
|
||||
user User @relation(fields: [userId, organizationId], references: [id, organizationId], onDelete: Restrict)
|
||||
orderId String? @map("order_id") @db.Uuid
|
||||
order Order? @relation(fields: [orderId, userId, organizationId], references: [id, userId, organizationId], onDelete: Restrict)
|
||||
quantity Int
|
||||
status ReservationStatus @default(ACTIVE)
|
||||
idempotencyKey String @map("idempotency_key") @db.Uuid
|
||||
|
|
@ -62,5 +64,3 @@ model StockReservation {
|
|||
@@index([userId, organizationId])
|
||||
@@map("stock_reservations")
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,5 +5,9 @@
|
|||
"20260909141447_catalog_addresses": "c89cc9448494d74f8e5ee0005e81b6265bb01d7f000a81c4af27dc3c9855ba84",
|
||||
"20260909141549_inventory": "ab5b0afde7332dd8de3190781dde95bd941bba91ab525308a7436d108cef7e36",
|
||||
"20260909141622_commerce_integrity": "30545784aa33f35783c0170b80de2bded32be8781e25e027c4e5e2e5da8348ac",
|
||||
"20260909153911_inventory_actor_scope": "7d6d2df3a8229032022f5a2ce43ed37c508c4a71743fd2bc6c24786634611d1e"
|
||||
"20260909153911_inventory_actor_scope": "7d6d2df3a8229032022f5a2ce43ed37c508c4a71743fd2bc6c24786634611d1e",
|
||||
"20260910182626_checkout_orders": "aa924db3868c96475c8255800788de42d8e7447cf0a2c84b789b116e533582cb",
|
||||
"20260910182800_checkout_integrity": "e88b972ab16a4cd95f6fb4f097ce41ddfc7917899dc3803a1e7757c3e56c19b2",
|
||||
"20260910183016_checkout_snapshot_guards": "f1c7a95b5a620e06a518a96d457fb493241bd2d5e7deb6b5788ad85c8f3b59f7",
|
||||
"20260910184357_order_reconciliation": "c32e7a917618e02ed1658abb740a7f4e0513a47e0734ad29d90fff325fd05336"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,142 @@
|
|||
-- CreateEnum
|
||||
CREATE TYPE "OrderStatus" AS ENUM ('PENDING_PAYMENT', 'CANCELLED');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "CouponKind" AS ENUM ('FIXED', 'PERCENT');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "stock_reservations" ADD COLUMN "order_id" UUID;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "carts" (
|
||||
"id" UUID NOT NULL,
|
||||
"organization_id" UUID NOT NULL,
|
||||
"user_id" UUID NOT NULL,
|
||||
"version" INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
CONSTRAINT "carts_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "cart_lines" (
|
||||
"id" UUID NOT NULL,
|
||||
"cart_id" UUID NOT NULL,
|
||||
"organization_id" UUID NOT NULL,
|
||||
"variant_id" UUID NOT NULL,
|
||||
"quantity" INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT "cart_lines_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "coupons" (
|
||||
"id" UUID NOT NULL,
|
||||
"organization_id" UUID NOT NULL,
|
||||
"code" VARCHAR(40) NOT NULL,
|
||||
"kind" "CouponKind" NOT NULL,
|
||||
"currency" CHAR(3) NOT NULL,
|
||||
"amount" DECIMAL(12,2),
|
||||
"percent_bps" INTEGER,
|
||||
"minimum_subtotal" DECIMAL(12,2) NOT NULL DEFAULT 0,
|
||||
"max_uses" INTEGER NOT NULL,
|
||||
"per_user_limit" INTEGER NOT NULL,
|
||||
"starts_at" TIMESTAMPTZ(3) NOT NULL,
|
||||
"ends_at" TIMESTAMPTZ(3) NOT NULL,
|
||||
"active" BOOLEAN NOT NULL DEFAULT true,
|
||||
|
||||
CONSTRAINT "coupons_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "orders" (
|
||||
"id" UUID NOT NULL,
|
||||
"organization_id" UUID NOT NULL,
|
||||
"user_id" UUID NOT NULL,
|
||||
"status" "OrderStatus" NOT NULL DEFAULT 'PENDING_PAYMENT',
|
||||
"currency" CHAR(3) NOT NULL,
|
||||
"subtotal" DECIMAL(16,2) NOT NULL,
|
||||
"discount" DECIMAL(16,2) NOT NULL,
|
||||
"merchandise_total" DECIMAL(16,2) NOT NULL,
|
||||
"address_snapshot" JSONB NOT NULL,
|
||||
"coupon_snapshot" JSONB,
|
||||
"coupon_id" UUID,
|
||||
"idempotency_key" UUID NOT NULL,
|
||||
"request_hash" CHAR(64) NOT NULL,
|
||||
"expires_at" TIMESTAMPTZ(3) NOT NULL,
|
||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "orders_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "order_lines" (
|
||||
"id" UUID NOT NULL,
|
||||
"order_id" UUID NOT NULL,
|
||||
"organization_id" UUID NOT NULL,
|
||||
"variant_id" UUID NOT NULL,
|
||||
"sku" VARCHAR(64) NOT NULL,
|
||||
"product_name" VARCHAR(160) NOT NULL,
|
||||
"variant_name" VARCHAR(160) NOT NULL,
|
||||
"quantity" INTEGER NOT NULL,
|
||||
"unit_price" DECIMAL(12,2) NOT NULL,
|
||||
"line_total" DECIMAL(16,2) NOT NULL,
|
||||
|
||||
CONSTRAINT "order_lines_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "carts_user_id_organization_id_key" ON "carts"("user_id", "organization_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "carts_id_organization_id_key" ON "carts"("id", "organization_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "cart_lines_cart_id_variant_id_key" ON "cart_lines"("cart_id", "variant_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "coupons_organization_id_code_key" ON "coupons"("organization_id", "code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "coupons_id_organization_id_key" ON "coupons"("id", "organization_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "orders_organization_id_user_id_created_at_id_idx" ON "orders"("organization_id", "user_id", "created_at", "id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "orders_coupon_id_status_expires_at_idx" ON "orders"("coupon_id", "status", "expires_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "orders_user_id_organization_id_idempotency_key_key" ON "orders"("user_id", "organization_id", "idempotency_key");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "orders_id_organization_id_key" ON "orders"("id", "organization_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "order_lines_order_id_variant_id_key" ON "order_lines"("order_id", "variant_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "carts" ADD CONSTRAINT "carts_user_id_organization_id_fkey" FOREIGN KEY ("user_id", "organization_id") REFERENCES "users"("id", "organization_id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "cart_lines" ADD CONSTRAINT "cart_lines_cart_id_organization_id_fkey" FOREIGN KEY ("cart_id", "organization_id") REFERENCES "carts"("id", "organization_id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "cart_lines" ADD CONSTRAINT "cart_lines_variant_id_organization_id_fkey" FOREIGN KEY ("variant_id", "organization_id") REFERENCES "product_variants"("id", "organization_id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "coupons" ADD CONSTRAINT "coupons_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "organizations"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "orders" ADD CONSTRAINT "orders_user_id_organization_id_fkey" FOREIGN KEY ("user_id", "organization_id") REFERENCES "users"("id", "organization_id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "orders" ADD CONSTRAINT "orders_coupon_id_organization_id_fkey" FOREIGN KEY ("coupon_id", "organization_id") REFERENCES "coupons"("id", "organization_id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "order_lines" ADD CONSTRAINT "order_lines_order_id_organization_id_fkey" FOREIGN KEY ("order_id", "organization_id") REFERENCES "orders"("id", "organization_id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "order_lines" ADD CONSTRAINT "order_lines_variant_id_organization_id_fkey" FOREIGN KEY ("variant_id", "organization_id") REFERENCES "product_variants"("id", "organization_id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "stock_reservations" ADD CONSTRAINT "stock_reservations_order_id_organization_id_fkey" FOREIGN KEY ("order_id", "organization_id") REFERENCES "orders"("id", "organization_id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
-- DropForeignKey
|
||||
ALTER TABLE "stock_reservations" DROP CONSTRAINT "stock_reservations_order_id_organization_id_fkey";
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "orders_id_user_id_organization_id_key" ON "orders"("id", "user_id", "organization_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "stock_reservations" ADD CONSTRAINT "stock_reservations_order_id_user_id_organization_id_fkey" FOREIGN KEY ("order_id", "user_id", "organization_id") REFERENCES "orders"("id", "user_id", "organization_id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
ALTER TABLE carts ADD CONSTRAINT cart_version_nonnegative CHECK (version >= 0);
|
||||
ALTER TABLE cart_lines ADD CONSTRAINT cart_quantity_bounds CHECK (quantity BETWEEN 1 AND 100);
|
||||
ALTER TABLE coupons ADD CONSTRAINT coupon_rule_valid CHECK (
|
||||
(kind = 'FIXED' AND amount IS NOT NULL AND amount > 0 AND percent_bps IS NULL)
|
||||
OR (kind = 'PERCENT' AND amount IS NULL AND percent_bps BETWEEN 1 AND 10000 AND percent_bps IS NOT NULL)
|
||||
);
|
||||
ALTER TABLE coupons ADD CONSTRAINT coupon_limits_valid CHECK (
|
||||
minimum_subtotal >= 0 AND max_uses > 0 AND per_user_limit > 0 AND per_user_limit <= max_uses
|
||||
AND ends_at > starts_at AND code ~ '^[A-Z0-9][A-Z0-9_-]{0,39}$'
|
||||
AND currency IN ('INR', 'USD', 'EUR', 'GBP')
|
||||
);
|
||||
ALTER TABLE orders ADD CONSTRAINT order_totals_valid CHECK (
|
||||
subtotal > 0 AND discount >= 0 AND discount <= subtotal
|
||||
AND merchandise_total = subtotal - discount AND expires_at > created_at
|
||||
);
|
||||
ALTER TABLE order_lines ADD CONSTRAINT order_line_totals_valid CHECK (
|
||||
quantity BETWEEN 1 AND 100 AND unit_price > 0 AND line_total = unit_price * quantity
|
||||
);
|
||||
|
||||
CREATE FUNCTION protect_order_snapshot() RETURNS trigger LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
IF TG_OP = 'DELETE' THEN RAISE EXCEPTION 'Orders cannot be deleted'; END IF;
|
||||
IF (to_jsonb(NEW) - 'status') IS DISTINCT FROM (to_jsonb(OLD) - 'status') THEN
|
||||
RAISE EXCEPTION 'Order snapshots are immutable';
|
||||
END IF;
|
||||
IF OLD.status = 'CANCELLED' AND NEW.status <> 'CANCELLED' THEN
|
||||
RAISE EXCEPTION 'Cancelled orders cannot be reopened';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
CREATE TRIGGER orders_snapshot_immutable BEFORE UPDATE OR DELETE ON orders
|
||||
FOR EACH ROW EXECUTE FUNCTION protect_order_snapshot();
|
||||
|
||||
CREATE FUNCTION protect_order_line() RETURNS trigger LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
RAISE EXCEPTION 'Order lines are immutable';
|
||||
END;
|
||||
$$;
|
||||
CREATE TRIGGER order_lines_immutable BEFORE UPDATE OR DELETE ON order_lines
|
||||
FOR EACH ROW EXECUTE FUNCTION protect_order_line();
|
||||
|
||||
CREATE FUNCTION protect_coupon_rules() RETURNS trigger LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
IF (to_jsonb(NEW) - 'active') IS DISTINCT FROM (to_jsonb(OLD) - 'active') THEN
|
||||
RAISE EXCEPTION 'Create a new coupon to change discount rules';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
CREATE TRIGGER coupon_rules_immutable BEFORE UPDATE ON coupons
|
||||
FOR EACH ROW EXECUTE FUNCTION protect_coupon_rules();
|
||||
|
||||
CREATE FUNCTION protect_order_reservation() RETURNS trigger LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
IF OLD.order_id IS NOT NULL THEN
|
||||
IF TG_OP = 'DELETE' THEN RAISE EXCEPTION 'Order reservations cannot be deleted'; END IF;
|
||||
IF (to_jsonb(NEW) - 'status') IS DISTINCT FROM (to_jsonb(OLD) - 'status') THEN
|
||||
RAISE EXCEPTION 'Order reservation allocation is immutable';
|
||||
END IF;
|
||||
END IF;
|
||||
IF TG_OP = 'DELETE' THEN RETURN OLD; END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
CREATE TRIGGER order_reservation_immutable BEFORE UPDATE OR DELETE ON stock_reservations
|
||||
FOR EACH ROW EXECUTE FUNCTION protect_order_reservation();
|
||||
|
||||
UPDATE roles SET permissions = ARRAY(
|
||||
SELECT DISTINCT permission FROM unnest(permissions || ARRAY[
|
||||
'coupons.manage', 'orders.read', 'orders.manage'
|
||||
]::text[]) AS permission ORDER BY permission
|
||||
) WHERE is_system = true;
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
-- Deferred reconciliation permits nested line creation in the same transaction
|
||||
-- while preventing incomplete orders or later additions to an existing snapshot.
|
||||
CREATE FUNCTION reconcile_order_lines() RETURNS trigger LANGUAGE plpgsql AS $$
|
||||
DECLARE
|
||||
target_order UUID;
|
||||
expected NUMERIC;
|
||||
actual NUMERIC;
|
||||
BEGIN
|
||||
IF TG_TABLE_NAME = 'orders' THEN target_order := NEW.id;
|
||||
ELSE target_order := NEW.order_id;
|
||||
END IF;
|
||||
SELECT subtotal INTO expected FROM orders WHERE id = target_order;
|
||||
SELECT COALESCE(SUM(line_total), 0) INTO actual FROM order_lines WHERE order_id = target_order;
|
||||
IF expected IS DISTINCT FROM actual THEN
|
||||
RAISE EXCEPTION 'Order subtotal does not match lines';
|
||||
END IF;
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$;
|
||||
CREATE CONSTRAINT TRIGGER orders_reconcile_lines AFTER INSERT ON orders
|
||||
DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION reconcile_order_lines();
|
||||
CREATE CONSTRAINT TRIGGER order_lines_reconcile_total AFTER INSERT ON order_lines
|
||||
DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION reconcile_order_lines();
|
||||
|
|
@ -22,6 +22,7 @@ model Organization {
|
|||
products Product[]
|
||||
catalogGroups CatalogGroup[]
|
||||
warehouses Warehouse[]
|
||||
coupons Coupon[]
|
||||
@@map("organizations")
|
||||
}
|
||||
model User {
|
||||
|
|
@ -41,6 +42,8 @@ model User {
|
|||
addresses Address[]
|
||||
stockEntries StockLedger[]
|
||||
stockReservations StockReservation[]
|
||||
carts Cart[]
|
||||
orders Order[]
|
||||
@@unique([organizationId, email])
|
||||
@@unique([id, organizationId])
|
||||
@@index([organizationId, createdAt, id])
|
||||
|
|
|
|||
Loading…
Reference in New Issue