// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // --------------------------------------------------------------------------- // Session — used by @shopify/shopify-app-session-storage-prisma. // DO NOT change the shape of this model (the session storage depends on it). // --------------------------------------------------------------------------- model Session { id String @id shop String state String isOnline Boolean @default(false) scope String? expires DateTime? accessToken String userId BigInt? firstName String? lastName String? email String? accountOwner Boolean @default(false) locale String? collaborator Boolean? @default(false) emailVerified Boolean? @default(false) refreshToken String? refreshTokenExpires DateTime? } // --------------------------------------------------------------------------- // Multi-tenant app models. Every tenant-scoped row carries `shop` + an index // on it (public-grade isolation from day 1, even with only 2-3 custom stores). // --------------------------------------------------------------------------- // Per-shop merchant configuration for the withdrawal button/flow. model Settings { id String @id @default(cuid()) shop String @unique buttonLabel String @default("Recedere dal contratto qui") confirmLabel String @default("Conferma recesso") brandPrimaryColor String? returnAddress String? defaultWindowDays Int @default(14) withdrawalInfoText String? emailSubject String? emailIntro String? emailNote String? notifyEnabled Boolean @default(true) notifyEmail String? tagEnabled Boolean @default(true) enforceWindow Boolean @default(false) enforceExclusions Boolean @default(false) stateAwareEmail Boolean @default(true) autoCancelUnfulfilled Boolean @default(false) returnAtCustomerExpense Boolean @default(true) returnInstructions String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([shop]) } // Art. 59 exclusions (made-to-order, perishable, sealed-for-hygiene, etc.). model ExclusionRule { id String @id @default(cuid()) shop String scope ExclusionScope targetId String? reason ExclusionReason active Boolean @default(true) createdAt DateTime @default(now()) @@index([shop]) } // A withdrawal (recesso) statement submitted by a consumer. model WithdrawalRequest { id String @id @default(cuid()) shop String orderId String orderName String? customerName String email String statementText String transmittedAt DateTime // legal timestamp of transmission (Art. 54-bis) locale String? channel WithdrawalChannel productType String? status WithdrawalStatus @default(RECEIVED) receiptSentAt DateTime? computedDeadline DateTime? shopifyReturnId String? // GID del Reso Shopify creato (se ordine evaso) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([shop]) @@index([shop, orderId]) } // Append-only audit trail (burden of proof — Art. 54-bis / R6). No updatedAt. model AuditLog { id String @id @default(cuid()) shop String event String payloadHash String? detail String? createdAt DateTime @default(now()) @@index([shop]) } // Webhook idempotency ledger. model WebhookEvent { id String @id @default(cuid()) shop String topic String webhookId String? @unique receivedAt DateTime @default(now()) processed Boolean @default(false) @@index([shop]) } // --------------------------------------------------------------------------- // Enums // --------------------------------------------------------------------------- enum ExclusionScope { PRODUCT COLLECTION TAG ALL } enum ExclusionReason { CUSTOM PERISHABLE HYGIENE OTHER } enum WithdrawalChannel { GUEST ACCOUNT } enum WithdrawalStatus { RECEIVED ACKNOWLEDGED GOODS_PENDING CLOSED REJECTED }