diff --git a/apps/api/Dockerfile b/apps/api/Dockerfile index d5668da..360c1a4 100644 --- a/apps/api/Dockerfile +++ b/apps/api/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.arvancloud.ir/node:20-alpine +FROM docker.arvancloud.ir/node:20-bookworm-slim # Pre-built on the developer machine: # pnpm build:release @@ -7,6 +7,8 @@ WORKDIR /app ENV NODE_ENV=production # Vendored Prisma lives in ./vendor (not node_modules — that path is gitignored) ENV NODE_PATH=/app/vendor +# Force Debian OpenSSL 3 engine (bookworm-slim); avoid wrong musl/openssl-1.1 pick +ENV PRISMA_QUERY_ENGINE_LIBRARY=/app/vendor/.prisma/client/libquery_engine-debian-openssl-3.0.x.so.node COPY release/ ./ diff --git a/apps/api/prisma/schema.prisma b/apps/api/prisma/schema.prisma index af8bd7c..c2d9644 100644 --- a/apps/api/prisma/schema.prisma +++ b/apps/api/prisma/schema.prisma @@ -5,8 +5,8 @@ generator client { provider = "prisma-client-js" - // native = local Windows/macOS; linux-musl* = Arvan/Alpine Docker (OpenSSL detect varies) - binaryTargets = ["native", "linux-musl", "linux-musl-openssl-3.0.x"] + // native = local Windows/macOS; debian-openssl-3.0.x = node:20-bookworm-slim (Arvan) + binaryTargets = ["native", "debian-openssl-3.0.x"] } datasource db { diff --git a/apps/api/release/vendor/.prisma/client/edge.js b/apps/api/release/vendor/.prisma/client/edge.js index 39b1491..33a27b9 100644 --- a/apps/api/release/vendor/.prisma/client/edge.js +++ b/apps/api/release/vendor/.prisma/client/edge.js @@ -427,11 +427,7 @@ const config = { }, { "fromEnvVar": null, - "value": "linux-musl" - }, - { - "fromEnvVar": null, - "value": "linux-musl-openssl-3.0.x" + "value": "debian-openssl-3.0.x" } ], "previewFeatures": [], @@ -456,8 +452,8 @@ const config = { } } }, - "inlineSchema": "// Gold Banking System — core schema\n// Canonical accounting model: legacy \"System A\" gram ledger (see migration notes).\n// The external \"Tahesab\" service is retired — LedgerEntry is the sole source of truth.\n// All money/weight fields use Decimal, never Float, to avoid rounding drift in debt math.\n\ngenerator client {\n provider = \"prisma-client-js\"\n // native = local Windows/macOS; linux-musl* = Arvan/Alpine Docker (OpenSSL detect varies)\n binaryTargets = [\"native\", \"linux-musl\", \"linux-musl-openssl-3.0.x\"]\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"DATABASE_URL\")\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Enums\n// ─────────────────────────────────────────────────────────────────────────\n\nenum AdminRole {\n SUPER_ADMIN\n SYSTEM_APPROVER // legacy: تایید مدیر سیستم\n SALES_EXPERT // legacy Customer.type=1, also acts as a referral \"reagent\"\n WORKSHOP_SUPERVISOR\n ACCOUNTANT\n}\n\nenum VerificationStep {\n UNVERIFIED\n STEP_ONE_SUBMITTED\n STEP_TWO_SUBMITTED\n VERIFIED\n}\n\nenum OrderStatus {\n SUSPENDED // credit limit (goldCreditLimitGrams) exceeded at placement\n PLACED\n ADMIN_APPROVED\n SOURCING\n PREPARING\n DELIVERED_TO_AGENT\n DELIVERED_TO_CUSTOMER // debt recognition point — mirrors legacy sts>4\n CANCELLED\n}\n\nenum PreOrderStatus {\n PLACED\n ADMIN_APPROVED\n WORKSHOP_APPROVED\n READY_TO_SHIP\n DELIVERED\n CANCELLED\n}\n\nenum LedgerCurrency {\n GOLD_GRAM\n RIAL\n}\n\nenum LedgerDirection {\n DEBIT // increases what the customer owes\n CREDIT // decreases what the customer owes\n}\n\nenum LedgerSourceType {\n ORDER_STOCK\n ORDER_PRE_ORDER\n REPAYMENT_MELTED_GOLD // karat-normalized: amount * karatPerMille / 750\n REPAYMENT_TRANSFER\n REPAYMENT_CHEQUE\n REPAYMENT_CASH // price-per-gram-normalized via pricePerGramRialAtEntry\n WALLET_BONUS\n ADJUSTMENT\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Identity\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel City {\n id String @id @default(cuid())\n name String\n province String?\n customers Customer[]\n addresses CustomerAddress[]\n branches Branch[]\n preOrders PreOrder[]\n}\n\nmodel AdminUser {\n id String @id @default(cuid())\n fullName String\n mobile String @unique\n passwordHash String\n role AdminRole\n branchId String?\n branch Branch? @relation(fields: [branchId], references: [id])\n wagePercent Decimal? @db.Decimal(6, 3) // legacy Customer.percent, commission on referred orders\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n referredCustomers Customer[] @relation(\"ReferralAgent\")\n postedLedgerEntries LedgerEntry[]\n orderStatusEvents OrderStatusEvent[]\n preOrderStatusEvents PreOrderStatusEvent[]\n\n @@index([role])\n}\n\nmodel Customer {\n id String @id @default(cuid())\n fullName String\n mobile String @unique\n nationalCode String @unique\n passwordHash String\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n\n referredByAgentId String?\n referredByAgent AdminUser? @relation(\"ReferralAgent\", fields: [referredByAgentId], references: [id])\n\n // Credit limit + cached ledger balances (source of truth is LedgerEntry; these\n // are denormalized for fast reads and reconciled by a scheduled job).\n goldCreditLimitGrams Decimal @default(0) @db.Decimal(14, 4) // legacy gram_level\n goldDebtGrams Decimal @default(0) @db.Decimal(14, 4) // legacy remain\n fiatDebtRial Decimal @default(0) @db.Decimal(20, 2) // legacy rial_remain\n\n verificationStep VerificationStep @default(UNVERIFIED)\n nationalIdImageUrl String?\n businessLicenseUrl String?\n shopImageUrl String?\n\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n addresses CustomerAddress[]\n orders Order[]\n preOrders PreOrder[]\n ledgerEntries LedgerEntry[]\n walletBonuses WalletBonus[]\n\n @@index([referredByAgentId])\n}\n\nmodel CustomerAddress {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n addressLine String\n isDefault Boolean @default(false)\n}\n\nmodel Branch {\n id String @id @default(cuid())\n name String\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n adminUsers AdminUser[]\n orders Order[]\n}\n\nmodel Workshop {\n id String @id @default(cuid())\n name String\n isActive Boolean @default(true)\n preOrders PreOrder[]\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Catalog — item reference catalog\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel Category {\n id String @id @default(cuid())\n title String\n parentId String?\n parent Category? @relation(\"CategoryTree\", fields: [parentId], references: [id])\n children Category[] @relation(\"CategoryTree\")\n products Product[]\n}\n\nmodel Brand {\n id String @id @default(cuid())\n title String\n products Product[]\n}\n\n/// Curated product groupings — replaces legacy \"Ciny/CinyProduct\" (a tagging\n/// feature, NOT a gold-price snapshot mechanism, despite the misleading legacy name).\nmodel Collection {\n id String @id @default(cuid())\n title String\n description String?\n isActive Boolean @default(true)\n products CollectionProduct[]\n}\n\nmodel CollectionProduct {\n collectionId String\n collection Collection @relation(fields: [collectionId], references: [id])\n productId String\n product Product @relation(fields: [productId], references: [id])\n\n @@id([collectionId, productId])\n}\n\nmodel Product {\n id String @id @default(cuid())\n title String\n description String?\n categoryId String?\n category Category? @relation(fields: [categoryId], references: [id])\n brandId String?\n brand Brand? @relation(fields: [brandId], references: [id])\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n variants ProductVariant[]\n images ProductImage[]\n collections CollectionProduct[]\n}\n\nmodel ProductImage {\n id String @id @default(cuid())\n productId String\n product Product @relation(fields: [productId], references: [id])\n url String\n sortOrder Int @default(0)\n}\n\nmodel ProductVariant {\n id String @id @default(cuid())\n productId String\n product Product @relation(fields: [productId], references: [id])\n title String\n sku String? @unique\n /// Base/raw weight before making charge (\"rash weight\").\n rashWeightGrams Decimal @db.Decimal(14, 4)\n /// Default making-charge percentage applied on top of rash weight.\n defaultWagePercent Decimal @db.Decimal(6, 3)\n isActive Boolean @default(true)\n\n orderItems OrderItem[]\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Orders — stock orders (debt-bearing)\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel Order {\n id String @id @default(cuid())\n orderNumber String @unique\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n branchId String?\n branch Branch? @relation(fields: [branchId], references: [id])\n status OrderStatus @default(PLACED)\n\n /// Total gold weight incl. wage across all items (\"allGram\" / order.w).\n totalGoldWeightGrams Decimal @db.Decimal(14, 4)\n /// Pure making-charge weight component (totalGoldWeightGrams minus raw weight sum).\n wageWeightGrams Decimal @db.Decimal(14, 4)\n /// Flat Rial charge independent of gold price (e.g. stones) — legacy rial_input.\n flatRialCharge Decimal @default(0) @db.Decimal(20, 2)\n\n isSuspendedOverCreditLimit Boolean @default(false)\n deliveredAt DateTime? // setting this is what triggers debt recognition\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n items OrderItem[]\n statusEvents OrderStatusEvent[]\n ledgerEntries LedgerEntry[]\n convertedFromPreOrder PreOrder?\n\n @@index([customerId])\n @@index([status])\n}\n\nmodel OrderItem {\n id String @id @default(cuid())\n orderId String\n order Order @relation(fields: [orderId], references: [id])\n productVariantId String\n productVariant ProductVariant @relation(fields: [productVariantId], references: [id])\n quantity Int\n\n // Snapshots at order time — never re-read from ProductVariant after placement.\n rashWeightGramsSnapshot Decimal @db.Decimal(14, 4)\n wagePercentSnapshot Decimal @db.Decimal(6, 3)\n discountPercentSnapshot Decimal @default(0) @db.Decimal(6, 3)\n /// computed: rash*(1 + (wagePercent-discount)/100)\n computedWeightGrams Decimal @db.Decimal(14, 4)\n flatRialCharge Decimal @default(0) @db.Decimal(20, 2)\n}\n\nmodel OrderStatusEvent {\n id String @id @default(cuid())\n orderId String\n order Order @relation(fields: [orderId], references: [id])\n fromStatus OrderStatus?\n toStatus OrderStatus\n note String?\n actorAdminUserId String?\n actorAdminUser AdminUser? @relation(fields: [actorAdminUserId], references: [id])\n createdAt DateTime @default(now())\n\n @@index([orderId])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Pre-orders — custom manufacturing requests (not debt-bearing until converted)\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel PreOrder {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n workshopId String?\n workshop Workshop? @relation(fields: [workshopId], references: [id])\n status PreOrderStatus @default(PLACED)\n\n designImageUrl String?\n colorName String?\n sizeSpec String?\n stoneColor String?\n minaColor String?\n estimatedWeightGrams Decimal? @db.Decimal(14, 4)\n estimatedPriceRial Decimal? @db.Decimal(20, 2)\n description String?\n\n /// Once priced, an admin converts this into a real (debt-bearing) Order.\n convertedOrderId String? @unique\n convertedOrder Order? @relation(fields: [convertedOrderId], references: [id])\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n statusEvents PreOrderStatusEvent[]\n\n @@index([customerId])\n}\n\nmodel PreOrderStatusEvent {\n id String @id @default(cuid())\n preOrderId String\n preOrder PreOrder @relation(fields: [preOrderId], references: [id])\n /// Field-level diff, e.g. {\"status\": {\"old\": \"PLACED\", \"new\": \"ADMIN_APPROVED\"}}\n changes Json\n actorAdminUserId String?\n actorAdminUser AdminUser? @relation(fields: [actorAdminUserId], references: [id])\n ipAddress String?\n createdAt DateTime @default(now())\n\n @@index([preOrderId])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Accounting — the dual-currency ledger (core of the system)\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel LedgerEntry {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n\n currency LedgerCurrency\n direction LedgerDirection\n sourceType LedgerSourceType\n /// Always positive; direction determines the sign applied to the customer balance.\n amount Decimal @db.Decimal(20, 4)\n\n /// Purity of the physical instrument, for REPAYMENT_MELTED_GOLD (e.g. 750, 916).\n karatPerMille Int?\n /// Gold price/gram at conversion time, for REPAYMENT_CASH.\n pricePerGramRialAtEntry Decimal? @db.Decimal(20, 2)\n\n orderId String?\n order Order? @relation(fields: [orderId], references: [id])\n\n /// Pending entries don't count toward the cached customer balance until confirmed.\n confirmed Boolean @default(false)\n confirmedAt DateTime?\n postedByAdminUserId String?\n postedByAdminUser AdminUser? @relation(fields: [postedByAdminUserId], references: [id])\n\n note String?\n createdAt DateTime @default(now())\n\n @@index([customerId, currency])\n @@index([orderId])\n}\n\n/// Replaces legacy WebServiceGold / ConfigStatic.gold_price — timestamped price feed.\nmodel GoldPriceQuote {\n id String @id @default(cuid())\n karatPerMille Int\n pricePerGramRial Decimal @db.Decimal(20, 2)\n source String\n quotedAt DateTime @default(now())\n\n @@index([karatPerMille, quotedAt])\n}\n\n/// Time-limited gold cashback bonus — replaces the legacy Rooyesh\\Wallet gold credit.\nmodel WalletBonus {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n orderId String?\n goldGrams Decimal @db.Decimal(14, 4)\n grantedAt DateTime @default(now())\n expiresAt DateTime\n consumedAt DateTime?\n isActive Boolean @default(true)\n\n @@index([customerId, isActive])\n}\n", - "inlineSchemaHash": "3d2a553a520468aa24bdcb865818fc11df79984db4b59e1e7ab13994e43c8a31", + "inlineSchema": "// Gold Banking System — core schema\n// Canonical accounting model: legacy \"System A\" gram ledger (see migration notes).\n// The external \"Tahesab\" service is retired — LedgerEntry is the sole source of truth.\n// All money/weight fields use Decimal, never Float, to avoid rounding drift in debt math.\n\ngenerator client {\n provider = \"prisma-client-js\"\n // native = local Windows/macOS; debian-openssl-3.0.x = node:20-bookworm-slim (Arvan)\n binaryTargets = [\"native\", \"debian-openssl-3.0.x\"]\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"DATABASE_URL\")\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Enums\n// ─────────────────────────────────────────────────────────────────────────\n\nenum AdminRole {\n SUPER_ADMIN\n SYSTEM_APPROVER // legacy: تایید مدیر سیستم\n SALES_EXPERT // legacy Customer.type=1, also acts as a referral \"reagent\"\n WORKSHOP_SUPERVISOR\n ACCOUNTANT\n}\n\nenum VerificationStep {\n UNVERIFIED\n STEP_ONE_SUBMITTED\n STEP_TWO_SUBMITTED\n VERIFIED\n}\n\nenum OrderStatus {\n SUSPENDED // credit limit (goldCreditLimitGrams) exceeded at placement\n PLACED\n ADMIN_APPROVED\n SOURCING\n PREPARING\n DELIVERED_TO_AGENT\n DELIVERED_TO_CUSTOMER // debt recognition point — mirrors legacy sts>4\n CANCELLED\n}\n\nenum PreOrderStatus {\n PLACED\n ADMIN_APPROVED\n WORKSHOP_APPROVED\n READY_TO_SHIP\n DELIVERED\n CANCELLED\n}\n\nenum LedgerCurrency {\n GOLD_GRAM\n RIAL\n}\n\nenum LedgerDirection {\n DEBIT // increases what the customer owes\n CREDIT // decreases what the customer owes\n}\n\nenum LedgerSourceType {\n ORDER_STOCK\n ORDER_PRE_ORDER\n REPAYMENT_MELTED_GOLD // karat-normalized: amount * karatPerMille / 750\n REPAYMENT_TRANSFER\n REPAYMENT_CHEQUE\n REPAYMENT_CASH // price-per-gram-normalized via pricePerGramRialAtEntry\n WALLET_BONUS\n ADJUSTMENT\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Identity\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel City {\n id String @id @default(cuid())\n name String\n province String?\n customers Customer[]\n addresses CustomerAddress[]\n branches Branch[]\n preOrders PreOrder[]\n}\n\nmodel AdminUser {\n id String @id @default(cuid())\n fullName String\n mobile String @unique\n passwordHash String\n role AdminRole\n branchId String?\n branch Branch? @relation(fields: [branchId], references: [id])\n wagePercent Decimal? @db.Decimal(6, 3) // legacy Customer.percent, commission on referred orders\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n referredCustomers Customer[] @relation(\"ReferralAgent\")\n postedLedgerEntries LedgerEntry[]\n orderStatusEvents OrderStatusEvent[]\n preOrderStatusEvents PreOrderStatusEvent[]\n\n @@index([role])\n}\n\nmodel Customer {\n id String @id @default(cuid())\n fullName String\n mobile String @unique\n nationalCode String @unique\n passwordHash String\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n\n referredByAgentId String?\n referredByAgent AdminUser? @relation(\"ReferralAgent\", fields: [referredByAgentId], references: [id])\n\n // Credit limit + cached ledger balances (source of truth is LedgerEntry; these\n // are denormalized for fast reads and reconciled by a scheduled job).\n goldCreditLimitGrams Decimal @default(0) @db.Decimal(14, 4) // legacy gram_level\n goldDebtGrams Decimal @default(0) @db.Decimal(14, 4) // legacy remain\n fiatDebtRial Decimal @default(0) @db.Decimal(20, 2) // legacy rial_remain\n\n verificationStep VerificationStep @default(UNVERIFIED)\n nationalIdImageUrl String?\n businessLicenseUrl String?\n shopImageUrl String?\n\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n addresses CustomerAddress[]\n orders Order[]\n preOrders PreOrder[]\n ledgerEntries LedgerEntry[]\n walletBonuses WalletBonus[]\n\n @@index([referredByAgentId])\n}\n\nmodel CustomerAddress {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n addressLine String\n isDefault Boolean @default(false)\n}\n\nmodel Branch {\n id String @id @default(cuid())\n name String\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n adminUsers AdminUser[]\n orders Order[]\n}\n\nmodel Workshop {\n id String @id @default(cuid())\n name String\n isActive Boolean @default(true)\n preOrders PreOrder[]\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Catalog — item reference catalog\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel Category {\n id String @id @default(cuid())\n title String\n parentId String?\n parent Category? @relation(\"CategoryTree\", fields: [parentId], references: [id])\n children Category[] @relation(\"CategoryTree\")\n products Product[]\n}\n\nmodel Brand {\n id String @id @default(cuid())\n title String\n products Product[]\n}\n\n/// Curated product groupings — replaces legacy \"Ciny/CinyProduct\" (a tagging\n/// feature, NOT a gold-price snapshot mechanism, despite the misleading legacy name).\nmodel Collection {\n id String @id @default(cuid())\n title String\n description String?\n isActive Boolean @default(true)\n products CollectionProduct[]\n}\n\nmodel CollectionProduct {\n collectionId String\n collection Collection @relation(fields: [collectionId], references: [id])\n productId String\n product Product @relation(fields: [productId], references: [id])\n\n @@id([collectionId, productId])\n}\n\nmodel Product {\n id String @id @default(cuid())\n title String\n description String?\n categoryId String?\n category Category? @relation(fields: [categoryId], references: [id])\n brandId String?\n brand Brand? @relation(fields: [brandId], references: [id])\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n variants ProductVariant[]\n images ProductImage[]\n collections CollectionProduct[]\n}\n\nmodel ProductImage {\n id String @id @default(cuid())\n productId String\n product Product @relation(fields: [productId], references: [id])\n url String\n sortOrder Int @default(0)\n}\n\nmodel ProductVariant {\n id String @id @default(cuid())\n productId String\n product Product @relation(fields: [productId], references: [id])\n title String\n sku String? @unique\n /// Base/raw weight before making charge (\"rash weight\").\n rashWeightGrams Decimal @db.Decimal(14, 4)\n /// Default making-charge percentage applied on top of rash weight.\n defaultWagePercent Decimal @db.Decimal(6, 3)\n isActive Boolean @default(true)\n\n orderItems OrderItem[]\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Orders — stock orders (debt-bearing)\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel Order {\n id String @id @default(cuid())\n orderNumber String @unique\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n branchId String?\n branch Branch? @relation(fields: [branchId], references: [id])\n status OrderStatus @default(PLACED)\n\n /// Total gold weight incl. wage across all items (\"allGram\" / order.w).\n totalGoldWeightGrams Decimal @db.Decimal(14, 4)\n /// Pure making-charge weight component (totalGoldWeightGrams minus raw weight sum).\n wageWeightGrams Decimal @db.Decimal(14, 4)\n /// Flat Rial charge independent of gold price (e.g. stones) — legacy rial_input.\n flatRialCharge Decimal @default(0) @db.Decimal(20, 2)\n\n isSuspendedOverCreditLimit Boolean @default(false)\n deliveredAt DateTime? // setting this is what triggers debt recognition\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n items OrderItem[]\n statusEvents OrderStatusEvent[]\n ledgerEntries LedgerEntry[]\n convertedFromPreOrder PreOrder?\n\n @@index([customerId])\n @@index([status])\n}\n\nmodel OrderItem {\n id String @id @default(cuid())\n orderId String\n order Order @relation(fields: [orderId], references: [id])\n productVariantId String\n productVariant ProductVariant @relation(fields: [productVariantId], references: [id])\n quantity Int\n\n // Snapshots at order time — never re-read from ProductVariant after placement.\n rashWeightGramsSnapshot Decimal @db.Decimal(14, 4)\n wagePercentSnapshot Decimal @db.Decimal(6, 3)\n discountPercentSnapshot Decimal @default(0) @db.Decimal(6, 3)\n /// computed: rash*(1 + (wagePercent-discount)/100)\n computedWeightGrams Decimal @db.Decimal(14, 4)\n flatRialCharge Decimal @default(0) @db.Decimal(20, 2)\n}\n\nmodel OrderStatusEvent {\n id String @id @default(cuid())\n orderId String\n order Order @relation(fields: [orderId], references: [id])\n fromStatus OrderStatus?\n toStatus OrderStatus\n note String?\n actorAdminUserId String?\n actorAdminUser AdminUser? @relation(fields: [actorAdminUserId], references: [id])\n createdAt DateTime @default(now())\n\n @@index([orderId])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Pre-orders — custom manufacturing requests (not debt-bearing until converted)\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel PreOrder {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n workshopId String?\n workshop Workshop? @relation(fields: [workshopId], references: [id])\n status PreOrderStatus @default(PLACED)\n\n designImageUrl String?\n colorName String?\n sizeSpec String?\n stoneColor String?\n minaColor String?\n estimatedWeightGrams Decimal? @db.Decimal(14, 4)\n estimatedPriceRial Decimal? @db.Decimal(20, 2)\n description String?\n\n /// Once priced, an admin converts this into a real (debt-bearing) Order.\n convertedOrderId String? @unique\n convertedOrder Order? @relation(fields: [convertedOrderId], references: [id])\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n statusEvents PreOrderStatusEvent[]\n\n @@index([customerId])\n}\n\nmodel PreOrderStatusEvent {\n id String @id @default(cuid())\n preOrderId String\n preOrder PreOrder @relation(fields: [preOrderId], references: [id])\n /// Field-level diff, e.g. {\"status\": {\"old\": \"PLACED\", \"new\": \"ADMIN_APPROVED\"}}\n changes Json\n actorAdminUserId String?\n actorAdminUser AdminUser? @relation(fields: [actorAdminUserId], references: [id])\n ipAddress String?\n createdAt DateTime @default(now())\n\n @@index([preOrderId])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Accounting — the dual-currency ledger (core of the system)\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel LedgerEntry {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n\n currency LedgerCurrency\n direction LedgerDirection\n sourceType LedgerSourceType\n /// Always positive; direction determines the sign applied to the customer balance.\n amount Decimal @db.Decimal(20, 4)\n\n /// Purity of the physical instrument, for REPAYMENT_MELTED_GOLD (e.g. 750, 916).\n karatPerMille Int?\n /// Gold price/gram at conversion time, for REPAYMENT_CASH.\n pricePerGramRialAtEntry Decimal? @db.Decimal(20, 2)\n\n orderId String?\n order Order? @relation(fields: [orderId], references: [id])\n\n /// Pending entries don't count toward the cached customer balance until confirmed.\n confirmed Boolean @default(false)\n confirmedAt DateTime?\n postedByAdminUserId String?\n postedByAdminUser AdminUser? @relation(fields: [postedByAdminUserId], references: [id])\n\n note String?\n createdAt DateTime @default(now())\n\n @@index([customerId, currency])\n @@index([orderId])\n}\n\n/// Replaces legacy WebServiceGold / ConfigStatic.gold_price — timestamped price feed.\nmodel GoldPriceQuote {\n id String @id @default(cuid())\n karatPerMille Int\n pricePerGramRial Decimal @db.Decimal(20, 2)\n source String\n quotedAt DateTime @default(now())\n\n @@index([karatPerMille, quotedAt])\n}\n\n/// Time-limited gold cashback bonus — replaces the legacy Rooyesh\\Wallet gold credit.\nmodel WalletBonus {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n orderId String?\n goldGrams Decimal @db.Decimal(14, 4)\n grantedAt DateTime @default(now())\n expiresAt DateTime\n consumedAt DateTime?\n isActive Boolean @default(true)\n\n @@index([customerId, isActive])\n}\n", + "inlineSchemaHash": "c0944da476bdb47884e1c9d2aeeb374e6077907fd89970148d89eff210dfe8ab", "copyEngine": true } config.dirname = '/' diff --git a/apps/api/release/vendor/.prisma/client/index.js b/apps/api/release/vendor/.prisma/client/index.js index 5d5e0e0..409be21 100644 --- a/apps/api/release/vendor/.prisma/client/index.js +++ b/apps/api/release/vendor/.prisma/client/index.js @@ -428,11 +428,7 @@ const config = { }, { "fromEnvVar": null, - "value": "linux-musl" - }, - { - "fromEnvVar": null, - "value": "linux-musl-openssl-3.0.x" + "value": "debian-openssl-3.0.x" } ], "previewFeatures": [], @@ -457,8 +453,8 @@ const config = { } } }, - "inlineSchema": "// Gold Banking System — core schema\n// Canonical accounting model: legacy \"System A\" gram ledger (see migration notes).\n// The external \"Tahesab\" service is retired — LedgerEntry is the sole source of truth.\n// All money/weight fields use Decimal, never Float, to avoid rounding drift in debt math.\n\ngenerator client {\n provider = \"prisma-client-js\"\n // native = local Windows/macOS; linux-musl* = Arvan/Alpine Docker (OpenSSL detect varies)\n binaryTargets = [\"native\", \"linux-musl\", \"linux-musl-openssl-3.0.x\"]\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"DATABASE_URL\")\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Enums\n// ─────────────────────────────────────────────────────────────────────────\n\nenum AdminRole {\n SUPER_ADMIN\n SYSTEM_APPROVER // legacy: تایید مدیر سیستم\n SALES_EXPERT // legacy Customer.type=1, also acts as a referral \"reagent\"\n WORKSHOP_SUPERVISOR\n ACCOUNTANT\n}\n\nenum VerificationStep {\n UNVERIFIED\n STEP_ONE_SUBMITTED\n STEP_TWO_SUBMITTED\n VERIFIED\n}\n\nenum OrderStatus {\n SUSPENDED // credit limit (goldCreditLimitGrams) exceeded at placement\n PLACED\n ADMIN_APPROVED\n SOURCING\n PREPARING\n DELIVERED_TO_AGENT\n DELIVERED_TO_CUSTOMER // debt recognition point — mirrors legacy sts>4\n CANCELLED\n}\n\nenum PreOrderStatus {\n PLACED\n ADMIN_APPROVED\n WORKSHOP_APPROVED\n READY_TO_SHIP\n DELIVERED\n CANCELLED\n}\n\nenum LedgerCurrency {\n GOLD_GRAM\n RIAL\n}\n\nenum LedgerDirection {\n DEBIT // increases what the customer owes\n CREDIT // decreases what the customer owes\n}\n\nenum LedgerSourceType {\n ORDER_STOCK\n ORDER_PRE_ORDER\n REPAYMENT_MELTED_GOLD // karat-normalized: amount * karatPerMille / 750\n REPAYMENT_TRANSFER\n REPAYMENT_CHEQUE\n REPAYMENT_CASH // price-per-gram-normalized via pricePerGramRialAtEntry\n WALLET_BONUS\n ADJUSTMENT\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Identity\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel City {\n id String @id @default(cuid())\n name String\n province String?\n customers Customer[]\n addresses CustomerAddress[]\n branches Branch[]\n preOrders PreOrder[]\n}\n\nmodel AdminUser {\n id String @id @default(cuid())\n fullName String\n mobile String @unique\n passwordHash String\n role AdminRole\n branchId String?\n branch Branch? @relation(fields: [branchId], references: [id])\n wagePercent Decimal? @db.Decimal(6, 3) // legacy Customer.percent, commission on referred orders\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n referredCustomers Customer[] @relation(\"ReferralAgent\")\n postedLedgerEntries LedgerEntry[]\n orderStatusEvents OrderStatusEvent[]\n preOrderStatusEvents PreOrderStatusEvent[]\n\n @@index([role])\n}\n\nmodel Customer {\n id String @id @default(cuid())\n fullName String\n mobile String @unique\n nationalCode String @unique\n passwordHash String\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n\n referredByAgentId String?\n referredByAgent AdminUser? @relation(\"ReferralAgent\", fields: [referredByAgentId], references: [id])\n\n // Credit limit + cached ledger balances (source of truth is LedgerEntry; these\n // are denormalized for fast reads and reconciled by a scheduled job).\n goldCreditLimitGrams Decimal @default(0) @db.Decimal(14, 4) // legacy gram_level\n goldDebtGrams Decimal @default(0) @db.Decimal(14, 4) // legacy remain\n fiatDebtRial Decimal @default(0) @db.Decimal(20, 2) // legacy rial_remain\n\n verificationStep VerificationStep @default(UNVERIFIED)\n nationalIdImageUrl String?\n businessLicenseUrl String?\n shopImageUrl String?\n\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n addresses CustomerAddress[]\n orders Order[]\n preOrders PreOrder[]\n ledgerEntries LedgerEntry[]\n walletBonuses WalletBonus[]\n\n @@index([referredByAgentId])\n}\n\nmodel CustomerAddress {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n addressLine String\n isDefault Boolean @default(false)\n}\n\nmodel Branch {\n id String @id @default(cuid())\n name String\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n adminUsers AdminUser[]\n orders Order[]\n}\n\nmodel Workshop {\n id String @id @default(cuid())\n name String\n isActive Boolean @default(true)\n preOrders PreOrder[]\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Catalog — item reference catalog\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel Category {\n id String @id @default(cuid())\n title String\n parentId String?\n parent Category? @relation(\"CategoryTree\", fields: [parentId], references: [id])\n children Category[] @relation(\"CategoryTree\")\n products Product[]\n}\n\nmodel Brand {\n id String @id @default(cuid())\n title String\n products Product[]\n}\n\n/// Curated product groupings — replaces legacy \"Ciny/CinyProduct\" (a tagging\n/// feature, NOT a gold-price snapshot mechanism, despite the misleading legacy name).\nmodel Collection {\n id String @id @default(cuid())\n title String\n description String?\n isActive Boolean @default(true)\n products CollectionProduct[]\n}\n\nmodel CollectionProduct {\n collectionId String\n collection Collection @relation(fields: [collectionId], references: [id])\n productId String\n product Product @relation(fields: [productId], references: [id])\n\n @@id([collectionId, productId])\n}\n\nmodel Product {\n id String @id @default(cuid())\n title String\n description String?\n categoryId String?\n category Category? @relation(fields: [categoryId], references: [id])\n brandId String?\n brand Brand? @relation(fields: [brandId], references: [id])\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n variants ProductVariant[]\n images ProductImage[]\n collections CollectionProduct[]\n}\n\nmodel ProductImage {\n id String @id @default(cuid())\n productId String\n product Product @relation(fields: [productId], references: [id])\n url String\n sortOrder Int @default(0)\n}\n\nmodel ProductVariant {\n id String @id @default(cuid())\n productId String\n product Product @relation(fields: [productId], references: [id])\n title String\n sku String? @unique\n /// Base/raw weight before making charge (\"rash weight\").\n rashWeightGrams Decimal @db.Decimal(14, 4)\n /// Default making-charge percentage applied on top of rash weight.\n defaultWagePercent Decimal @db.Decimal(6, 3)\n isActive Boolean @default(true)\n\n orderItems OrderItem[]\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Orders — stock orders (debt-bearing)\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel Order {\n id String @id @default(cuid())\n orderNumber String @unique\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n branchId String?\n branch Branch? @relation(fields: [branchId], references: [id])\n status OrderStatus @default(PLACED)\n\n /// Total gold weight incl. wage across all items (\"allGram\" / order.w).\n totalGoldWeightGrams Decimal @db.Decimal(14, 4)\n /// Pure making-charge weight component (totalGoldWeightGrams minus raw weight sum).\n wageWeightGrams Decimal @db.Decimal(14, 4)\n /// Flat Rial charge independent of gold price (e.g. stones) — legacy rial_input.\n flatRialCharge Decimal @default(0) @db.Decimal(20, 2)\n\n isSuspendedOverCreditLimit Boolean @default(false)\n deliveredAt DateTime? // setting this is what triggers debt recognition\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n items OrderItem[]\n statusEvents OrderStatusEvent[]\n ledgerEntries LedgerEntry[]\n convertedFromPreOrder PreOrder?\n\n @@index([customerId])\n @@index([status])\n}\n\nmodel OrderItem {\n id String @id @default(cuid())\n orderId String\n order Order @relation(fields: [orderId], references: [id])\n productVariantId String\n productVariant ProductVariant @relation(fields: [productVariantId], references: [id])\n quantity Int\n\n // Snapshots at order time — never re-read from ProductVariant after placement.\n rashWeightGramsSnapshot Decimal @db.Decimal(14, 4)\n wagePercentSnapshot Decimal @db.Decimal(6, 3)\n discountPercentSnapshot Decimal @default(0) @db.Decimal(6, 3)\n /// computed: rash*(1 + (wagePercent-discount)/100)\n computedWeightGrams Decimal @db.Decimal(14, 4)\n flatRialCharge Decimal @default(0) @db.Decimal(20, 2)\n}\n\nmodel OrderStatusEvent {\n id String @id @default(cuid())\n orderId String\n order Order @relation(fields: [orderId], references: [id])\n fromStatus OrderStatus?\n toStatus OrderStatus\n note String?\n actorAdminUserId String?\n actorAdminUser AdminUser? @relation(fields: [actorAdminUserId], references: [id])\n createdAt DateTime @default(now())\n\n @@index([orderId])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Pre-orders — custom manufacturing requests (not debt-bearing until converted)\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel PreOrder {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n workshopId String?\n workshop Workshop? @relation(fields: [workshopId], references: [id])\n status PreOrderStatus @default(PLACED)\n\n designImageUrl String?\n colorName String?\n sizeSpec String?\n stoneColor String?\n minaColor String?\n estimatedWeightGrams Decimal? @db.Decimal(14, 4)\n estimatedPriceRial Decimal? @db.Decimal(20, 2)\n description String?\n\n /// Once priced, an admin converts this into a real (debt-bearing) Order.\n convertedOrderId String? @unique\n convertedOrder Order? @relation(fields: [convertedOrderId], references: [id])\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n statusEvents PreOrderStatusEvent[]\n\n @@index([customerId])\n}\n\nmodel PreOrderStatusEvent {\n id String @id @default(cuid())\n preOrderId String\n preOrder PreOrder @relation(fields: [preOrderId], references: [id])\n /// Field-level diff, e.g. {\"status\": {\"old\": \"PLACED\", \"new\": \"ADMIN_APPROVED\"}}\n changes Json\n actorAdminUserId String?\n actorAdminUser AdminUser? @relation(fields: [actorAdminUserId], references: [id])\n ipAddress String?\n createdAt DateTime @default(now())\n\n @@index([preOrderId])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Accounting — the dual-currency ledger (core of the system)\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel LedgerEntry {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n\n currency LedgerCurrency\n direction LedgerDirection\n sourceType LedgerSourceType\n /// Always positive; direction determines the sign applied to the customer balance.\n amount Decimal @db.Decimal(20, 4)\n\n /// Purity of the physical instrument, for REPAYMENT_MELTED_GOLD (e.g. 750, 916).\n karatPerMille Int?\n /// Gold price/gram at conversion time, for REPAYMENT_CASH.\n pricePerGramRialAtEntry Decimal? @db.Decimal(20, 2)\n\n orderId String?\n order Order? @relation(fields: [orderId], references: [id])\n\n /// Pending entries don't count toward the cached customer balance until confirmed.\n confirmed Boolean @default(false)\n confirmedAt DateTime?\n postedByAdminUserId String?\n postedByAdminUser AdminUser? @relation(fields: [postedByAdminUserId], references: [id])\n\n note String?\n createdAt DateTime @default(now())\n\n @@index([customerId, currency])\n @@index([orderId])\n}\n\n/// Replaces legacy WebServiceGold / ConfigStatic.gold_price — timestamped price feed.\nmodel GoldPriceQuote {\n id String @id @default(cuid())\n karatPerMille Int\n pricePerGramRial Decimal @db.Decimal(20, 2)\n source String\n quotedAt DateTime @default(now())\n\n @@index([karatPerMille, quotedAt])\n}\n\n/// Time-limited gold cashback bonus — replaces the legacy Rooyesh\\Wallet gold credit.\nmodel WalletBonus {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n orderId String?\n goldGrams Decimal @db.Decimal(14, 4)\n grantedAt DateTime @default(now())\n expiresAt DateTime\n consumedAt DateTime?\n isActive Boolean @default(true)\n\n @@index([customerId, isActive])\n}\n", - "inlineSchemaHash": "3d2a553a520468aa24bdcb865818fc11df79984db4b59e1e7ab13994e43c8a31", + "inlineSchema": "// Gold Banking System — core schema\n// Canonical accounting model: legacy \"System A\" gram ledger (see migration notes).\n// The external \"Tahesab\" service is retired — LedgerEntry is the sole source of truth.\n// All money/weight fields use Decimal, never Float, to avoid rounding drift in debt math.\n\ngenerator client {\n provider = \"prisma-client-js\"\n // native = local Windows/macOS; debian-openssl-3.0.x = node:20-bookworm-slim (Arvan)\n binaryTargets = [\"native\", \"debian-openssl-3.0.x\"]\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"DATABASE_URL\")\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Enums\n// ─────────────────────────────────────────────────────────────────────────\n\nenum AdminRole {\n SUPER_ADMIN\n SYSTEM_APPROVER // legacy: تایید مدیر سیستم\n SALES_EXPERT // legacy Customer.type=1, also acts as a referral \"reagent\"\n WORKSHOP_SUPERVISOR\n ACCOUNTANT\n}\n\nenum VerificationStep {\n UNVERIFIED\n STEP_ONE_SUBMITTED\n STEP_TWO_SUBMITTED\n VERIFIED\n}\n\nenum OrderStatus {\n SUSPENDED // credit limit (goldCreditLimitGrams) exceeded at placement\n PLACED\n ADMIN_APPROVED\n SOURCING\n PREPARING\n DELIVERED_TO_AGENT\n DELIVERED_TO_CUSTOMER // debt recognition point — mirrors legacy sts>4\n CANCELLED\n}\n\nenum PreOrderStatus {\n PLACED\n ADMIN_APPROVED\n WORKSHOP_APPROVED\n READY_TO_SHIP\n DELIVERED\n CANCELLED\n}\n\nenum LedgerCurrency {\n GOLD_GRAM\n RIAL\n}\n\nenum LedgerDirection {\n DEBIT // increases what the customer owes\n CREDIT // decreases what the customer owes\n}\n\nenum LedgerSourceType {\n ORDER_STOCK\n ORDER_PRE_ORDER\n REPAYMENT_MELTED_GOLD // karat-normalized: amount * karatPerMille / 750\n REPAYMENT_TRANSFER\n REPAYMENT_CHEQUE\n REPAYMENT_CASH // price-per-gram-normalized via pricePerGramRialAtEntry\n WALLET_BONUS\n ADJUSTMENT\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Identity\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel City {\n id String @id @default(cuid())\n name String\n province String?\n customers Customer[]\n addresses CustomerAddress[]\n branches Branch[]\n preOrders PreOrder[]\n}\n\nmodel AdminUser {\n id String @id @default(cuid())\n fullName String\n mobile String @unique\n passwordHash String\n role AdminRole\n branchId String?\n branch Branch? @relation(fields: [branchId], references: [id])\n wagePercent Decimal? @db.Decimal(6, 3) // legacy Customer.percent, commission on referred orders\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n referredCustomers Customer[] @relation(\"ReferralAgent\")\n postedLedgerEntries LedgerEntry[]\n orderStatusEvents OrderStatusEvent[]\n preOrderStatusEvents PreOrderStatusEvent[]\n\n @@index([role])\n}\n\nmodel Customer {\n id String @id @default(cuid())\n fullName String\n mobile String @unique\n nationalCode String @unique\n passwordHash String\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n\n referredByAgentId String?\n referredByAgent AdminUser? @relation(\"ReferralAgent\", fields: [referredByAgentId], references: [id])\n\n // Credit limit + cached ledger balances (source of truth is LedgerEntry; these\n // are denormalized for fast reads and reconciled by a scheduled job).\n goldCreditLimitGrams Decimal @default(0) @db.Decimal(14, 4) // legacy gram_level\n goldDebtGrams Decimal @default(0) @db.Decimal(14, 4) // legacy remain\n fiatDebtRial Decimal @default(0) @db.Decimal(20, 2) // legacy rial_remain\n\n verificationStep VerificationStep @default(UNVERIFIED)\n nationalIdImageUrl String?\n businessLicenseUrl String?\n shopImageUrl String?\n\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n addresses CustomerAddress[]\n orders Order[]\n preOrders PreOrder[]\n ledgerEntries LedgerEntry[]\n walletBonuses WalletBonus[]\n\n @@index([referredByAgentId])\n}\n\nmodel CustomerAddress {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n addressLine String\n isDefault Boolean @default(false)\n}\n\nmodel Branch {\n id String @id @default(cuid())\n name String\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n adminUsers AdminUser[]\n orders Order[]\n}\n\nmodel Workshop {\n id String @id @default(cuid())\n name String\n isActive Boolean @default(true)\n preOrders PreOrder[]\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Catalog — item reference catalog\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel Category {\n id String @id @default(cuid())\n title String\n parentId String?\n parent Category? @relation(\"CategoryTree\", fields: [parentId], references: [id])\n children Category[] @relation(\"CategoryTree\")\n products Product[]\n}\n\nmodel Brand {\n id String @id @default(cuid())\n title String\n products Product[]\n}\n\n/// Curated product groupings — replaces legacy \"Ciny/CinyProduct\" (a tagging\n/// feature, NOT a gold-price snapshot mechanism, despite the misleading legacy name).\nmodel Collection {\n id String @id @default(cuid())\n title String\n description String?\n isActive Boolean @default(true)\n products CollectionProduct[]\n}\n\nmodel CollectionProduct {\n collectionId String\n collection Collection @relation(fields: [collectionId], references: [id])\n productId String\n product Product @relation(fields: [productId], references: [id])\n\n @@id([collectionId, productId])\n}\n\nmodel Product {\n id String @id @default(cuid())\n title String\n description String?\n categoryId String?\n category Category? @relation(fields: [categoryId], references: [id])\n brandId String?\n brand Brand? @relation(fields: [brandId], references: [id])\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n variants ProductVariant[]\n images ProductImage[]\n collections CollectionProduct[]\n}\n\nmodel ProductImage {\n id String @id @default(cuid())\n productId String\n product Product @relation(fields: [productId], references: [id])\n url String\n sortOrder Int @default(0)\n}\n\nmodel ProductVariant {\n id String @id @default(cuid())\n productId String\n product Product @relation(fields: [productId], references: [id])\n title String\n sku String? @unique\n /// Base/raw weight before making charge (\"rash weight\").\n rashWeightGrams Decimal @db.Decimal(14, 4)\n /// Default making-charge percentage applied on top of rash weight.\n defaultWagePercent Decimal @db.Decimal(6, 3)\n isActive Boolean @default(true)\n\n orderItems OrderItem[]\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Orders — stock orders (debt-bearing)\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel Order {\n id String @id @default(cuid())\n orderNumber String @unique\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n branchId String?\n branch Branch? @relation(fields: [branchId], references: [id])\n status OrderStatus @default(PLACED)\n\n /// Total gold weight incl. wage across all items (\"allGram\" / order.w).\n totalGoldWeightGrams Decimal @db.Decimal(14, 4)\n /// Pure making-charge weight component (totalGoldWeightGrams minus raw weight sum).\n wageWeightGrams Decimal @db.Decimal(14, 4)\n /// Flat Rial charge independent of gold price (e.g. stones) — legacy rial_input.\n flatRialCharge Decimal @default(0) @db.Decimal(20, 2)\n\n isSuspendedOverCreditLimit Boolean @default(false)\n deliveredAt DateTime? // setting this is what triggers debt recognition\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n items OrderItem[]\n statusEvents OrderStatusEvent[]\n ledgerEntries LedgerEntry[]\n convertedFromPreOrder PreOrder?\n\n @@index([customerId])\n @@index([status])\n}\n\nmodel OrderItem {\n id String @id @default(cuid())\n orderId String\n order Order @relation(fields: [orderId], references: [id])\n productVariantId String\n productVariant ProductVariant @relation(fields: [productVariantId], references: [id])\n quantity Int\n\n // Snapshots at order time — never re-read from ProductVariant after placement.\n rashWeightGramsSnapshot Decimal @db.Decimal(14, 4)\n wagePercentSnapshot Decimal @db.Decimal(6, 3)\n discountPercentSnapshot Decimal @default(0) @db.Decimal(6, 3)\n /// computed: rash*(1 + (wagePercent-discount)/100)\n computedWeightGrams Decimal @db.Decimal(14, 4)\n flatRialCharge Decimal @default(0) @db.Decimal(20, 2)\n}\n\nmodel OrderStatusEvent {\n id String @id @default(cuid())\n orderId String\n order Order @relation(fields: [orderId], references: [id])\n fromStatus OrderStatus?\n toStatus OrderStatus\n note String?\n actorAdminUserId String?\n actorAdminUser AdminUser? @relation(fields: [actorAdminUserId], references: [id])\n createdAt DateTime @default(now())\n\n @@index([orderId])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Pre-orders — custom manufacturing requests (not debt-bearing until converted)\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel PreOrder {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n workshopId String?\n workshop Workshop? @relation(fields: [workshopId], references: [id])\n status PreOrderStatus @default(PLACED)\n\n designImageUrl String?\n colorName String?\n sizeSpec String?\n stoneColor String?\n minaColor String?\n estimatedWeightGrams Decimal? @db.Decimal(14, 4)\n estimatedPriceRial Decimal? @db.Decimal(20, 2)\n description String?\n\n /// Once priced, an admin converts this into a real (debt-bearing) Order.\n convertedOrderId String? @unique\n convertedOrder Order? @relation(fields: [convertedOrderId], references: [id])\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n statusEvents PreOrderStatusEvent[]\n\n @@index([customerId])\n}\n\nmodel PreOrderStatusEvent {\n id String @id @default(cuid())\n preOrderId String\n preOrder PreOrder @relation(fields: [preOrderId], references: [id])\n /// Field-level diff, e.g. {\"status\": {\"old\": \"PLACED\", \"new\": \"ADMIN_APPROVED\"}}\n changes Json\n actorAdminUserId String?\n actorAdminUser AdminUser? @relation(fields: [actorAdminUserId], references: [id])\n ipAddress String?\n createdAt DateTime @default(now())\n\n @@index([preOrderId])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Accounting — the dual-currency ledger (core of the system)\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel LedgerEntry {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n\n currency LedgerCurrency\n direction LedgerDirection\n sourceType LedgerSourceType\n /// Always positive; direction determines the sign applied to the customer balance.\n amount Decimal @db.Decimal(20, 4)\n\n /// Purity of the physical instrument, for REPAYMENT_MELTED_GOLD (e.g. 750, 916).\n karatPerMille Int?\n /// Gold price/gram at conversion time, for REPAYMENT_CASH.\n pricePerGramRialAtEntry Decimal? @db.Decimal(20, 2)\n\n orderId String?\n order Order? @relation(fields: [orderId], references: [id])\n\n /// Pending entries don't count toward the cached customer balance until confirmed.\n confirmed Boolean @default(false)\n confirmedAt DateTime?\n postedByAdminUserId String?\n postedByAdminUser AdminUser? @relation(fields: [postedByAdminUserId], references: [id])\n\n note String?\n createdAt DateTime @default(now())\n\n @@index([customerId, currency])\n @@index([orderId])\n}\n\n/// Replaces legacy WebServiceGold / ConfigStatic.gold_price — timestamped price feed.\nmodel GoldPriceQuote {\n id String @id @default(cuid())\n karatPerMille Int\n pricePerGramRial Decimal @db.Decimal(20, 2)\n source String\n quotedAt DateTime @default(now())\n\n @@index([karatPerMille, quotedAt])\n}\n\n/// Time-limited gold cashback bonus — replaces the legacy Rooyesh\\Wallet gold credit.\nmodel WalletBonus {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n orderId String?\n goldGrams Decimal @db.Decimal(14, 4)\n grantedAt DateTime @default(now())\n expiresAt DateTime\n consumedAt DateTime?\n isActive Boolean @default(true)\n\n @@index([customerId, isActive])\n}\n", + "inlineSchemaHash": "c0944da476bdb47884e1c9d2aeeb374e6077907fd89970148d89eff210dfe8ab", "copyEngine": true } @@ -500,12 +496,8 @@ path.join(__dirname, "query_engine-windows.dll.node"); path.join(process.cwd(), "../../node_modules/.pnpm/@prisma+client@5.22.0_prisma@5.22.0/node_modules/.prisma/client/query_engine-windows.dll.node") // file annotations for bundling tools to include these files -path.join(__dirname, "libquery_engine-linux-musl.so.node"); -path.join(process.cwd(), "../../node_modules/.pnpm/@prisma+client@5.22.0_prisma@5.22.0/node_modules/.prisma/client/libquery_engine-linux-musl.so.node") - -// file annotations for bundling tools to include these files -path.join(__dirname, "libquery_engine-linux-musl-openssl-3.0.x.so.node"); -path.join(process.cwd(), "../../node_modules/.pnpm/@prisma+client@5.22.0_prisma@5.22.0/node_modules/.prisma/client/libquery_engine-linux-musl-openssl-3.0.x.so.node") +path.join(__dirname, "libquery_engine-debian-openssl-3.0.x.so.node"); +path.join(process.cwd(), "../../node_modules/.pnpm/@prisma+client@5.22.0_prisma@5.22.0/node_modules/.prisma/client/libquery_engine-debian-openssl-3.0.x.so.node") // file annotations for bundling tools to include these files path.join(__dirname, "schema.prisma"); path.join(process.cwd(), "../../node_modules/.pnpm/@prisma+client@5.22.0_prisma@5.22.0/node_modules/.prisma/client/schema.prisma") diff --git a/apps/api/release/vendor/.prisma/client/libquery_engine-debian-openssl-3.0.x.so.node b/apps/api/release/vendor/.prisma/client/libquery_engine-debian-openssl-3.0.x.so.node new file mode 100644 index 0000000..827ccc8 Binary files /dev/null and b/apps/api/release/vendor/.prisma/client/libquery_engine-debian-openssl-3.0.x.so.node differ diff --git a/apps/api/release/vendor/.prisma/client/package.json b/apps/api/release/vendor/.prisma/client/package.json index e493dd0..df326de 100644 --- a/apps/api/release/vendor/.prisma/client/package.json +++ b/apps/api/release/vendor/.prisma/client/package.json @@ -1,5 +1,5 @@ { - "name": "prisma-client-7456678a68d33d1115534aa06ea14b6a72845b59f9c4ccefaa27e3167d919ad8", + "name": "prisma-client-d0d5db76aa59566edce744ecac2768fcb398c1a9f370089a45b9416272282fab", "main": "index.js", "types": "index.d.ts", "browser": "index-browser.js", diff --git a/apps/api/release/vendor/.prisma/client/schema.prisma b/apps/api/release/vendor/.prisma/client/schema.prisma index f9c5e1b..03ab62d 100644 --- a/apps/api/release/vendor/.prisma/client/schema.prisma +++ b/apps/api/release/vendor/.prisma/client/schema.prisma @@ -5,8 +5,8 @@ generator client { provider = "prisma-client-js" - // native = local Windows/macOS; linux-musl* = Arvan/Alpine Docker (OpenSSL detect varies) - binaryTargets = ["native", "linux-musl", "linux-musl-openssl-3.0.x"] + // native = local Windows/macOS; debian-openssl-3.0.x = node:20-bookworm-slim (Arvan) + binaryTargets = ["native", "debian-openssl-3.0.x"] } datasource db {