diff --git a/DELIVERY.md b/DELIVERY.md index a55846f..aafef94 100644 --- a/DELIVERY.md +++ b/DELIVERY.md @@ -102,9 +102,17 @@ pnpm build:release # the production/Docker build ### Deploy (Docker) ```bash +pnpm build:release # ⚠ REQUIRED FIRST — see below docker compose up -d --build ``` +> **⚠ `pnpm build:release` must be run before `docker compose build`.** +> The API image is `COPY release/ ./` — it ships a bundle built on the +> developer machine, not one built inside the image. So `docker compose build` +> will happily produce an image from a **stale** `release/app.js` and report +> success. During this delivery's compose run that bundle was four days old and +> the container was missing every route added since. Tracked as **DEPLOY-03**. + Services: `db` (Postgres 15), `api` (port 3000), `admin-panel`, `user-app`, `user-app-v2` (each nginx on port 80). The apps expose no host ports — Dokploy/Traefik routes to them by domain. Uploads persist in the `api_uploads` @@ -144,7 +152,7 @@ All green on `release/delivery`: | Suite | Result | |---|---| | `pnpm build` (5 workspaces) | ✅ all pass | -| jest (api unit) | **45/45** | +| jest (api unit) | **54/54** | | `smoke-admin.cjs` — admin flows | **43/43** | | `smoke-apps.cjs` — customer flows, both apps | **43/43** | | `scan-admin-b2b.cjs` — every B2B read endpoint | **34 ok, 0 broken** | @@ -154,9 +162,32 @@ All green on `release/delivery`: | `test-sales-expert-app-login.cjs` — expert app login (D21) | **36/36** | | `test-complete-profile.cjs` — «تکمیل پروفایل» | **38/38** | | `test-report.cjs` — «گزارش» | **40/40** | +| `test-compose-e2e.cjs` — **the whole flow inside Docker Compose** | **32/32** | | `test-name-trigger.cjs` | **9/9** | | typecheck (api, admin-panel, user-app, user-app-v2) | ✅ clean | +### Verified inside Docker Compose, not just locally + +The whole delivery flow was run against the **built container**, which is what +actually ships: `test-compose-e2e.cjs` signs a **sales expert** and an +**ordinary customer** into the app with the master code, has **both place an +order**, and confirms **both orders appear in the admin panel** and open there. +It also re-checks the three token boundaries in the deployed image. **32/32.** +The three page suites were re-run against the container too — complete-profile +38/38, report 40/40, expert login 36/36. + +That run found two deployment problems that no local test could have caught: + +- **DEPLOY-02 (fixed):** admin login returned **500 for every user in Docker**. + Compose passes optional variables as `${X:-}`, so an unconfigured one arrives + as `""`, not `undefined`; the code used `?? 'default'`, which only falls back + on `undefined`, so the empty string reached `jsonwebtoken` and it threw on + every sign. Fixed with `src/common/env.ts`, applied to every auth, port, + upload and SMS read, and pinned by a test that reproduces the original throw. +- **DEPLOY-03 (process):** the API image ships a **pre-built** bundle, so + `docker compose build` can silently produce an image from stale code. See the + warning in the deploy section above. + Flows walked end to end: admin login, customer creation, sales-expert create/edit, product and variant create/edit, order list/detail/status change, wholesale order placement from the admin panel, cash document creation + diff --git a/apps/api/release/app.js b/apps/api/release/app.js index 8ddf06e..3f24709 100644 --- a/apps/api/release/app.js +++ b/apps/api/release/app.js @@ -76277,8 +76277,18 @@ var require_catalog_service = __commonJS({ return this.prisma.product.findMany({ where: { isActive: true }, include: { - variants: true, - images: true, + // Only orderable variants reach the storefront. An inactive one used to + // be listed and then rejected by getVariantOrThrow at checkout, so the + // customer could pick a chip and get a 404 on the order. + variants: { + where: { isActive: true }, + // Each variant carries its own gallery picture, so the app can render + // the chip without a second request per variant. + include: { image: true } + }, + // Explicit order: the UI treats images[0] as the product's main photo, + // which is only meaningful if the rows come back sorted. + images: { orderBy: { sortOrder: "asc" } }, category: true, brand: true, collections: { include: { collection: true } } @@ -77528,6 +77538,7 @@ var require_customers_service = __commonJS({ businessLicenseUrl: true, shopImageUrl: true, isActive: true, + isArchived: true, createdAt: true, updatedAt: true }; @@ -77545,12 +77556,6 @@ var require_customers_service = __commonJS({ throw new common_12.NotFoundException(`Customer ${customerId} not found`); return customer; } - list() { - return this.prisma.customer.findMany({ - select: PUBLIC_CUSTOMER_SELECT, - orderBy: { createdAt: "desc" } - }); - } getOrders(customerId) { return this.prisma.order.findMany({ where: { customerId }, @@ -77573,83 +77578,6 @@ var require_customers_service = __commonJS({ } }); -// dist/modules/customers/customers.controller.js -var require_customers_controller = __commonJS({ - "dist/modules/customers/customers.controller.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - var __metadata3 = exports2 && exports2.__metadata || function(k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); - }; - var __param3 = exports2 && exports2.__param || function(paramIndex, decorator) { - return function(target, key) { - decorator(target, key, paramIndex); - }; - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.CustomersController = void 0; - var common_12 = require_common(); - var customers_service_1 = require_customers_service(); - var guards_1 = require_guards2(); - var CustomersController = class CustomersController { - customers; - constructor(customers) { - this.customers = customers; - } - list() { - return this.customers.list(); - } - get(id) { - return this.customers.getOrThrow(id); - } - getOrders(id) { - return this.customers.getOrders(id); - } - getLedgerEntries(id) { - return this.customers.getLedgerEntries(id); - } - }; - exports2.CustomersController = CustomersController; - __decorate4([ - (0, common_12.Get)(), - __metadata3("design:type", Function), - __metadata3("design:paramtypes", []), - __metadata3("design:returntype", void 0) - ], CustomersController.prototype, "list", null); - __decorate4([ - (0, common_12.Get)(":id"), - __param3(0, (0, common_12.Param)("id")), - __metadata3("design:type", Function), - __metadata3("design:paramtypes", [String]), - __metadata3("design:returntype", void 0) - ], CustomersController.prototype, "get", null); - __decorate4([ - (0, common_12.Get)(":id/orders"), - __param3(0, (0, common_12.Param)("id")), - __metadata3("design:type", Function), - __metadata3("design:paramtypes", [String]), - __metadata3("design:returntype", void 0) - ], CustomersController.prototype, "getOrders", null); - __decorate4([ - (0, common_12.Get)(":id/ledger-entries"), - __param3(0, (0, common_12.Param)("id")), - __metadata3("design:type", Function), - __metadata3("design:paramtypes", [String]), - __metadata3("design:returntype", void 0) - ], CustomersController.prototype, "getLedgerEntries", null); - exports2.CustomersController = CustomersController = __decorate4([ - (0, guards_1.Public)(), - (0, common_12.Controller)("customers"), - __metadata3("design:paramtypes", [customers_service_1.CustomersService]) - ], CustomersController); - } -}); - // dist/modules/customers/customers.module.js var require_customers_module = __commonJS({ "dist/modules/customers/customers.module.js"(exports2) { @@ -77664,13 +77592,11 @@ var require_customers_module = __commonJS({ exports2.CustomersModule = void 0; var common_12 = require_common(); var customers_service_1 = require_customers_service(); - var customers_controller_1 = require_customers_controller(); var CustomersModule = class CustomersModule { }; exports2.CustomersModule = CustomersModule; exports2.CustomersModule = CustomersModule = __decorate4([ (0, common_12.Module)({ - controllers: [customers_controller_1.CustomersController], providers: [customers_service_1.CustomersService], exports: [customers_service_1.CustomersService] }) @@ -77678,1008 +77604,6 @@ var require_customers_module = __commonJS({ } }); -// ../../packages/shared-types/dist/index.js -var require_dist3 = __commonJS({ - "../../packages/shared-types/dist/index.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.PreOrderStatus = exports2.OrderStatus = exports2.LedgerSourceType = exports2.LedgerDirection = exports2.LedgerCurrency = exports2.GRAMS_PER_MESGHAL = exports2.STANDARD_KARAT_PER_MILLE = void 0; - exports2.STANDARD_KARAT_PER_MILLE = 750; - exports2.GRAMS_PER_MESGHAL = 4.331802; - var LedgerCurrency; - (function(LedgerCurrency2) { - LedgerCurrency2["GOLD_GRAM"] = "GOLD_GRAM"; - LedgerCurrency2["RIAL"] = "RIAL"; - })(LedgerCurrency || (exports2.LedgerCurrency = LedgerCurrency = {})); - var LedgerDirection; - (function(LedgerDirection2) { - LedgerDirection2["DEBIT"] = "DEBIT"; - LedgerDirection2["CREDIT"] = "CREDIT"; - })(LedgerDirection || (exports2.LedgerDirection = LedgerDirection = {})); - var LedgerSourceType; - (function(LedgerSourceType2) { - LedgerSourceType2["ORDER_STOCK"] = "ORDER_STOCK"; - LedgerSourceType2["ORDER_PRE_ORDER"] = "ORDER_PRE_ORDER"; - LedgerSourceType2["REPAYMENT_MELTED_GOLD"] = "REPAYMENT_MELTED_GOLD"; - LedgerSourceType2["REPAYMENT_TRANSFER"] = "REPAYMENT_TRANSFER"; - LedgerSourceType2["REPAYMENT_CHEQUE"] = "REPAYMENT_CHEQUE"; - LedgerSourceType2["REPAYMENT_CASH"] = "REPAYMENT_CASH"; - LedgerSourceType2["WALLET_BONUS"] = "WALLET_BONUS"; - LedgerSourceType2["ADJUSTMENT"] = "ADJUSTMENT"; - })(LedgerSourceType || (exports2.LedgerSourceType = LedgerSourceType = {})); - var OrderStatus; - (function(OrderStatus2) { - OrderStatus2["SUSPENDED"] = "SUSPENDED"; - OrderStatus2["PLACED"] = "PLACED"; - OrderStatus2["ADMIN_APPROVED"] = "ADMIN_APPROVED"; - OrderStatus2["SOURCING"] = "SOURCING"; - OrderStatus2["PREPARING"] = "PREPARING"; - OrderStatus2["DELIVERED_TO_AGENT"] = "DELIVERED_TO_AGENT"; - OrderStatus2["DELIVERED_TO_CUSTOMER"] = "DELIVERED_TO_CUSTOMER"; - OrderStatus2["CANCELLED"] = "CANCELLED"; - })(OrderStatus || (exports2.OrderStatus = OrderStatus = {})); - var PreOrderStatus; - (function(PreOrderStatus2) { - PreOrderStatus2["PLACED"] = "PLACED"; - PreOrderStatus2["ADMIN_APPROVED"] = "ADMIN_APPROVED"; - PreOrderStatus2["WORKSHOP_APPROVED"] = "WORKSHOP_APPROVED"; - PreOrderStatus2["READY_TO_SHIP"] = "READY_TO_SHIP"; - PreOrderStatus2["DELIVERED"] = "DELIVERED"; - PreOrderStatus2["CANCELLED"] = "CANCELLED"; - })(PreOrderStatus || (exports2.PreOrderStatus = PreOrderStatus = {})); - } -}); - -// dist/modules/accounting/ledger.service.js -var require_ledger_service = __commonJS({ - "dist/modules/accounting/ledger.service.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - var __metadata3 = exports2 && exports2.__metadata || function(k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.LedgerService = void 0; - var common_12 = require_common(); - var client_1 = require("@prisma/client"); - var shared_types_1 = require_dist3(); - var prisma_service_1 = require_prisma_service(); - var LedgerService = class LedgerService { - prisma; - constructor(prisma) { - this.prisma = prisma; - } - /** Posts a ledger entry and, if confirmed, applies it to the cached customer balance. */ - async postEntry(input, tx = this.prisma) { - const amount = new client_1.Prisma.Decimal(input.amount); - if (amount.lte(0)) { - throw new common_12.BadRequestException("Ledger entry amount must be positive"); - } - const confirmed = input.confirmed ?? false; - const entry = await tx.ledgerEntry.create({ - data: { - customerId: input.customerId, - currency: input.currency, - direction: input.direction, - sourceType: input.sourceType, - amount, - orderId: input.orderId, - karatPerMille: input.karatPerMille, - pricePerGramRialAtEntry: input.pricePerGramRialAtEntry !== void 0 ? new client_1.Prisma.Decimal(input.pricePerGramRialAtEntry) : void 0, - note: input.note, - confirmed, - confirmedAt: confirmed ? /* @__PURE__ */ new Date() : null, - postedByAdminUserId: input.postedByAdminUserId - } - }); - if (confirmed) { - await this.applyToBalance(input.customerId, input.currency, input.direction, amount, tx); - } - return entry; - } - /** - * Marks a pending repayment entry confirmed. Legacy's confirmed toggle - * (Adm\CashController::toggleConfiremCash) is bidirectional and used for - * post-hoc correction/rejection — legacy Cash rows are auto-confirmed - * (confirmed=1) at creation, not created pending. This system intentionally - * requires an explicit confirmation step before a repayment counts (see - * postGoldRepayment for the full rationale) — that is a deliberate design - * change from legacy, not a faithful mirror of it. - */ - async confirmEntry(entryId, adminUserId, tx = this.prisma) { - const entry = await tx.ledgerEntry.findUnique({ where: { id: entryId } }); - if (!entry) - throw new common_12.NotFoundException("Ledger entry not found"); - if (entry.confirmed) - return entry; - const updated = await tx.ledgerEntry.update({ - where: { id: entryId }, - data: { confirmed: true, confirmedAt: /* @__PURE__ */ new Date(), postedByAdminUserId: adminUserId ?? entry.postedByAdminUserId } - }); - await this.applyToBalance(entry.customerId, entry.currency, entry.direction, entry.amount, tx); - return updated; - } - async applyToBalance(customerId, currency, direction, amount, tx) { - const signedDelta = direction === client_1.LedgerDirection.DEBIT ? amount : amount.neg(); - await tx.customer.update({ - where: { id: customerId }, - data: currency === client_1.LedgerCurrency.GOLD_GRAM ? { goldDebtGrams: { increment: signedDelta } } : { fiatDebtRial: { increment: signedDelta } } - }); - } - /** - * Posts the dual-currency debt for a stock order once it reaches - * DELIVERED_TO_AGENT: GOLD_GRAM debt = order.totalGoldWeightGrams, RIAL debt - * = order.flatRialCharge. Both entries are self-confirming — the status - * transition itself is the confirming event (mirrors legacy sts>4, which - * first becomes true at sts=5 "تحویل مسئول فروش" / delivered to agent, not - * sts=6 delivered to customer). - */ - async postOrderDebt(orderId, tx = this.prisma) { - const order = await tx.order.findUnique({ where: { id: orderId } }); - if (!order) - throw new common_12.NotFoundException("Order not found"); - await this.postEntry({ - customerId: order.customerId, - currency: client_1.LedgerCurrency.GOLD_GRAM, - direction: client_1.LedgerDirection.DEBIT, - sourceType: client_1.LedgerSourceType.ORDER_STOCK, - amount: order.totalGoldWeightGrams, - orderId: order.id, - confirmed: true, - note: "Gold-weight debt on delivery" - }, tx); - if (new client_1.Prisma.Decimal(order.flatRialCharge).gt(0)) { - await this.postEntry({ - customerId: order.customerId, - currency: client_1.LedgerCurrency.RIAL, - direction: client_1.LedgerDirection.DEBIT, - sourceType: client_1.LedgerSourceType.ORDER_STOCK, - amount: order.flatRialCharge, - orderId: order.id, - confirmed: true, - note: "Flat Rial charge (e.g. stones) on delivery" - }, tx); - } - } - /** - * Normalizes a physical repayment instrument into gold grams and posts a - * pending (unconfirmed) CREDIT entry against the customer's gold debt. - * Formulas preserved verbatim from legacy CashController::store. Note: - * legacy auto-confirms (confirmed=1) every Cash row at creation — posting - * this as pending instead is an intentional design change, not a mirror of - * legacy's default. - */ - async postGoldRepayment(params, tx = this.prisma) { - const raw = new client_1.Prisma.Decimal(params.rawValue); - let goldGramsValue; - let sourceType; - switch (params.instrument) { - case "MELTED_GOLD": { - if (!params.karatPerMille) { - throw new common_12.BadRequestException("karatPerMille is required for a melted-gold repayment"); - } - goldGramsValue = raw.mul(params.karatPerMille).div(shared_types_1.STANDARD_KARAT_PER_MILLE); - sourceType = client_1.LedgerSourceType.REPAYMENT_MELTED_GOLD; - break; - } - case "CASH": { - if (!params.pricePerGramRial) { - throw new common_12.BadRequestException("pricePerGramRial is required for a cash repayment"); - } - const pricePerGram = new client_1.Prisma.Decimal(params.pricePerGramRial); - goldGramsValue = raw.div(pricePerGram.div(shared_types_1.GRAMS_PER_MESGHAL)); - sourceType = client_1.LedgerSourceType.REPAYMENT_CASH; - break; - } - case "TRANSFER": - goldGramsValue = raw; - sourceType = client_1.LedgerSourceType.REPAYMENT_TRANSFER; - break; - case "CHEQUE": - goldGramsValue = raw; - sourceType = client_1.LedgerSourceType.REPAYMENT_CHEQUE; - break; - } - return this.postEntry({ - customerId: params.customerId, - currency: client_1.LedgerCurrency.GOLD_GRAM, - direction: client_1.LedgerDirection.CREDIT, - sourceType, - amount: goldGramsValue, - karatPerMille: params.karatPerMille, - pricePerGramRialAtEntry: params.pricePerGramRial, - note: params.note, - confirmed: false, - // pending until an admin confirms — intentional deviation from legacy, which auto-confirms on creation - postedByAdminUserId: params.postedByAdminUserId - }, tx); - } - /** - * Direct Rial-debt repayment. Not present in the legacy system (its - * rial_remain had no repayment offset at all — a gap flagged during - * migration analysis) but supported here since the new ledger is symmetric. - */ - async postRialRepayment(params, tx = this.prisma) { - return this.postEntry({ - customerId: params.customerId, - currency: client_1.LedgerCurrency.RIAL, - direction: client_1.LedgerDirection.CREDIT, - sourceType: client_1.LedgerSourceType.REPAYMENT_CASH, - amount: params.amount, - note: params.note, - confirmed: false, - postedByAdminUserId: params.postedByAdminUserId - }, tx); - } - async postAdjustment(params, tx = this.prisma) { - return this.postEntry({ - customerId: params.customerId, - currency: params.currency, - direction: params.direction, - sourceType: client_1.LedgerSourceType.ADJUSTMENT, - amount: params.amount, - note: params.note, - confirmed: true, - postedByAdminUserId: params.postedByAdminUserId - }, tx); - } - async getBalance(customerId) { - const customer = await this.prisma.customer.findUnique({ - where: { id: customerId }, - select: { id: true, goldDebtGrams: true, fiatDebtRial: true, goldCreditLimitGrams: true } - }); - if (!customer) - throw new common_12.NotFoundException("Customer not found"); - return { - customerId: customer.id, - goldDebtGrams: customer.goldDebtGrams, - fiatDebtRial: customer.fiatDebtRial, - goldCreditLimitGrams: customer.goldCreditLimitGrams - }; - } - /** - * Recomputes cached balances from confirmed LedgerEntry rows and overwrites - * the cache — a self-healing reconciliation, run periodically or on demand, - * unlike legacy's recompute-on-every-write (Customer::updateRemain). - */ - async reconcileBalance(customerId) { - const [goldDebits, goldCredits, rialDebits, rialCredits] = await Promise.all([ - this.sumConfirmed(customerId, client_1.LedgerCurrency.GOLD_GRAM, client_1.LedgerDirection.DEBIT), - this.sumConfirmed(customerId, client_1.LedgerCurrency.GOLD_GRAM, client_1.LedgerDirection.CREDIT), - this.sumConfirmed(customerId, client_1.LedgerCurrency.RIAL, client_1.LedgerDirection.DEBIT), - this.sumConfirmed(customerId, client_1.LedgerCurrency.RIAL, client_1.LedgerDirection.CREDIT) - ]); - return this.prisma.customer.update({ - where: { id: customerId }, - data: { - goldDebtGrams: goldDebits.sub(goldCredits), - fiatDebtRial: rialDebits.sub(rialCredits) - } - }); - } - async sumConfirmed(customerId, currency, direction) { - const result = await this.prisma.ledgerEntry.aggregate({ - where: { customerId, currency, direction, confirmed: true }, - _sum: { amount: true } - }); - return result._sum.amount ?? new client_1.Prisma.Decimal(0); - } - }; - exports2.LedgerService = LedgerService; - exports2.LedgerService = LedgerService = __decorate4([ - (0, common_12.Injectable)(), - __metadata3("design:paramtypes", [prisma_service_1.PrismaService]) - ], LedgerService); - } -}); - -// dist/modules/orders/orders.service.js -var require_orders_service = __commonJS({ - "dist/modules/orders/orders.service.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - var __metadata3 = exports2 && exports2.__metadata || function(k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.OrdersService = void 0; - var common_12 = require_common(); - var client_1 = require("@prisma/client"); - var prisma_service_1 = require_prisma_service(); - var catalog_service_1 = require_catalog_service(); - var ledger_service_1 = require_ledger_service(); - var OrdersService = class OrdersService { - prisma; - catalog; - ledger; - constructor(prisma, catalog, ledger) { - this.prisma = prisma; - this.catalog = catalog; - this.ledger = ledger; - } - /** - * Places a stock order. Per line item: - * w = rashWeightGrams * (1 + (wagePercent - discountPercent) / 100) - * mirrors legacy ApiMobileController::setOrder. Debt is NOT posted here — - * only once the order reaches DELIVERED_TO_AGENT (see updateStatus). - */ - async createOrder(dto) { - const customer = await this.prisma.customer.findUnique({ where: { id: dto.customerId } }); - if (!customer) - throw new common_12.NotFoundException(`Customer ${dto.customerId} not found`); - const computedItems = await Promise.all(dto.items.map(async (item) => { - const variant = await this.catalog.getVariantOrThrow(item.productVariantId); - const rashWeight = new client_1.Prisma.Decimal(variant.rashWeightGrams); - const wagePercent = new client_1.Prisma.Decimal(item.wagePercent); - const discountPercent = new client_1.Prisma.Decimal(item.discountPercent ?? 0); - const netPercent = wagePercent.sub(discountPercent); - const computedWeightGrams = rashWeight.mul(netPercent.div(100).add(1)); - return { - productVariantId: variant.id, - quantity: item.quantity, - rashWeightGramsSnapshot: rashWeight, - wagePercentSnapshot: wagePercent, - discountPercentSnapshot: discountPercent, - computedWeightGrams, - flatRialCharge: new client_1.Prisma.Decimal(item.flatRialCharge ?? 0) - }; - })); - const totalGoldWeightGrams = computedItems.reduce((sum, item) => sum.add(item.computedWeightGrams.mul(item.quantity)), new client_1.Prisma.Decimal(0)); - const totalRashWeightGrams = computedItems.reduce((sum, item) => sum.add(item.rashWeightGramsSnapshot.mul(item.quantity)), new client_1.Prisma.Decimal(0)); - const wageWeightGrams = totalGoldWeightGrams.sub(totalRashWeightGrams); - const flatRialCharge = computedItems.reduce((sum, item) => sum.add(item.flatRialCharge.mul(item.quantity)), new client_1.Prisma.Decimal(0)); - const isSuspendedOverCreditLimit = totalGoldWeightGrams.add(customer.goldDebtGrams).gt(customer.goldCreditLimitGrams); - const initialStatus = isSuspendedOverCreditLimit ? client_1.OrderStatus.SUSPENDED : client_1.OrderStatus.PLACED; - return this.prisma.$transaction(async (tx) => { - const order = await tx.order.create({ - data: { - orderNumber: this.generateOrderNumber(), - customerId: dto.customerId, - branchId: dto.branchId, - channel: dto.channel ?? client_1.OrderChannel.RETAIL, - status: initialStatus, - totalGoldWeightGrams, - wageWeightGrams, - flatRialCharge, - isSuspendedOverCreditLimit, - items: { - create: computedItems.map((item) => ({ - productVariantId: item.productVariantId, - quantity: item.quantity, - rashWeightGramsSnapshot: item.rashWeightGramsSnapshot, - wagePercentSnapshot: item.wagePercentSnapshot, - discountPercentSnapshot: item.discountPercentSnapshot, - computedWeightGrams: item.computedWeightGrams, - flatRialCharge: item.flatRialCharge - })) - }, - statusEvents: { - create: { toStatus: initialStatus, note: isSuspendedOverCreditLimit ? "Credit limit exceeded" : void 0 } - } - }, - include: { items: true, statusEvents: true } - }); - return order; - }); - } - async updateStatus(orderId, newStatus, actorAdminUserId, note) { - return this.prisma.$transaction(async (tx) => { - const order = await tx.order.findUnique({ where: { id: orderId } }); - if (!order) - throw new common_12.NotFoundException(`Order ${orderId} not found`); - const updated = await tx.order.update({ - where: { id: orderId }, - data: { - status: newStatus, - deliveredAt: newStatus === client_1.OrderStatus.DELIVERED_TO_CUSTOMER ? /* @__PURE__ */ new Date() : order.deliveredAt - } - }); - await tx.orderStatusEvent.create({ - data: { orderId, fromStatus: order.status, toStatus: newStatus, note, actorAdminUserId } - }); - if (newStatus === client_1.OrderStatus.DELIVERED_TO_AGENT && !order.deliveredAt) { - await this.ledger.postOrderDebt(orderId, tx); - } - return updated; - }); - } - getById(orderId) { - return this.prisma.order.findUnique({ - where: { id: orderId }, - include: { items: true, statusEvents: true, ledgerEntries: true } - }); - } - generateOrderNumber() { - const random = Math.random().toString(36).slice(2, 8).toUpperCase(); - return `ORD-${Date.now()}-${random}`; - } - }; - exports2.OrdersService = OrdersService; - exports2.OrdersService = OrdersService = __decorate4([ - (0, common_12.Injectable)(), - __metadata3("design:paramtypes", [ - prisma_service_1.PrismaService, - catalog_service_1.CatalogService, - ledger_service_1.LedgerService - ]) - ], OrdersService); - } -}); - -// dist/modules/orders/dto/create-order.dto.js -var require_create_order_dto = __commonJS({ - "dist/modules/orders/dto/create-order.dto.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - var __metadata3 = exports2 && exports2.__metadata || function(k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.CreateOrderDto = exports2.CreateOrderItemInputDto = void 0; - var class_transformer_1 = require_cjs2(); - var client_1 = require("@prisma/client"); - var class_validator_1 = require_cjs(); - var CreateOrderItemInputDto = class { - productVariantId; - quantity; - /** Making-charge percentage negotiated for this order (may differ from the variant default). */ - wagePercent; - discountPercent; - /** Flat Rial charge per unit, independent of gold weight (e.g. stones). */ - flatRialCharge; - }; - exports2.CreateOrderItemInputDto = CreateOrderItemInputDto; - __decorate4([ - (0, class_validator_1.IsString)(), - __metadata3("design:type", String) - ], CreateOrderItemInputDto.prototype, "productVariantId", void 0); - __decorate4([ - (0, class_validator_1.IsInt)(), - (0, class_validator_1.Min)(1), - __metadata3("design:type", Number) - ], CreateOrderItemInputDto.prototype, "quantity", void 0); - __decorate4([ - (0, class_validator_1.IsNumber)(), - __metadata3("design:type", Number) - ], CreateOrderItemInputDto.prototype, "wagePercent", void 0); - __decorate4([ - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsNumber)(), - __metadata3("design:type", Number) - ], CreateOrderItemInputDto.prototype, "discountPercent", void 0); - __decorate4([ - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsNumber)(), - __metadata3("design:type", Number) - ], CreateOrderItemInputDto.prototype, "flatRialCharge", void 0); - var CreateOrderDto = class { - customerId; - branchId; - /** - * Which sales channel placed the order. Legacy had no such field because the - * two channels lived in separate tables (orders vs orders_orders); the one - * Order model carries the distinction here. Defaults to RETAIL. - */ - channel; - items; - }; - exports2.CreateOrderDto = CreateOrderDto; - __decorate4([ - (0, class_validator_1.IsString)(), - __metadata3("design:type", String) - ], CreateOrderDto.prototype, "customerId", void 0); - __decorate4([ - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsString)(), - __metadata3("design:type", String) - ], CreateOrderDto.prototype, "branchId", void 0); - __decorate4([ - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsEnum)(client_1.OrderChannel), - __metadata3("design:type", String) - ], CreateOrderDto.prototype, "channel", void 0); - __decorate4([ - (0, class_validator_1.IsArray)(), - (0, class_validator_1.ArrayMinSize)(1), - (0, class_validator_1.ValidateNested)({ each: true }), - (0, class_transformer_1.Type)(() => CreateOrderItemInputDto), - __metadata3("design:type", Array) - ], CreateOrderDto.prototype, "items", void 0); - } -}); - -// dist/modules/orders/dto/update-order-status.dto.js -var require_update_order_status_dto = __commonJS({ - "dist/modules/orders/dto/update-order-status.dto.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - var __metadata3 = exports2 && exports2.__metadata || function(k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.UpdateOrderStatusDto = void 0; - var class_validator_1 = require_cjs(); - var client_1 = require("@prisma/client"); - var UpdateOrderStatusDto = class { - status; - note; - actorAdminUserId; - }; - exports2.UpdateOrderStatusDto = UpdateOrderStatusDto; - __decorate4([ - (0, class_validator_1.IsEnum)(client_1.OrderStatus), - __metadata3("design:type", String) - ], UpdateOrderStatusDto.prototype, "status", void 0); - __decorate4([ - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsString)(), - __metadata3("design:type", String) - ], UpdateOrderStatusDto.prototype, "note", void 0); - __decorate4([ - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsString)(), - __metadata3("design:type", String) - ], UpdateOrderStatusDto.prototype, "actorAdminUserId", void 0); - } -}); - -// dist/modules/orders/orders.controller.js -var require_orders_controller = __commonJS({ - "dist/modules/orders/orders.controller.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - var __metadata3 = exports2 && exports2.__metadata || function(k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); - }; - var __param3 = exports2 && exports2.__param || function(paramIndex, decorator) { - return function(target, key) { - decorator(target, key, paramIndex); - }; - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.OrdersController = void 0; - var common_12 = require_common(); - var orders_service_1 = require_orders_service(); - var create_order_dto_1 = require_create_order_dto(); - var update_order_status_dto_1 = require_update_order_status_dto(); - var guards_1 = require_guards2(); - var OrdersController = class OrdersController { - orders; - constructor(orders) { - this.orders = orders; - } - create(dto) { - return this.orders.createOrder(dto); - } - get(id) { - return this.orders.getById(id); - } - updateStatus(id, dto) { - return this.orders.updateStatus(id, dto.status, dto.actorAdminUserId, dto.note); - } - }; - exports2.OrdersController = OrdersController; - __decorate4([ - (0, common_12.Post)(), - __param3(0, (0, common_12.Body)()), - __metadata3("design:type", Function), - __metadata3("design:paramtypes", [create_order_dto_1.CreateOrderDto]), - __metadata3("design:returntype", void 0) - ], OrdersController.prototype, "create", null); - __decorate4([ - (0, common_12.Get)(":id"), - __param3(0, (0, common_12.Param)("id")), - __metadata3("design:type", Function), - __metadata3("design:paramtypes", [String]), - __metadata3("design:returntype", void 0) - ], OrdersController.prototype, "get", null); - __decorate4([ - (0, common_12.Patch)(":id/status"), - __param3(0, (0, common_12.Param)("id")), - __param3(1, (0, common_12.Body)()), - __metadata3("design:type", Function), - __metadata3("design:paramtypes", [String, update_order_status_dto_1.UpdateOrderStatusDto]), - __metadata3("design:returntype", void 0) - ], OrdersController.prototype, "updateStatus", null); - exports2.OrdersController = OrdersController = __decorate4([ - (0, guards_1.Public)(), - (0, common_12.Controller)("orders"), - __metadata3("design:paramtypes", [orders_service_1.OrdersService]) - ], OrdersController); - } -}); - -// dist/modules/accounting/dto/post-gold-repayment.dto.js -var require_post_gold_repayment_dto = __commonJS({ - "dist/modules/accounting/dto/post-gold-repayment.dto.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - var __metadata3 = exports2 && exports2.__metadata || function(k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.PostGoldRepaymentDto = void 0; - var class_validator_1 = require_cjs(); - var PostGoldRepaymentDto = class { - customerId; - instrument; - rawValue; - karatPerMille; - pricePerGramRial; - note; - }; - exports2.PostGoldRepaymentDto = PostGoldRepaymentDto; - __decorate4([ - (0, class_validator_1.IsString)(), - __metadata3("design:type", String) - ], PostGoldRepaymentDto.prototype, "customerId", void 0); - __decorate4([ - (0, class_validator_1.IsIn)(["MELTED_GOLD", "TRANSFER", "CHEQUE", "CASH"]), - __metadata3("design:type", String) - ], PostGoldRepaymentDto.prototype, "instrument", void 0); - __decorate4([ - (0, class_validator_1.IsNumber)(), - (0, class_validator_1.IsPositive)(), - __metadata3("design:type", Number) - ], PostGoldRepaymentDto.prototype, "rawValue", void 0); - __decorate4([ - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsNumber)(), - __metadata3("design:type", Number) - ], PostGoldRepaymentDto.prototype, "karatPerMille", void 0); - __decorate4([ - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsNumber)(), - (0, class_validator_1.IsPositive)(), - __metadata3("design:type", Number) - ], PostGoldRepaymentDto.prototype, "pricePerGramRial", void 0); - __decorate4([ - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsString)(), - __metadata3("design:type", String) - ], PostGoldRepaymentDto.prototype, "note", void 0); - } -}); - -// dist/modules/accounting/dto/post-rial-repayment.dto.js -var require_post_rial_repayment_dto = __commonJS({ - "dist/modules/accounting/dto/post-rial-repayment.dto.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - var __metadata3 = exports2 && exports2.__metadata || function(k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.PostRialRepaymentDto = void 0; - var class_validator_1 = require_cjs(); - var PostRialRepaymentDto = class { - customerId; - amount; - note; - }; - exports2.PostRialRepaymentDto = PostRialRepaymentDto; - __decorate4([ - (0, class_validator_1.IsString)(), - __metadata3("design:type", String) - ], PostRialRepaymentDto.prototype, "customerId", void 0); - __decorate4([ - (0, class_validator_1.IsNumber)(), - (0, class_validator_1.IsPositive)(), - __metadata3("design:type", Number) - ], PostRialRepaymentDto.prototype, "amount", void 0); - __decorate4([ - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsString)(), - __metadata3("design:type", String) - ], PostRialRepaymentDto.prototype, "note", void 0); - } -}); - -// dist/modules/accounting/accounting.controller.js -var require_accounting_controller = __commonJS({ - "dist/modules/accounting/accounting.controller.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - var __metadata3 = exports2 && exports2.__metadata || function(k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); - }; - var __param3 = exports2 && exports2.__param || function(paramIndex, decorator) { - return function(target, key) { - decorator(target, key, paramIndex); - }; - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.AccountingController = void 0; - var common_12 = require_common(); - var ledger_service_1 = require_ledger_service(); - var post_gold_repayment_dto_1 = require_post_gold_repayment_dto(); - var post_rial_repayment_dto_1 = require_post_rial_repayment_dto(); - var guards_1 = require_guards2(); - var AccountingController = class AccountingController { - ledger; - constructor(ledger) { - this.ledger = ledger; - } - getBalance(customerId) { - return this.ledger.getBalance(customerId); - } - reconcile(customerId) { - return this.ledger.reconcileBalance(customerId); - } - postGoldRepayment(dto) { - return this.ledger.postGoldRepayment(dto); - } - postRialRepayment(dto) { - return this.ledger.postRialRepayment(dto); - } - confirmEntry(entryId) { - return this.ledger.confirmEntry(entryId); - } - }; - exports2.AccountingController = AccountingController; - __decorate4([ - (0, common_12.Get)("customers/:customerId/balance"), - __param3(0, (0, common_12.Param)("customerId")), - __metadata3("design:type", Function), - __metadata3("design:paramtypes", [String]), - __metadata3("design:returntype", void 0) - ], AccountingController.prototype, "getBalance", null); - __decorate4([ - (0, common_12.Post)("customers/:customerId/reconcile"), - __param3(0, (0, common_12.Param)("customerId")), - __metadata3("design:type", Function), - __metadata3("design:paramtypes", [String]), - __metadata3("design:returntype", void 0) - ], AccountingController.prototype, "reconcile", null); - __decorate4([ - (0, common_12.Post)("repayments/gold"), - __param3(0, (0, common_12.Body)()), - __metadata3("design:type", Function), - __metadata3("design:paramtypes", [post_gold_repayment_dto_1.PostGoldRepaymentDto]), - __metadata3("design:returntype", void 0) - ], AccountingController.prototype, "postGoldRepayment", null); - __decorate4([ - (0, common_12.Post)("repayments/rial"), - __param3(0, (0, common_12.Body)()), - __metadata3("design:type", Function), - __metadata3("design:paramtypes", [post_rial_repayment_dto_1.PostRialRepaymentDto]), - __metadata3("design:returntype", void 0) - ], AccountingController.prototype, "postRialRepayment", null); - __decorate4([ - (0, common_12.Post)("entries/:entryId/confirm"), - __param3(0, (0, common_12.Param)("entryId")), - __metadata3("design:type", Function), - __metadata3("design:paramtypes", [String]), - __metadata3("design:returntype", void 0) - ], AccountingController.prototype, "confirmEntry", null); - exports2.AccountingController = AccountingController = __decorate4([ - (0, guards_1.Public)(), - (0, common_12.Controller)("accounting"), - __metadata3("design:paramtypes", [ledger_service_1.LedgerService]) - ], AccountingController); - } -}); - -// dist/modules/accounting/accounting.module.js -var require_accounting_module = __commonJS({ - "dist/modules/accounting/accounting.module.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.AccountingModule = void 0; - var common_12 = require_common(); - var ledger_service_1 = require_ledger_service(); - var accounting_controller_1 = require_accounting_controller(); - var AccountingModule = class AccountingModule { - }; - exports2.AccountingModule = AccountingModule; - exports2.AccountingModule = AccountingModule = __decorate4([ - (0, common_12.Module)({ - controllers: [accounting_controller_1.AccountingController], - providers: [ledger_service_1.LedgerService], - exports: [ledger_service_1.LedgerService] - }) - ], AccountingModule); - } -}); - -// dist/modules/orders/orders.module.js -var require_orders_module = __commonJS({ - "dist/modules/orders/orders.module.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.OrdersModule = void 0; - var common_12 = require_common(); - var orders_service_1 = require_orders_service(); - var orders_controller_1 = require_orders_controller(); - var catalog_module_1 = require_catalog_module(); - var accounting_module_1 = require_accounting_module(); - var OrdersModule = class OrdersModule { - }; - exports2.OrdersModule = OrdersModule; - exports2.OrdersModule = OrdersModule = __decorate4([ - (0, common_12.Module)({ - imports: [catalog_module_1.CatalogModule, accounting_module_1.AccountingModule], - controllers: [orders_controller_1.OrdersController], - providers: [orders_service_1.OrdersService], - exports: [orders_service_1.OrdersService] - }) - ], OrdersModule); - } -}); - -// dist/modules/pricing/pricing.service.js -var require_pricing_service = __commonJS({ - "dist/modules/pricing/pricing.service.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - var __metadata3 = exports2 && exports2.__metadata || function(k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.PricingService = void 0; - var common_12 = require_common(); - var prisma_service_1 = require_prisma_service(); - var PricingService = class PricingService { - prisma; - constructor(prisma) { - this.prisma = prisma; - } - async recordQuote(karatPerMille, pricePerGramRial, source) { - return this.prisma.goldPriceQuote.create({ - data: { karatPerMille, pricePerGramRial, source } - }); - } - async getLatestQuote(karatPerMille) { - const quote = await this.prisma.goldPriceQuote.findFirst({ - where: { karatPerMille }, - orderBy: { quotedAt: "desc" } - }); - if (!quote) - throw new common_12.NotFoundException(`No gold price quote recorded for ${karatPerMille}\u2030`); - return quote; - } - }; - exports2.PricingService = PricingService; - exports2.PricingService = PricingService = __decorate4([ - (0, common_12.Injectable)(), - __metadata3("design:paramtypes", [prisma_service_1.PrismaService]) - ], PricingService); - } -}); - -// dist/modules/pricing/pricing.controller.js -var require_pricing_controller = __commonJS({ - "dist/modules/pricing/pricing.controller.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - var __metadata3 = exports2 && exports2.__metadata || function(k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); - }; - var __param3 = exports2 && exports2.__param || function(paramIndex, decorator) { - return function(target, key) { - decorator(target, key, paramIndex); - }; - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.PricingController = void 0; - var common_12 = require_common(); - var pricing_service_1 = require_pricing_service(); - var guards_1 = require_guards2(); - var PricingController = class PricingController { - pricing; - constructor(pricing) { - this.pricing = pricing; - } - getLatest(karatPerMille) { - return this.pricing.getLatestQuote(Number(karatPerMille) || 750); - } - }; - exports2.PricingController = PricingController; - __decorate4([ - (0, common_12.Get)("latest"), - __param3(0, (0, common_12.Query)("karatPerMille")), - __metadata3("design:type", Function), - __metadata3("design:paramtypes", [String]), - __metadata3("design:returntype", void 0) - ], PricingController.prototype, "getLatest", null); - exports2.PricingController = PricingController = __decorate4([ - (0, guards_1.Public)(), - (0, common_12.Controller)("pricing"), - __metadata3("design:paramtypes", [pricing_service_1.PricingService]) - ], PricingController); - } -}); - -// dist/modules/pricing/pricing.module.js -var require_pricing_module = __commonJS({ - "dist/modules/pricing/pricing.module.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.PricingModule = void 0; - var common_12 = require_common(); - var pricing_service_1 = require_pricing_service(); - var pricing_controller_1 = require_pricing_controller(); - var PricingModule = class PricingModule { - }; - exports2.PricingModule = PricingModule; - exports2.PricingModule = PricingModule = __decorate4([ - (0, common_12.Module)({ - controllers: [pricing_controller_1.PricingController], - providers: [pricing_service_1.PricingService], - exports: [pricing_service_1.PricingService] - }) - ], PricingModule); - } -}); - // ../../node_modules/.pnpm/jws@4.0.1/node_modules/jws/lib/data-stream.js var require_data_stream = __commonJS({ "../../node_modules/.pnpm/jws@4.0.1/node_modules/jws/lib/data-stream.js"(exports2, module2) { @@ -84467,6 +83391,3512 @@ var require_umd = __commonJS({ } }); +// dist/common/env.js +var require_env = __commonJS({ + "dist/common/env.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.envOr = envOr; + exports2.envOrNull = envOrNull; + function envOr(name, fallback) { + const raw = process.env[name]; + const trimmed = typeof raw === "string" ? raw.trim() : ""; + return trimmed === "" ? fallback : trimmed; + } + function envOrNull(name) { + const raw = process.env[name]; + const trimmed = typeof raw === "string" ? raw.trim() : ""; + return trimmed === "" ? null : trimmed; + } + } +}); + +// ../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/auth_header.js +var require_auth_header = __commonJS({ + "../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/auth_header.js"(exports2, module2) { + "use strict"; + var re = /(\S+)\s+(\S+)/; + function parseAuthHeader(hdrValue) { + if (typeof hdrValue !== "string") { + return null; + } + var matches = hdrValue.match(re); + return matches && { scheme: matches[1], value: matches[2] }; + } + module2.exports = { + parse: parseAuthHeader + }; + } +}); + +// ../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/helpers/assign.js +var require_assign = __commonJS({ + "../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/helpers/assign.js"(exports2, module2) { + module2.exports = function(target, varArgs) { + if (target == null) { + throw new TypeError("Cannot convert undefined or null to object"); + } + var to = Object(target); + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + if (nextSource != null) { + for (var nextKey in nextSource) { + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; + }; + } +}); + +// ../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/verify_jwt.js +var require_verify_jwt = __commonJS({ + "../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/verify_jwt.js"(exports2, module2) { + var jwt = require_jsonwebtoken(); + module2.exports = function(token, secretOrKey, options, callback) { + return jwt.verify(token, secretOrKey, options, callback); + }; + } +}); + +// ../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/strategy.js +var require_strategy2 = __commonJS({ + "../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/strategy.js"(exports2, module2) { + var passport = require_lib6(); + var auth_hdr = require_auth_header(); + var util2 = require("util"); + var url = require("url"); + var assign = require_assign(); + function JwtStrategy(options, verify) { + passport.Strategy.call(this); + this.name = "jwt"; + this._secretOrKeyProvider = options.secretOrKeyProvider; + if (options.secretOrKey) { + if (this._secretOrKeyProvider) { + throw new TypeError("JwtStrategy has been given both a secretOrKey and a secretOrKeyProvider"); + } + this._secretOrKeyProvider = function(request, rawJwtToken, done) { + done(null, options.secretOrKey); + }; + } + if (!this._secretOrKeyProvider) { + throw new TypeError("JwtStrategy requires a secret or key"); + } + this._verify = verify; + if (!this._verify) { + throw new TypeError("JwtStrategy requires a verify callback"); + } + this._jwtFromRequest = options.jwtFromRequest; + if (!this._jwtFromRequest) { + throw new TypeError("JwtStrategy requires a function to retrieve jwt from requests (see option jwtFromRequest)"); + } + this._passReqToCallback = options.passReqToCallback; + var jsonWebTokenOptions = options.jsonWebTokenOptions || {}; + this._verifOpts = assign({}, jsonWebTokenOptions, { + audience: options.audience, + issuer: options.issuer, + algorithms: options.algorithms, + ignoreExpiration: !!options.ignoreExpiration + }); + } + util2.inherits(JwtStrategy, passport.Strategy); + JwtStrategy.JwtVerifier = require_verify_jwt(); + JwtStrategy.prototype.authenticate = function(req, options) { + var self2 = this; + var token = self2._jwtFromRequest(req); + if (!token) { + return self2.fail(new Error("No auth token")); + } + this._secretOrKeyProvider(req, token, function(secretOrKeyError, secretOrKey) { + if (secretOrKeyError) { + self2.fail(secretOrKeyError); + } else { + JwtStrategy.JwtVerifier(token, secretOrKey, self2._verifOpts, function(jwt_err, payload) { + if (jwt_err) { + return self2.fail(jwt_err); + } else { + var verified = function(err, user, info) { + if (err) { + return self2.error(err); + } else if (!user) { + return self2.fail(info); + } else { + return self2.success(user, info); + } + }; + try { + if (self2._passReqToCallback) { + self2._verify(req, payload, verified); + } else { + self2._verify(payload, verified); + } + } catch (ex) { + self2.error(ex); + } + } + }); + } + }); + }; + module2.exports = JwtStrategy; + } +}); + +// ../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/extract_jwt.js +var require_extract_jwt = __commonJS({ + "../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/extract_jwt.js"(exports2, module2) { + "use strict"; + var url = require("url"); + var auth_hdr = require_auth_header(); + var AUTH_HEADER = "authorization"; + var LEGACY_AUTH_SCHEME = "JWT"; + var BEARER_AUTH_SCHEME = "bearer"; + var extractors = {}; + extractors.fromHeader = function(header_name) { + return function(request) { + var token = null; + if (request.headers[header_name]) { + token = request.headers[header_name]; + } + return token; + }; + }; + extractors.fromBodyField = function(field_name) { + return function(request) { + var token = null; + if (request.body && Object.prototype.hasOwnProperty.call(request.body, field_name)) { + token = request.body[field_name]; + } + return token; + }; + }; + extractors.fromUrlQueryParameter = function(param_name) { + return function(request) { + var token = null, parsed_url = url.parse(request.url, true); + if (parsed_url.query && Object.prototype.hasOwnProperty.call(parsed_url.query, param_name)) { + token = parsed_url.query[param_name]; + } + return token; + }; + }; + extractors.fromAuthHeaderWithScheme = function(auth_scheme) { + var auth_scheme_lower = auth_scheme.toLowerCase(); + return function(request) { + var token = null; + if (request.headers[AUTH_HEADER]) { + var auth_params = auth_hdr.parse(request.headers[AUTH_HEADER]); + if (auth_params && auth_scheme_lower === auth_params.scheme.toLowerCase()) { + token = auth_params.value; + } + } + return token; + }; + }; + extractors.fromAuthHeaderAsBearerToken = function() { + return extractors.fromAuthHeaderWithScheme(BEARER_AUTH_SCHEME); + }; + extractors.fromExtractors = function(extractors2) { + if (!Array.isArray(extractors2)) { + throw new TypeError("extractors.fromExtractors expects an array"); + } + return function(request) { + var token = null; + var index = 0; + while (!token && index < extractors2.length) { + token = extractors2[index].call(this, request); + index++; + } + return token; + }; + }; + extractors.versionOneCompatibility = function(options) { + var authScheme = options.authScheme || LEGACY_AUTH_SCHEME, bodyField = options.tokenBodyField || "auth_token", queryParam = options.tokenQueryParameterName || "auth_token"; + return function(request) { + var authHeaderExtractor = extractors.fromAuthHeaderWithScheme(authScheme); + var token = authHeaderExtractor(request); + if (!token) { + var bodyExtractor = extractors.fromBodyField(bodyField); + token = bodyExtractor(request); + } + if (!token) { + var queryExtractor = extractors.fromUrlQueryParameter(queryParam); + token = queryExtractor(request); + } + return token; + }; + }; + module2.exports = extractors; + } +}); + +// ../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/index.js +var require_lib8 = __commonJS({ + "../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/index.js"(exports2, module2) { + "use strict"; + var Strategy = require_strategy2(); + var ExtractJwt = require_extract_jwt(); + module2.exports = { + Strategy, + ExtractJwt + }; + } +}); + +// dist/modules/customer-auth/app-principal.js +var require_app_principal = __commonJS({ + "dist/modules/customer-auth/app-principal.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.EXPERT_TOKEN_TYPE = exports2.CUSTOMER_TOKEN_TYPE = void 0; + exports2.isSalesExpert = isSalesExpert; + exports2.CUSTOMER_TOKEN_TYPE = "customer"; + exports2.EXPERT_TOKEN_TYPE = "sales-expert"; + function isSalesExpert(me) { + return me.type === exports2.EXPERT_TOKEN_TYPE && Boolean(me.adminUserId); + } + } +}); + +// dist/modules/customer-auth/customer-jwt.strategy.js +var require_customer_jwt_strategy = __commonJS({ + "dist/modules/customer-auth/customer-jwt.strategy.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.CustomerJwtStrategy = exports2.EXPERT_TOKEN_TYPE = exports2.CUSTOMER_TOKEN_TYPE = void 0; + var common_12 = require_common(); + var passport_1 = require_passport(); + var client_1 = require("@prisma/client"); + var passport_jwt_1 = require_lib8(); + var prisma_service_1 = require_prisma_service(); + var customer_auth_service_12 = require_customer_auth_service(); + var app_principal_1 = require_app_principal(); + var app_principal_2 = require_app_principal(); + Object.defineProperty(exports2, "CUSTOMER_TOKEN_TYPE", { enumerable: true, get: function() { + return app_principal_2.CUSTOMER_TOKEN_TYPE; + } }); + Object.defineProperty(exports2, "EXPERT_TOKEN_TYPE", { enumerable: true, get: function() { + return app_principal_2.EXPERT_TOKEN_TYPE; + } }); + var CustomerJwtStrategy = class CustomerJwtStrategy extends (0, passport_1.PassportStrategy)(passport_jwt_1.Strategy, "customer-jwt") { + prisma; + constructor(prisma) { + super({ + jwtFromRequest: passport_jwt_1.ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKey: (0, customer_auth_service_12.customerJwtSecret)() + }); + this.prisma = prisma; + } + async validate(payload) { + if (payload?.typ === app_principal_1.EXPERT_TOKEN_TYPE) { + return this.validateExpert(payload); + } + if (payload?.typ !== app_principal_1.CUSTOMER_TOKEN_TYPE) { + throw new common_12.UnauthorizedException(); + } + const customer = await this.prisma.customer.findUnique({ + where: { id: payload.sub }, + select: { id: true, mobile: true, isActive: true, isArchived: true } + }); + if (!customer || !customer.isActive || customer.isArchived) { + throw new common_12.UnauthorizedException(); + } + return { id: customer.id, mobile: customer.mobile, type: app_principal_1.CUSTOMER_TOKEN_TYPE }; + } + /** + * A sales expert. `sub` is their AdminUser id, so the role and active flag + * are re-checked on every request: demoting an expert or deactivating them + * locks them out immediately rather than at token expiry. + * + * The trading Customer row is resolved here rather than stored in the token, + * so a token issued before that row existed keeps working. + */ + async validateExpert(payload) { + const admin = await this.prisma.adminUser.findUnique({ + where: { id: payload.sub }, + select: { + id: true, + mobile: true, + role: true, + isActive: true, + salesExpertProfile: { + select: { + tradingCustomerId: true, + tradingCustomer: { select: { id: true, isActive: true, isArchived: true } } + } + } + } + }); + if (!admin || !admin.isActive || admin.role !== client_1.AdminRole.SALES_EXPERT) { + throw new common_12.UnauthorizedException(); + } + const trading = admin.salesExpertProfile?.tradingCustomer; + if (!trading || !trading.isActive || trading.isArchived) { + throw new common_12.UnauthorizedException(); + } + return { + id: trading.id, + mobile: admin.mobile, + type: app_principal_1.EXPERT_TOKEN_TYPE, + adminUserId: admin.id + }; + } + }; + exports2.CustomerJwtStrategy = CustomerJwtStrategy; + exports2.CustomerJwtStrategy = CustomerJwtStrategy = __decorate4([ + (0, common_12.Injectable)(), + __metadata3("design:paramtypes", [prisma_service_1.PrismaService]) + ], CustomerJwtStrategy); + } +}); + +// dist/modules/customer-auth/digits.js +var require_digits = __commonJS({ + "dist/modules/customer-auth/digits.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.faToEn = faToEn; + function faToEn(input) { + return input.replace(/[۰-۹]/g, (d) => String(d.charCodeAt(0) - 1776)).replace(/[٠-٩]/g, (d) => String(d.charCodeAt(0) - 1632)).trim(); + } + } +}); + +// dist/modules/sms/sms.service.js +var require_sms_service = __commonJS({ + "dist/modules/sms/sms.service.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + var SmsService_1; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.SmsService = void 0; + exports2.smsConfig = smsConfig; + exports2.smsConfigured = smsConfigured; + exports2.assertSmsConfigured = assertSmsConfigured; + var common_12 = require_common(); + var client_1 = require("@prisma/client"); + var prisma_service_1 = require_prisma_service(); + var env_12 = require_env(); + var DEFAULT_TREZ_URL = "https://smspanel.trez.ir/SendMessageWithCode.ashx"; + var REQUEST_TIMEOUT_MS = 2e4; + function smsConfig() { + return { + provider: (0, env_12.envOr)("SMS_PROVIDER", "trez").toLowerCase(), + url: (0, env_12.envOr)("SMS_TREZ_URL", DEFAULT_TREZ_URL), + username: process.env.SMS_TREZ_USERNAME ?? "", + password: process.env.SMS_TREZ_PASSWORD ?? "", + senderLine: process.env.SMS_TREZ_SENDER_LINE ?? "" + }; + } + function smsConfigured() { + const c = smsConfig(); + if (c.provider === "none") + return false; + return Boolean(c.username && c.password && c.senderLine); + } + function assertSmsConfigured(logger = new common_12.Logger("SmsConfig")) { + const c = smsConfig(); + if (c.provider === "none") { + logger.warn("SMS_PROVIDER=none \u2014 no SMS will be sent."); + return; + } + if (c.provider !== "trez") { + logger.warn(`Unknown SMS_PROVIDER "${c.provider}" (supported: "trez", "none"). No SMS will be sent.`); + return; + } + const missing = [ + ["SMS_TREZ_USERNAME", c.username], + ["SMS_TREZ_PASSWORD", c.password], + ["SMS_TREZ_SENDER_LINE", c.senderLine] + ].filter(([, v]) => !v).map(([name]) => name); + if (missing.length === 0) + return; + logger.warn(`SMS provider "trez" is selected but these are not set: ${missing.join(", ")}. Messages are logged, not sent. Customers cannot receive a login code.`); + } + var SmsService = SmsService_1 = class SmsService { + prisma; + logger = new common_12.Logger(SmsService_1.name); + constructor(prisma) { + this.prisma = prisma; + } + /** + * Sends one message. Never throws — a failed SMS must not roll back the + * business operation that triggered it, exactly as in the reference. + */ + async send(mobile, body, options = {}) { + const { persist = true, audience = client_1.MessageAudience.SINGLE_NUMBER } = options; + const text = body.trim(); + let result; + if (!smsConfigured()) { + this.logger.warn(`[dev] SMS not configured; not sending to ${mobile}`); + result = { ok: false, error: "sms-not-configured" }; + } else { + result = await this.deliverViaTrez(mobile, text); + } + if (persist) { + await this.record(mobile, text, audience); + } + return result; + } + /** POST to the Trez panel, shaped exactly as the reference's form post. */ + async deliverViaTrez(mobile, text) { + const c = smsConfig(); + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS); + try { + const response = await fetch(c.url, { + method: "POST", + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams({ + PhoneNumber: c.senderLine, + Username: c.username, + Password: c.password, + Mobile: mobile, + Message: text + }).toString(), + signal: controller.signal + }); + const responseBody = await response.text(); + if (!response.ok) { + this.logger.error(`SMS to ${mobile} failed: HTTP ${response.status} ${responseBody}`); + return { ok: false, response: responseBody, error: `http-${response.status}` }; + } + return { ok: true, response: responseBody }; + } catch (e) { + const error = e instanceof Error ? e.message : String(e); + this.logger.error(`SMS to ${mobile} failed: ${error}`); + return { ok: false, error }; + } finally { + clearTimeout(timer); + } + } + /** The `messages` row the reference writes for every send (`Sms.php:120-129`). */ + async record(mobile, body, audience) { + try { + await this.prisma.smsMessage.create({ + data: { audience, recipients: mobile, body, sentCount: 1 } + }); + } catch (e) { + this.logger.error(`Could not record the SMS to ${mobile}: ${String(e)}`); + } + } + }; + exports2.SmsService = SmsService; + exports2.SmsService = SmsService = SmsService_1 = __decorate4([ + (0, common_12.Injectable)(), + __metadata3("design:paramtypes", [prisma_service_1.PrismaService]) + ], SmsService); + } +}); + +// dist/modules/customer-auth/customer-auth.service.js +var require_customer_auth_service = __commonJS({ + "dist/modules/customer-auth/customer-auth.service.js"(exports2) { + "use strict"; + var __createBinding2 = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + }) : (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + })); + var __setModuleDefault2 = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + }) : function(o, v) { + o["default"] = v; + }); + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __importStar2 = exports2 && exports2.__importStar || /* @__PURE__ */ (function() { + var ownKeys2 = function(o) { + ownKeys2 = Object.getOwnPropertyNames || function(o2) { + var ar = []; + for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys2(o); + }; + return function(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) { + for (var k = ownKeys2(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding2(result, mod, k[i]); + } + __setModuleDefault2(result, mod); + return result; + }; + })(); + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + var CustomerAuthService_1; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.CustomerAuthService = void 0; + exports2.masterOtpCode = masterOtpCode; + exports2.masterOtpEnabled = masterOtpEnabled; + exports2.customerJwtSecret = customerJwtSecret; + var common_12 = require_common(); + var jwt_1 = (init_dist(), __toCommonJS(dist_exports)); + var client_1 = require("@prisma/client"); + var bcrypt = __importStar2(require_umd()); + var prisma_service_1 = require_prisma_service(); + var env_12 = require_env(); + var customer_jwt_strategy_1 = require_customer_jwt_strategy(); + var digits_1 = require_digits(); + var sms_service_12 = require_sms_service(); + var TooManyRequestsException = class extends common_12.HttpException { + constructor(message) { + super(message, common_12.HttpStatus.TOO_MANY_REQUESTS); + } + }; + var OTP_TTL_MS = 2 * 60 * 1e3; + var MAX_ATTEMPTS = 5; + var MAX_SENDS_PER_MOBILE = 5; + var MAX_SENDS_PER_IP = 20; + var SEND_WINDOW_MS = 10 * 60 * 1e3; + var RESEND_COOLDOWN_MS = 60 * 1e3; + function masterOtpCode() { + const raw = process.env.OTP_MASTER_CODE?.trim(); + return raw ? raw : null; + } + function masterOtpEnabled() { + return masterOtpCode() !== null; + } + var CustomerAuthService = CustomerAuthService_1 = class CustomerAuthService { + prisma; + jwt; + sms; + logger = new common_12.Logger(CustomerAuthService_1.name); + constructor(prisma, jwt, sms) { + this.prisma = prisma; + this.jwt = jwt; + this.sms = sms; + } + /** + * Step 1 — port of ApiMobileController::userAuth (henza_server:39-49). + * + * Reference: validate `username`, find an ACTIVE customer by mobile, generate + * rand(1000,9999), store it, send it by SMS, return `true`. + * + * Kept: the active-customer filter, the 4-digit range, the opaque `true` + * response (it never reveals whether the number exists). + * Changed: the code is hashed, it expires, and sends are rate limited. + */ + async requestOtp(rawMobile, ip) { + const mobile = (0, digits_1.faToEn)(rawMobile); + await this.assertSendAllowed(mobile, ip); + const expertAccount = await this.resolveExpertTradingAccount(mobile); + const customer = expertAccount ?? await this.prisma.customer.findFirst({ + where: { mobile, isActive: true, isArchived: false }, + select: { id: true } + }); + if (!customer) { + this.logger.warn(`OTP requested for unknown or inactive mobile ${mobile}`); + return { sent: true }; + } + const code = String(Math.floor(1e3 + Math.random() * 9e3)); + await this.prisma.customerOtp.create({ + data: { + mobile, + customerId: customer.id, + codeHash: await bcrypt.hash(code, 10), + expiresAt: new Date(Date.now() + OTP_TTL_MS), + requestIp: ip ?? null + } + }); + await this.deliver(mobile, code); + return { sent: true }; + } + /** + * D21 — find an active sales expert by mobile and return their trading + * Customer account, creating it on first sign-in. + * + * WHY A TRADING ACCOUNT EXISTS + * In the reference a sales agent WAS a Customer (type=1), so `setOrder` did + * `Customer::findOrFail(auth()->id())` and read that row's `gram_level` + * (ApiMobileController.php:534-535) — the agent's own order, own credit + * limit, own ledger. Here the expert is an AdminUser (D17), which has no + * ledger and cannot be an Order's subject, so this one linked Customer row + * plays that part. It is NOT one of their referred customers. + * + * Seeded from what the reference used for exactly these two numbers: + * gram_level -> goldCreditLimitGrams (SalesExpertProfile.gramLevel) + * percent -> makingChargeOffsetPercent (AdminUser.wagePercent) + * + * Returns null for any mobile that is not an active sales expert, so an + * ordinary customer's login is completely unaffected. + */ + async resolveExpertTradingAccount(mobile) { + const admin = await this.prisma.adminUser.findFirst({ + where: { mobile, isActive: true, role: client_1.AdminRole.SALES_EXPERT }, + select: { + id: true, + fullName: true, + firstName: true, + lastName: true, + mobile: true, + wagePercent: true, + salesExpertProfile: { + select: { id: true, gramLevel: true, nationalCode: true, tradingCustomerId: true } + } + } + }); + if (!admin) + return null; + const existingId = admin.salesExpertProfile?.tradingCustomerId; + if (existingId) { + const existing = await this.prisma.customer.findUnique({ + where: { id: existingId }, + select: { id: true, isActive: true, isArchived: true } + }); + if (existing && existing.isActive && !existing.isArchived) + return { id: existing.id }; + this.logger.warn(`Sales expert ${admin.id} has a trading account that is inactive or archived; refusing to create another.`); + return null; + } + if (!admin.salesExpertProfile) { + this.logger.warn(`Sales expert ${admin.id} has no SalesExpertProfile; cannot sign in to the app.`); + return null; + } + const clash = await this.prisma.customer.findFirst({ + where: { mobile }, + select: { id: true, isActive: true, isArchived: true } + }); + if (clash) { + this.logger.warn(`Mobile ${mobile} belongs to both sales expert ${admin.id} and customer ${clash.id}; adopting the existing customer row as the expert's trading account.`); + if (!clash.isActive || clash.isArchived) + return null; + await this.prisma.salesExpertProfile.update({ + where: { id: admin.salesExpertProfile.id }, + data: { tradingCustomerId: clash.id } + }); + return { id: clash.id }; + } + const created = await this.prisma.customer.create({ + data: { + fullName: admin.fullName, + firstName: admin.firstName, + lastName: admin.lastName, + mobile: admin.mobile, + // Customer.nationalCode is required and unique. The expert's own code + // when they have one, else the mobile — the same fallback + // admin-customers.service.ts uses when a form omits it. + nationalCode: admin.salesExpertProfile.nationalCode || admin.mobile, + // No app password: this account is reached only through the expert's + // AdminUser session, never by a password login of its own. + passwordHash: "", + kind: client_1.CustomerKind.APP_SHOPKEEPER, + goldCreditLimitGrams: admin.salesExpertProfile.gramLevel, + makingChargeOffsetPercent: admin.wagePercent ?? new client_1.Prisma.Decimal(0) + }, + select: { id: true } + }); + await this.prisma.salesExpertProfile.update({ + where: { id: admin.salesExpertProfile.id }, + data: { tradingCustomerId: created.id } + }); + this.logger.log(`Created trading account ${created.id} for sales expert ${admin.id} on first app sign-in.`); + return { id: created.id }; + } + /** + * Step 2 — port of ApiMobileController::userVerfiy (henza_server:51-64). + * + * Reference: `if (faTOen($request->password) != $customer->send_password) abort(422);` + * then issue a token and return THAT ONE customer. + * + * The parity behaviour is the comparison and the rejection. The attempt + * counter, the expiry check and the single-use consumption are the D6 + * additions. + */ + async verifyOtp(rawMobile, rawCode, ip) { + const mobile = (0, digits_1.faToEn)(rawMobile); + const code = (0, digits_1.faToEn)(rawCode); + const reject = () => new common_12.UnauthorizedException("\u06A9\u062F \u0648\u0627\u0631\u062F \u0634\u062F\u0647 \u0635\u062D\u06CC\u062D \u0646\u06CC\u0633\u062A"); + const master = masterOtpCode(); + if (master !== null && code === master) { + const expert2 = await this.resolveExpertPrincipal(mobile); + if (expert2) { + this.logger.warn(`OTP_MASTER_CODE used to sign in SALES EXPERT ${expert2.adminUserId} (mobile ${mobile})`); + return { + token: await this.issueExpertToken(expert2.adminUserId, mobile), + customer: { id: expert2.tradingCustomerId, mobile } + }; + } + const customer = await this.prisma.customer.findFirst({ + where: { mobile, isActive: true, isArchived: false }, + select: { id: true, mobile: true } + }); + if (!customer) + throw reject(); + this.logger.warn(`OTP_MASTER_CODE used to sign in customer ${customer.id} (mobile ${customer.mobile})`); + return { + token: await this.issueToken(customer.id, customer.mobile), + customer: { id: customer.id, mobile: customer.mobile } + }; + } + const challenge = await this.prisma.customerOtp.findFirst({ + where: { mobile, consumedAt: null }, + orderBy: { createdAt: "desc" }, + include: { + customer: { + select: { id: true, mobile: true, isActive: true, isArchived: true } + } + } + }); + if (!challenge) + throw reject(); + if (challenge.expiresAt.getTime() < Date.now()) + throw reject(); + if (challenge.attempts >= MAX_ATTEMPTS) { + throw new TooManyRequestsException("\u062A\u0639\u062F\u0627\u062F \u062A\u0644\u0627\u0634\u200C\u0647\u0627\u06CC \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u06CC\u0634 \u0627\u0632 \u062D\u062F \u0645\u062C\u0627\u0632 \u0627\u0633\u062A. \u0644\u0637\u0641\u0627\u064B \u06A9\u062F \u062C\u062F\u06CC\u062F\u06CC \u062F\u0631\u062E\u0648\u0627\u0633\u062A \u06A9\u0646\u06CC\u062F."); + } + const ok = await bcrypt.compare(code, challenge.codeHash); + if (!ok) { + await this.prisma.customerOtp.update({ + where: { id: challenge.id }, + data: { attempts: { increment: 1 } } + }); + throw reject(); + } + if (!challenge.customer.isActive || challenge.customer.isArchived) { + throw reject(); + } + await this.prisma.customerOtp.update({ + where: { id: challenge.id }, + data: { consumedAt: /* @__PURE__ */ new Date() } + }); + const expert = await this.resolveExpertPrincipal(mobile); + if (expert) { + return { + token: await this.issueExpertToken(expert.adminUserId, mobile), + customer: { id: expert.tradingCustomerId, mobile } + }; + } + return { + token: await this.issueToken(challenge.customer.id, challenge.customer.mobile), + customer: { id: challenge.customer.id, mobile: challenge.customer.mobile } + }; + } + /** + * D21 — is this mobile an active sales expert with a usable trading account? + * + * Read-only: unlike `resolveExpertTradingAccount`, this never creates + * anything, because by verify time requestOtp has already done so. Returns + * null for every ordinary customer, so their path is untouched. + */ + async resolveExpertPrincipal(mobile) { + const admin = await this.prisma.adminUser.findFirst({ + where: { mobile, isActive: true, role: client_1.AdminRole.SALES_EXPERT }, + select: { + id: true, + salesExpertProfile: { + select: { + tradingCustomer: { select: { id: true, isActive: true, isArchived: true } } + } + } + } + }); + const trading = admin?.salesExpertProfile?.tradingCustomer; + if (!admin || !trading || !trading.isActive || trading.isArchived) + return null; + return { adminUserId: admin.id, tradingCustomerId: trading.id }; + } + /** + * Sign a SALES EXPERT app token. + * + * `sub` is the AdminUser id — the durable identity — not the trading + * Customer id, so the token survives anything that changes the trading row + * and the role check can be re-applied on every request. + * + * Same secret and lifetime as a customer token; only `typ` differs. It is + * NOT an admin-panel token: JwtStrategy requires the admin type and refuses + * this one on every admin route. + */ + issueExpertToken(adminUserId, mobile) { + const payload = { + sub: adminUserId, + mobile, + typ: customer_jwt_strategy_1.EXPERT_TOKEN_TYPE + }; + return this.jwt.signAsync(payload, { + secret: customerJwtSecret(), + expiresIn: (0, env_12.envOr)("JWT_CUSTOMER_EXPIRES_IN", "30d") + }); + } + /** + * Sign a customer JWT. Shared by the normal verify path and the D20 master + * code so both issue exactly the same token — the bypass grants no extra + * privilege and no longer lifetime. + * + * Reference reused api_token forever once set (:60-62); a fresh token is + * issued on every verify here (T1). + */ + issueToken(customerId, mobile) { + const payload = { + sub: customerId, + mobile, + typ: customer_jwt_strategy_1.CUSTOMER_TOKEN_TYPE + }; + return this.jwt.signAsync(payload, { + secret: customerJwtSecret(), + // jsonwebtoken types expiresIn as a template-literal union, which a plain + // string from process.env does not satisfy — same cast AuthModule uses. + expiresIn: (0, env_12.envOr)("JWT_CUSTOMER_EXPIRES_IN", "30d") + }); + } + /** + * Per-mobile and per-IP send throttle. + * + * Counted off the CustomerOtp rows rather than an in-memory map, so the limit + * survives a restart and holds across replicas. The reference had no limit: + * userAuth could be called in a loop, each call sending a real (billed) SMS. + */ + async assertSendAllowed(mobile, ip) { + const since = new Date(Date.now() - SEND_WINDOW_MS); + const lastToMobile = await this.prisma.customerOtp.findFirst({ + where: { mobile }, + orderBy: { createdAt: "desc" }, + select: { createdAt: true } + }); + if (lastToMobile && Date.now() - lastToMobile.createdAt.getTime() < RESEND_COOLDOWN_MS) { + const wait = Math.ceil((RESEND_COOLDOWN_MS - (Date.now() - lastToMobile.createdAt.getTime())) / 1e3); + throw new TooManyRequestsException(`\u0644\u0637\u0641\u0627\u064B ${wait} \u062B\u0627\u0646\u06CC\u0647 \u062F\u06CC\u06AF\u0631 \u062A\u0644\u0627\u0634 \u06A9\u0646\u06CC\u062F`); + } + const sentToMobile = await this.prisma.customerOtp.count({ + where: { mobile, createdAt: { gte: since } } + }); + if (sentToMobile >= MAX_SENDS_PER_MOBILE) { + throw new TooManyRequestsException("\u062A\u0639\u062F\u0627\u062F \u062F\u0631\u062E\u0648\u0627\u0633\u062A\u200C\u0647\u0627\u06CC \u06A9\u062F \u0628\u06CC\u0634 \u0627\u0632 \u062D\u062F \u0645\u062C\u0627\u0632 \u0627\u0633\u062A. \u0644\u0637\u0641\u0627\u064B \u0628\u0639\u062F\u0627\u064B \u062A\u0644\u0627\u0634 \u06A9\u0646\u06CC\u062F."); + } + if (ip) { + const sentFromIp = await this.prisma.customerOtp.count({ + where: { requestIp: ip, createdAt: { gte: since } } + }); + if (sentFromIp >= MAX_SENDS_PER_IP) { + throw new TooManyRequestsException("\u062A\u0639\u062F\u0627\u062F \u062F\u0631\u062E\u0648\u0627\u0633\u062A\u200C\u0647\u0627\u06CC \u06A9\u062F \u0628\u06CC\u0634 \u0627\u0632 \u062D\u062F \u0645\u062C\u0627\u0632 \u0627\u0633\u062A. \u0644\u0637\u0641\u0627\u064B \u0628\u0639\u062F\u0627\u064B \u062A\u0644\u0627\u0634 \u06A9\u0646\u06CC\u062F."); + } + } + } + /** + * Hands the code to the SMS gateway (SmsService -> Trez, per Sms.php:57-82). + * + * When no credentials are configured, SmsService does not send and the code + * is logged instead — development only; production refuses to boot without + * credentials (assertSmsConfigured in main.ts). + * + * A failed send does NOT throw. The reference ignored Sms::Send's result, so + * a gateway outage never blocked the flow that triggered it; the customer + * simply retries, and the throttle in assertSendAllowed still applies. + */ + async deliver(mobile, code) { + const smsText = `<#> code is ${code} +\u0631\u0645\u0632 \u0639\u0628\u0648\u0631 \u06CC\u06A9\u0628\u0627\u0631 \u0645\u0635\u0631\u0641 \u0634\u0645\u0627 \u062C\u0647\u062A \u0648\u0631\u0648\u062F \u0628\u0647 \u0627\u067E\u0644\u06CC\u06A9\u0634\u0646 \u0647\u0646\u0632\u0627 \u06AF\u0644\u062F ${code} \u0645\u06CC \u0628\u0627\u0634\u062F +aMo6iFyUQaY`; + await this.sms.send(mobile, smsText, { persist: false }); + if (process.env.NODE_ENV !== "production" && !(0, sms_service_12.smsConfigured)()) { + this.logger.log(`[dev] OTP for ${mobile}: ${code}`); + } + await this.prisma.smsMessage.create({ + data: { + audience: client_1.MessageAudience.SINGLE_NUMBER, + recipients: mobile, + body: "\u06A9\u062F \u06CC\u06A9\u0628\u0627\u0631 \u0645\u0635\u0631\u0641 \u0648\u0631\u0648\u062F (\u0645\u062A\u0646 \u067E\u06CC\u0627\u0645 \u062B\u0628\u062A \u0646\u0645\u06CC\u200C\u0634\u0648\u062F)", + sentCount: 1 + } + }); + } + }; + exports2.CustomerAuthService = CustomerAuthService; + exports2.CustomerAuthService = CustomerAuthService = CustomerAuthService_1 = __decorate4([ + (0, common_12.Injectable)(), + __metadata3("design:paramtypes", [ + prisma_service_1.PrismaService, + jwt_1.JwtService, + sms_service_12.SmsService + ]) + ], CustomerAuthService); + function customerJwtSecret() { + return (0, env_12.envOrNull)("JWT_CUSTOMER_SECRET") ?? (0, env_12.envOrNull)("JWT_SECRET") ?? "dev-only-insecure-customer-secret"; + } + } +}); + +// dist/modules/customer-auth/dto/request-otp.dto.js +var require_request_otp_dto = __commonJS({ + "dist/modules/customer-auth/dto/request-otp.dto.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.RequestOtpDto = void 0; + var class_validator_1 = require_cjs(); + var RequestOtpDto = class { + username; + }; + exports2.RequestOtpDto = RequestOtpDto; + __decorate4([ + (0, class_validator_1.IsString)(), + (0, class_validator_1.IsNotEmpty)({ message: "\u0634\u0645\u0627\u0631\u0647 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A" }), + (0, class_validator_1.MaxLength)(20), + __metadata3("design:type", String) + ], RequestOtpDto.prototype, "username", void 0); + } +}); + +// dist/modules/customer-auth/dto/verify-otp.dto.js +var require_verify_otp_dto = __commonJS({ + "dist/modules/customer-auth/dto/verify-otp.dto.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.VerifyOtpDto = void 0; + var class_validator_1 = require_cjs(); + var VerifyOtpDto = class { + username; + password; + }; + exports2.VerifyOtpDto = VerifyOtpDto; + __decorate4([ + (0, class_validator_1.IsString)(), + (0, class_validator_1.IsNotEmpty)({ message: "\u0634\u0645\u0627\u0631\u0647 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A" }), + (0, class_validator_1.MaxLength)(20), + __metadata3("design:type", String) + ], VerifyOtpDto.prototype, "username", void 0); + __decorate4([ + (0, class_validator_1.IsString)(), + (0, class_validator_1.IsNotEmpty)({ message: "\u06A9\u062F \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A" }), + (0, class_validator_1.MaxLength)(10), + __metadata3("design:type", String) + ], VerifyOtpDto.prototype, "password", void 0); + } +}); + +// dist/modules/customer-auth/customer.guards.js +var require_customer_guards = __commonJS({ + "dist/modules/customer-auth/customer.guards.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.CurrentCustomer = exports2.CustomerAuth = exports2.CustomerJwtGuard = void 0; + var common_12 = require_common(); + var passport_1 = require_passport(); + var guards_1 = require_guards2(); + var common_2 = require_common(); + var CustomerJwtGuard = class CustomerJwtGuard extends (0, passport_1.AuthGuard)("customer-jwt") { + }; + exports2.CustomerJwtGuard = CustomerJwtGuard; + exports2.CustomerJwtGuard = CustomerJwtGuard = __decorate4([ + (0, common_12.Injectable)() + ], CustomerJwtGuard); + var CustomerAuth = () => (0, common_2.applyDecorators)((0, guards_1.Public)(), (0, common_2.UseGuards)(CustomerJwtGuard)); + exports2.CustomerAuth = CustomerAuth; + exports2.CurrentCustomer = (0, common_12.createParamDecorator)((_data, ctx) => ctx.switchToHttp().getRequest().user); + } +}); + +// dist/modules/customer-auth/customer-auth.controller.js +var require_customer_auth_controller = __commonJS({ + "dist/modules/customer-auth/customer-auth.controller.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + var __param3 = exports2 && exports2.__param || function(paramIndex, decorator) { + return function(target, key) { + decorator(target, key, paramIndex); + }; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.CustomerAuthController = void 0; + var common_12 = require_common(); + var customer_auth_service_12 = require_customer_auth_service(); + var customers_service_1 = require_customers_service(); + var request_otp_dto_1 = require_request_otp_dto(); + var verify_otp_dto_1 = require_verify_otp_dto(); + var guards_1 = require_guards2(); + var customer_guards_1 = require_customer_guards(); + var CustomerAuthController = class CustomerAuthController { + auth; + customers; + constructor(auth, customers) { + this.auth = auth; + this.customers = customers; + } + /** Step 1. Always answers `{ sent: true }` — it never reveals whether the number is registered. */ + requestOtp(dto, ip) { + return this.auth.requestOtp(dto.username, ip); + } + /** Step 2. Returns the token and that one customer, as the reference did. */ + verifyOtp(dto, ip) { + return this.auth.verifyOtp(dto.username, dto.password, ip); + } + me(me) { + return this.customers.getOrThrow(me.id); + } + myOrders(me) { + return this.customers.getOrders(me.id); + } + myLedgerEntries(me) { + return this.customers.getLedgerEntries(me.id); + } + }; + exports2.CustomerAuthController = CustomerAuthController; + __decorate4([ + (0, guards_1.Public)(), + (0, common_12.Post)("auth/request-otp"), + (0, common_12.HttpCode)(200), + __param3(0, (0, common_12.Body)()), + __param3(1, (0, common_12.Ip)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [request_otp_dto_1.RequestOtpDto, String]), + __metadata3("design:returntype", void 0) + ], CustomerAuthController.prototype, "requestOtp", null); + __decorate4([ + (0, guards_1.Public)(), + (0, common_12.Post)("auth/verify-otp"), + (0, common_12.HttpCode)(200), + __param3(0, (0, common_12.Body)()), + __param3(1, (0, common_12.Ip)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [verify_otp_dto_1.VerifyOtpDto, String]), + __metadata3("design:returntype", void 0) + ], CustomerAuthController.prototype, "verifyOtp", null); + __decorate4([ + (0, customer_guards_1.CustomerAuth)(), + (0, common_12.Get)("me"), + __param3(0, (0, customer_guards_1.CurrentCustomer)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [Object]), + __metadata3("design:returntype", void 0) + ], CustomerAuthController.prototype, "me", null); + __decorate4([ + (0, customer_guards_1.CustomerAuth)(), + (0, common_12.Get)("me/orders"), + __param3(0, (0, customer_guards_1.CurrentCustomer)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [Object]), + __metadata3("design:returntype", void 0) + ], CustomerAuthController.prototype, "myOrders", null); + __decorate4([ + (0, customer_guards_1.CustomerAuth)(), + (0, common_12.Get)("me/ledger-entries"), + __param3(0, (0, customer_guards_1.CurrentCustomer)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [Object]), + __metadata3("design:returntype", void 0) + ], CustomerAuthController.prototype, "myLedgerEntries", null); + exports2.CustomerAuthController = CustomerAuthController = __decorate4([ + (0, common_12.Controller)("customer"), + __metadata3("design:paramtypes", [ + customer_auth_service_12.CustomerAuthService, + customers_service_1.CustomersService + ]) + ], CustomerAuthController); + } +}); + +// dist/modules/customer-auth/customer-auth.module.js +var require_customer_auth_module = __commonJS({ + "dist/modules/customer-auth/customer-auth.module.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.CustomerAuthModule = void 0; + var common_12 = require_common(); + var jwt_1 = (init_dist(), __toCommonJS(dist_exports)); + var passport_1 = require_passport(); + var prisma_module_1 = require_prisma_module(); + var customers_module_1 = require_customers_module(); + var customer_auth_controller_1 = require_customer_auth_controller(); + var customer_auth_service_12 = require_customer_auth_service(); + var customer_jwt_strategy_1 = require_customer_jwt_strategy(); + var CustomerAuthModule = class CustomerAuthModule { + }; + exports2.CustomerAuthModule = CustomerAuthModule; + exports2.CustomerAuthModule = CustomerAuthModule = __decorate4([ + (0, common_12.Module)({ + imports: [prisma_module_1.PrismaModule, passport_1.PassportModule, jwt_1.JwtModule.register({}), customers_module_1.CustomersModule], + controllers: [customer_auth_controller_1.CustomerAuthController], + providers: [customer_auth_service_12.CustomerAuthService, customer_jwt_strategy_1.CustomerJwtStrategy], + exports: [customer_auth_service_12.CustomerAuthService] + }) + ], CustomerAuthModule); + } +}); + +// dist/modules/sales-expert-app/sales-expert-app.service.js +var require_sales_expert_app_service = __commonJS({ + "dist/modules/sales-expert-app/sales-expert-app.service.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.SalesExpertAppService = void 0; + var common_12 = require_common(); + var client_1 = require("@prisma/client"); + var prisma_service_1 = require_prisma_service(); + var SalesExpertAppService = class SalesExpertAppService { + prisma; + constructor(prisma) { + this.prisma = prisma; + } + /** Refuses any principal that is not a sales expert. */ + assertExpert(adminUserId) { + if (!adminUserId) { + throw new common_12.ForbiddenException("\u0627\u06CC\u0646 \u0628\u062E\u0634 \u0641\u0642\u0637 \u0628\u0631\u0627\u06CC \u06A9\u0627\u0631\u0634\u0646\u0627\u0633 \u0641\u0631\u0648\u0634 \u0627\u0633\u062A"); + } + return adminUserId; + } + /** + * Port of `getChildOrder` (ApiMobileController.php:759-781). + * + * The reference filtered `where('sts', 5)` — legacy status 5 is + * «تحویل مسئول فروش» (delivered to the sales agent), the point at which the + * agent has to act on the order. Reproduced with the equivalent status here; + * the heading the reference hardcodes on every card is that same label + * (ChildOrders.vue:48). + * + * Search covers what the reference searched: order id, customer name and + * customer mobile (`initSearch(['id','customer_id','customer.name','customer.mobile'])`). + * Ordered `id DESC`, i.e. newest first. + */ + async childOrders(adminUserId, params) { + const agentId = this.assertExpert(adminUserId); + const page = Math.max(1, Number(params.page ?? 1)); + const limit = Math.min(100, Math.max(1, Number(params.limit ?? 20))); + const search = (params.search ?? "").trim(); + const scope = { + // The order belongs to one of THIS expert's referred customers. An + // expert can never see another expert's customers, and no id from the + // request takes part in this. + customer: { referredByAgentId: agentId }, + status: client_1.OrderStatus.DELIVERED_TO_AGENT + }; + const where = search ? { + AND: [ + scope, + { + OR: [ + { orderNumber: { contains: search, mode: "insensitive" } }, + { customer: { fullName: { contains: search, mode: "insensitive" } } }, + { customer: { mobile: { contains: search } } } + ] + } + ] + } : scope; + const [rows, total] = await Promise.all([ + this.prisma.order.findMany({ + where, + orderBy: { createdAt: "desc" }, + skip: (page - 1) * limit, + take: limit, + include: { + items: true, + customer: { select: { id: true, fullName: true, mobile: true } } + } + }), + this.prisma.order.count({ where }) + ]); + return { + rows: rows.map((o) => ({ + id: o.id, + orderNumber: o.orderNumber, + status: o.status, + // The reference's card shows the order's total weight as `item.w` + // (ChildOrders.vue:55) and the item count as `items.length`. + w: o.totalGoldWeightGrams, + totalGoldWeightGrams: o.totalGoldWeightGrams, + createdAt: o.createdAt, + items: o.items, + customer: o.customer ? { id: o.customer.id, name: o.customer.fullName, mobile: o.customer.mobile } : null + })), + total, + page, + limit + }; + } + /** + * Port of `myChild` (ApiMobileController.php:184-195) — the expert's own + * referred customers, for "انتخاب مشتری". + * + * The reference searched name, mobile, phone, store_name and pasazh_name; + * all five exist here. It also returned EVERY referred customer, active or + * not — the `active()` scope is commented out at :187 — so that is + * reproduced rather than "fixed" (D4). + * + * NOT ported: the reference minted an `api_token` for each listed customer + * as a side effect (:197-201), which let an agent act AS that customer. That + * is a session-hijack primitive; this system derives every session from the + * caller's own token. See D21. + */ + async myCustomers(adminUserId, params) { + const agentId = this.assertExpert(adminUserId); + const search = (params.search ?? "").trim(); + const scope = { referredByAgentId: agentId }; + const where = search ? { + AND: [ + scope, + { + OR: [ + { fullName: { contains: search, mode: "insensitive" } }, + { mobile: { contains: search } }, + { phone: { contains: search } }, + { storeName: { contains: search, mode: "insensitive" } }, + { pasazhName: { contains: search, mode: "insensitive" } } + ] + } + ] + } : scope; + const rows = await this.prisma.customer.findMany({ + where, + orderBy: { createdAt: "desc" }, + select: { + id: true, + fullName: true, + mobile: true, + phone: true, + storeName: true, + pasazhName: true, + goldCreditLimitGrams: true, + goldDebtGrams: true, + fiatDebtRial: true, + isActive: true, + createdAt: true + } + }); + return rows.map((c) => ({ + id: c.id, + // legacy key, which the ported card binds to + name: c.fullName, + fullName: c.fullName, + mobile: c.mobile, + phone: c.phone, + store_name: c.storeName, + pasazh_name: c.pasazhName, + gram_level: c.goldCreditLimitGrams, + remain: c.goldDebtGrams, + rial_remain: c.fiatDebtRial, + active: c.isActive ? 1 : -1, + createdAt: c.createdAt + })); + } + /** The expert's own profile strip, for the app header. */ + async me(adminUserId) { + const agentId = this.assertExpert(adminUserId); + const admin = await this.prisma.adminUser.findUnique({ + where: { id: agentId }, + select: { + id: true, + fullName: true, + firstName: true, + lastName: true, + mobile: true, + wagePercent: true, + salesExpertProfile: { + select: { gramLevel: true, tradingCustomerId: true, city: { select: { id: true, name: true } } } + }, + _count: { select: { referredCustomers: true } } + } + }); + return { + id: admin?.id, + name: admin?.fullName, + fname: admin?.firstName ?? admin?.fullName, + lname: admin?.lastName ?? "", + mobile: admin?.mobile, + percent: admin?.wagePercent, + gram_level: admin?.salesExpertProfile?.gramLevel ?? null, + city: admin?.salesExpertProfile?.city ? { id: admin.salesExpertProfile.city.id, title: admin.salesExpertProfile.city.name } : null, + customers_count: admin?._count.referredCustomers ?? 0, + /** The Customer row the expert's OWN orders land on. */ + trading_customer_id: admin?.salesExpertProfile?.tradingCustomerId ?? null + }; + } + }; + exports2.SalesExpertAppService = SalesExpertAppService; + exports2.SalesExpertAppService = SalesExpertAppService = __decorate4([ + (0, common_12.Injectable)(), + __metadata3("design:paramtypes", [prisma_service_1.PrismaService]) + ], SalesExpertAppService); + } +}); + +// dist/modules/sales-expert-app/sales-expert-app.controller.js +var require_sales_expert_app_controller = __commonJS({ + "dist/modules/sales-expert-app/sales-expert-app.controller.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + var __param3 = exports2 && exports2.__param || function(paramIndex, decorator) { + return function(target, key) { + decorator(target, key, paramIndex); + }; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.SalesExpertAppController = void 0; + var common_12 = require_common(); + var sales_expert_app_service_1 = require_sales_expert_app_service(); + var customer_guards_1 = require_customer_guards(); + var SalesExpertAppController = class SalesExpertAppController { + expert; + constructor(expert) { + this.expert = expert; + } + /** Legacy POST /order/child — POST because the reference posted its paging. */ + childOrders(body, me) { + return this.expert.childOrders(me.adminUserId, body ?? {}); + } + /** Legacy POST /my-child. */ + myCustomers(body, me) { + return this.expert.myCustomers(me.adminUserId, { + search: body?.search ?? body?.text_search ?? null + }); + } + me(me) { + return this.expert.me(me.adminUserId); + } + /** + * Whether the signed-in principal is a sales expert. The apps call this once + * after login to decide whether to show the two expert entries; the answer + * comes from the token, so a customer cannot make it say yes. + */ + whoami(me) { + return { isSalesExpert: Boolean(me.adminUserId), type: me.type }; + } + }; + exports2.SalesExpertAppController = SalesExpertAppController; + __decorate4([ + (0, customer_guards_1.CustomerAuth)(), + (0, common_12.Post)("child-orders"), + (0, common_12.HttpCode)(200), + __param3(0, (0, common_12.Body)()), + __param3(1, (0, customer_guards_1.CurrentCustomer)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [Object, Object]), + __metadata3("design:returntype", void 0) + ], SalesExpertAppController.prototype, "childOrders", null); + __decorate4([ + (0, customer_guards_1.CustomerAuth)(), + (0, common_12.Post)("my-customers"), + (0, common_12.HttpCode)(200), + __param3(0, (0, common_12.Body)()), + __param3(1, (0, customer_guards_1.CurrentCustomer)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [Object, Object]), + __metadata3("design:returntype", void 0) + ], SalesExpertAppController.prototype, "myCustomers", null); + __decorate4([ + (0, customer_guards_1.CustomerAuth)(), + (0, common_12.Get)("me"), + __param3(0, (0, customer_guards_1.CurrentCustomer)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [Object]), + __metadata3("design:returntype", void 0) + ], SalesExpertAppController.prototype, "me", null); + __decorate4([ + (0, customer_guards_1.CustomerAuth)(), + (0, common_12.Get)("whoami"), + __param3(0, (0, customer_guards_1.CurrentCustomer)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [Object]), + __metadata3("design:returntype", void 0) + ], SalesExpertAppController.prototype, "whoami", null); + exports2.SalesExpertAppController = SalesExpertAppController = __decorate4([ + (0, common_12.Controller)("expert"), + __metadata3("design:paramtypes", [sales_expert_app_service_1.SalesExpertAppService]) + ], SalesExpertAppController); + } +}); + +// dist/modules/sales-expert-app/sales-expert-app.module.js +var require_sales_expert_app_module = __commonJS({ + "dist/modules/sales-expert-app/sales-expert-app.module.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.SalesExpertAppModule = void 0; + var common_12 = require_common(); + var prisma_module_1 = require_prisma_module(); + var sales_expert_app_controller_1 = require_sales_expert_app_controller(); + var sales_expert_app_service_1 = require_sales_expert_app_service(); + var SalesExpertAppModule = class SalesExpertAppModule { + }; + exports2.SalesExpertAppModule = SalesExpertAppModule; + exports2.SalesExpertAppModule = SalesExpertAppModule = __decorate4([ + (0, common_12.Module)({ + imports: [prisma_module_1.PrismaModule], + controllers: [sales_expert_app_controller_1.SalesExpertAppController], + providers: [sales_expert_app_service_1.SalesExpertAppService], + exports: [sales_expert_app_service_1.SalesExpertAppService] + }) + ], SalesExpertAppModule); + } +}); + +// dist/modules/sms/sms.module.js +var require_sms_module = __commonJS({ + "dist/modules/sms/sms.module.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.SmsModule = void 0; + var common_12 = require_common(); + var sms_service_12 = require_sms_service(); + var SmsModule = class SmsModule { + }; + exports2.SmsModule = SmsModule; + exports2.SmsModule = SmsModule = __decorate4([ + (0, common_12.Global)(), + (0, common_12.Module)({ + providers: [sms_service_12.SmsService], + exports: [sms_service_12.SmsService] + }) + ], SmsModule); + } +}); + +// dist/modules/admin-uploads/upload-path.js +var require_upload_path = __commonJS({ + "dist/modules/admin-uploads/upload-path.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.getUploadDir = getUploadDir; + exports2.mimeForUpload = mimeForUpload; + exports2.resolveStoredUpload = resolveStoredUpload; + var node_fs_1 = require("node:fs"); + var node_path_12 = require("node:path"); + var env_12 = require_env(); + function getUploadDir() { + return (0, node_path_12.resolve)((0, env_12.envOr)("UPLOAD_DIR", (0, node_path_12.join)(process.cwd(), "storage", "uploads"))); + } + var UPLOAD_MIME = { + ".png": "image/png", + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".gif": "image/gif", + ".webp": "image/webp", + ".avif": "image/avif", + ".svg": "image/svg+xml", + ".pdf": "application/pdf" + }; + function mimeForUpload(filePath) { + const dot = filePath.lastIndexOf("."); + const ext = dot >= 0 ? filePath.slice(dot).toLowerCase() : ""; + return UPLOAD_MIME[ext]; + } + function resolveStoredUpload(rawName) { + const safe = (0, node_path_12.basename)(rawName); + if (!safe || safe === "." || safe === "..") + return null; + const dir = getUploadDir(); + const candidates = [(0, node_path_12.join)(dir, safe), (0, node_path_12.join)(dir, "uploads", safe)]; + for (const candidate of candidates) { + const resolved = (0, node_path_12.resolve)(candidate); + const rel = (0, node_path_12.relative)(dir, resolved); + if (!rel || rel.startsWith("..") || (0, node_path_12.isAbsolute)(rel)) + continue; + if ((0, node_fs_1.existsSync)(resolved) && (0, node_fs_1.statSync)(resolved).isFile()) + return resolved; + } + return null; + } + } +}); + +// dist/modules/admin-uploads/admin-uploads.service.js +var require_admin_uploads_service = __commonJS({ + "dist/modules/admin-uploads/admin-uploads.service.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + var AdminUploadsService_1; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.AdminUploadsService = void 0; + var common_12 = require_common(); + var node_crypto_1 = require("node:crypto"); + var promises_1 = require("node:fs/promises"); + var node_path_12 = require("node:path"); + var prisma_service_1 = require_prisma_service(); + var upload_path_1 = require_upload_path(); + var AdminUploadsService = class AdminUploadsService { + static { + AdminUploadsService_1 = this; + } + prisma; + /** Matches legacy's flat public/storage directory. */ + dir = (0, upload_path_1.getUploadDir)(); + /** Mirrors legacy's `type` field: 1 = image, 2 = any other document. */ + static IMAGE_MIME = /^image\/(jpeg|png|gif|webp|svg\+xml)$/; + static ALLOWED = /^(image\/(jpeg|png|gif|webp|svg\+xml)|application\/pdf)$/; + /** 8 MB. Legacy relied on PHP's upload_max_filesize; this makes it explicit. */ + static MAX_BYTES = 8 * 1024 * 1024; + constructor(prisma) { + this.prisma = prisma; + } + async store(file2, purpose, actorAdminUserId) { + if (!file2) + throw new common_12.BadRequestException("\u0641\u0627\u06CC\u0644\u06CC \u0627\u0631\u0633\u0627\u0644 \u0646\u0634\u062F\u0647 \u0627\u0633\u062A"); + if (file2.size > AdminUploadsService_1.MAX_BYTES) { + throw new common_12.BadRequestException("\u062D\u062C\u0645 \u0641\u0627\u06CC\u0644 \u0628\u06CC\u0634 \u0627\u0632 \u062D\u062F \u0645\u062C\u0627\u0632 \u0627\u0633\u062A (\u06F8 \u0645\u06AF\u0627\u0628\u0627\u06CC\u062A)"); + } + if (!AdminUploadsService_1.ALLOWED.test(file2.mimetype)) { + throw new common_12.BadRequestException("\u0646\u0648\u0639 \u0641\u0627\u06CC\u0644 \u0645\u062C\u0627\u0632 \u0646\u06CC\u0633\u062A"); + } + const ext = (0, node_path_12.extname)(file2.originalname).toLowerCase(); + const stored = (0, node_crypto_1.createHash)("md5").update(`${Date.now()}${file2.originalname}${(0, node_crypto_1.randomUUID)()}`).digest("hex") + ext; + await (0, promises_1.mkdir)(this.dir, { recursive: true }); + await (0, promises_1.writeFile)((0, node_path_12.join)(this.dir, stored), file2.buffer); + return this.prisma.uploadedFile.create({ + data: { + url: `/uploads/${stored}`, + originalName: file2.originalname, + mimeType: file2.mimetype, + sizeBytes: file2.size, + purpose: purpose ?? null, + uploadedByAdminUserId: actorAdminUserId + } + }); + } + async findOne(id) { + const f = await this.prisma.uploadedFile.findUnique({ where: { id } }); + if (!f) + throw new common_12.NotFoundException("\u0641\u0627\u06CC\u0644 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F"); + return f; + } + /** True when the stored file is an image, for callers that care. */ + static isImage(mime) { + return AdminUploadsService_1.IMAGE_MIME.test(mime); + } + }; + exports2.AdminUploadsService = AdminUploadsService; + exports2.AdminUploadsService = AdminUploadsService = AdminUploadsService_1 = __decorate4([ + (0, common_12.Injectable)(), + __metadata3("design:paramtypes", [prisma_service_1.PrismaService]) + ], AdminUploadsService); + } +}); + +// dist/modules/admin-uploads/admin-uploads.controller.js +var require_admin_uploads_controller = __commonJS({ + "dist/modules/admin-uploads/admin-uploads.controller.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + var __param3 = exports2 && exports2.__param || function(paramIndex, decorator) { + return function(target, key) { + decorator(target, key, paramIndex); + }; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.AdminUploadsController = void 0; + var common_12 = require_common(); + var platform_express_1 = require_platform_express(); + var admin_uploads_service_1 = require_admin_uploads_service(); + var guards_1 = require_guards2(); + var common_2 = require_common(); + var AdminUploadsController = class AdminUploadsController { + uploads; + constructor(uploads) { + this.uploads = uploads; + } + create(file2, purpose, admin) { + return this.uploads.store(file2, purpose, admin.id ?? null); + } + findOne(id) { + return this.uploads.findOne(id); + } + }; + exports2.AdminUploadsController = AdminUploadsController; + __decorate4([ + (0, common_12.Post)("create"), + (0, common_12.HttpCode)(200), + (0, common_12.UseInterceptors)((0, platform_express_1.FileInterceptor)("file")), + __param3(0, (0, common_12.UploadedFile)()), + __param3(1, (0, common_2.Body)("purpose")), + __param3(2, (0, guards_1.CurrentAdmin)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [Object, Object, Object]), + __metadata3("design:returntype", void 0) + ], AdminUploadsController.prototype, "create", null); + __decorate4([ + (0, common_12.Get)(":id"), + __param3(0, (0, common_12.Param)("id")), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [String]), + __metadata3("design:returntype", void 0) + ], AdminUploadsController.prototype, "findOne", null); + exports2.AdminUploadsController = AdminUploadsController = __decorate4([ + (0, common_12.Controller)("admin/files"), + __metadata3("design:paramtypes", [admin_uploads_service_1.AdminUploadsService]) + ], AdminUploadsController); + } +}); + +// dist/modules/admin-uploads/public-uploads.controller.js +var require_public_uploads_controller = __commonJS({ + "dist/modules/admin-uploads/public-uploads.controller.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + var __param3 = exports2 && exports2.__param || function(paramIndex, decorator) { + return function(target, key) { + decorator(target, key, paramIndex); + }; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.PublicUploadsController = void 0; + var common_12 = require_common(); + var node_fs_1 = require("node:fs"); + var guards_1 = require_guards2(); + var upload_path_1 = require_upload_path(); + var PublicUploadsController = class PublicUploadsController { + stream(filename) { + const file2 = (0, upload_path_1.resolveStoredUpload)(filename); + if (!file2) + throw new common_12.NotFoundException("\u0641\u0627\u06CC\u0644 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F"); + return new common_12.StreamableFile((0, node_fs_1.createReadStream)(file2), { + type: (0, upload_path_1.mimeForUpload)(file2) ?? "application/octet-stream", + disposition: "inline" + }); + } + }; + exports2.PublicUploadsController = PublicUploadsController; + __decorate4([ + (0, common_12.Get)(":filename"), + (0, common_12.Header)("Cache-Control", "public, max-age=86400"), + __param3(0, (0, common_12.Param)("filename")), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [String]), + __metadata3("design:returntype", common_12.StreamableFile) + ], PublicUploadsController.prototype, "stream", null); + exports2.PublicUploadsController = PublicUploadsController = __decorate4([ + (0, guards_1.Public)(), + (0, common_12.Controller)("uploads") + ], PublicUploadsController); + } +}); + +// dist/modules/admin-uploads/admin-uploads.module.js +var require_admin_uploads_module = __commonJS({ + "dist/modules/admin-uploads/admin-uploads.module.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.AdminUploadsModule = void 0; + var common_12 = require_common(); + var prisma_module_1 = require_prisma_module(); + var admin_uploads_controller_1 = require_admin_uploads_controller(); + var public_uploads_controller_1 = require_public_uploads_controller(); + var admin_uploads_service_1 = require_admin_uploads_service(); + var AdminUploadsModule = class AdminUploadsModule { + }; + exports2.AdminUploadsModule = AdminUploadsModule; + exports2.AdminUploadsModule = AdminUploadsModule = __decorate4([ + (0, common_12.Module)({ + imports: [prisma_module_1.PrismaModule], + controllers: [admin_uploads_controller_1.AdminUploadsController, public_uploads_controller_1.PublicUploadsController], + providers: [admin_uploads_service_1.AdminUploadsService], + exports: [admin_uploads_service_1.AdminUploadsService] + }) + ], AdminUploadsModule); + } +}); + +// dist/modules/customer-profile/customer-profile.service.js +var require_customer_profile_service = __commonJS({ + "dist/modules/customer-profile/customer-profile.service.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + var CustomerProfileService_1; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.CustomerProfileService = void 0; + var common_12 = require_common(); + var prisma_service_1 = require_prisma_service(); + var sms_service_12 = require_sms_service(); + var CustomerProfileService = CustomerProfileService_1 = class CustomerProfileService { + prisma; + sms; + logger = new common_12.Logger(CustomerProfileService_1.name); + constructor(prisma, sms) { + this.prisma = prisma; + this.sms = sms; + } + /** + * The admins who are told a customer uploaded their documents. + * + * The reference hardcoded four numbers; this reads a comma-separated env var + * instead. Unset means nobody is notified, and the customer's own SMS and the + * upload itself still go through — consistent with D20, where SMS never + * blocks an operation. + */ + adminMobiles() { + return (process.env.PROFILE_ADMIN_MOBILES ?? "").split(",").map((m) => m.trim()).filter(Boolean); + } + /** + * Resolves an uploaded-file id to its stored URL. + * + * The reference stored the raw id in the customer row and joined to `files` + * on read; the target's Customer columns hold URLs + * (`nationalIdImageUrl`, `businessLicenseUrl`, `shopImageUrl`), so the id is + * resolved here. An id that names no file is a 400 rather than a silent + * null, so a half-completed profile can never be marked verified. + */ + async urlOf(id, label) { + const file2 = await this.prisma.uploadedFile.findUnique({ where: { id } }); + if (!file2) + throw new common_12.BadRequestException(`${label} \u06CC\u0627\u0641\u062A \u0646\u0634\u062F`); + return file2.url; + } + async complete(customerId, dto) { + const customer = await this.prisma.customer.findUnique({ where: { id: customerId } }); + if (!customer) + throw new common_12.NotFoundException("\u0645\u0634\u062A\u0631\u06CC \u06CC\u0627\u0641\u062A \u0646\u0634\u062F"); + const [businessLicenseUrl, shopImageUrl, nationalIdImageUrl] = await Promise.all([ + this.urlOf(dto.business_license_image_id, "\u062A\u0635\u0648\u06CC\u0631 \u062C\u0648\u0627\u0632 \u06A9\u0633\u0628"), + this.urlOf(dto.shop_image_id, "\u062A\u0635\u0648\u06CC\u0631 \u0633\u0631\u062F\u0631 \u0645\u063A\u0627\u0632\u0647"), + this.urlOf(dto.ncode_image_id, "\u062A\u0635\u0648\u06CC\u0631 \u06A9\u0627\u0631\u062A \u0645\u0644\u06CC") + ]); + const updated = await this.prisma.customer.update({ + where: { id: customerId }, + data: { + businessLicenseUrl, + shopImageUrl, + nationalIdImageUrl, + // The reference's live value: 2, approved outright (:85). + verificationStep: "VERIFIED" + } + }); + this.notify(updated.mobile, updated.firstName ?? "", updated.lastName ?? ""); + return { ok: true }; + } + /** + * Both messages, fire-and-forget. + * + * D20: a failed or unconfigured SMS must never fail the upload, so nothing + * here is awaited and every rejection is swallowed into the log. The + * reference likewise queued them (SendSmsAfterCreate::dispatch, :100). + */ + notify(mobile, firstName, lastName) { + const message = `\u0647\u0646\u0632\u0627 \u06AF\u0644\u062F +\u0647\u0645\u06A9\u0627\u0631 \u06AF\u0631\u0627\u0645\u06CC ${firstName} ${lastName} +\u062A\u06A9\u0645\u06CC\u0644 \u067E\u0631\u0648\u0641\u0627\u06CC\u0644 \u0634\u0645\u0627 \u0628\u0627 \u0645\u0648\u0641\u0642\u06CC\u062A \u0627\u0646\u062C\u0627\u0645 \u0634\u062F.`; + const adminMessage = `\u0647\u0646\u0632\u0627 \u06AF\u0644\u062F +\u0645\u0634\u062A\u0631\u06CC \u0628\u0627 \u0646\u0627\u0645 ${firstName} ${lastName} \u0645\u062F\u0627\u0631\u06A9 \u062E\u0648\u062F \u0631\u0627 \u0628\u0627\u0631\u06AF\u0630\u0627\u0631\u06CC \u06A9\u0631\u062F.`; + const fire = (to, body) => { + void this.sms.send(to, body).catch((e) => { + this.logger.error(`Complete-profile SMS to ${to} failed: ${String(e)}`); + }); + }; + fire(mobile, message); + for (const admin of this.adminMobiles()) + fire(admin, adminMessage); + } + }; + exports2.CustomerProfileService = CustomerProfileService; + exports2.CustomerProfileService = CustomerProfileService = CustomerProfileService_1 = __decorate4([ + (0, common_12.Injectable)(), + __metadata3("design:paramtypes", [ + prisma_service_1.PrismaService, + sms_service_12.SmsService + ]) + ], CustomerProfileService); + } +}); + +// dist/modules/customer-profile/dto/complete-profile.dto.js +var require_complete_profile_dto = __commonJS({ + "dist/modules/customer-profile/dto/complete-profile.dto.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.CompleteProfileDto = void 0; + var class_validator_1 = require_cjs(); + var CompleteProfileDto = class { + business_license_image_id; + shop_image_id; + ncode_image_id; + }; + exports2.CompleteProfileDto = CompleteProfileDto; + __decorate4([ + (0, class_validator_1.IsString)(), + (0, class_validator_1.IsNotEmpty)({ message: "\u062A\u0635\u0648\u06CC\u0631 \u062C\u0648\u0627\u0632 \u06A9\u0633\u0628 \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A" }), + (0, class_validator_1.MaxLength)(64), + __metadata3("design:type", String) + ], CompleteProfileDto.prototype, "business_license_image_id", void 0); + __decorate4([ + (0, class_validator_1.IsString)(), + (0, class_validator_1.IsNotEmpty)({ message: "\u062A\u0635\u0648\u06CC\u0631 \u0633\u0631\u062F\u0631 \u0645\u063A\u0627\u0632\u0647 \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A" }), + (0, class_validator_1.MaxLength)(64), + __metadata3("design:type", String) + ], CompleteProfileDto.prototype, "shop_image_id", void 0); + __decorate4([ + (0, class_validator_1.IsString)(), + (0, class_validator_1.IsNotEmpty)({ message: "\u062A\u0635\u0648\u06CC\u0631 \u06A9\u0627\u0631\u062A \u0645\u0644\u06CC \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A" }), + (0, class_validator_1.MaxLength)(64), + __metadata3("design:type", String) + ], CompleteProfileDto.prototype, "ncode_image_id", void 0); + } +}); + +// dist/modules/customer-profile/customer-profile.controller.js +var require_customer_profile_controller = __commonJS({ + "dist/modules/customer-profile/customer-profile.controller.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + var __param3 = exports2 && exports2.__param || function(paramIndex, decorator) { + return function(target, key) { + decorator(target, key, paramIndex); + }; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.CustomerProfileController = void 0; + var common_12 = require_common(); + var common_2 = require_common(); + var platform_express_1 = require_platform_express(); + var customer_profile_service_1 = require_customer_profile_service(); + var admin_uploads_service_1 = require_admin_uploads_service(); + var complete_profile_dto_1 = require_complete_profile_dto(); + var customer_guards_1 = require_customer_guards(); + var CustomerProfileController = class CustomerProfileController { + profile; + uploads; + constructor(profile, uploads) { + this.profile = profile; + this.uploads = uploads; + } + /** + * A customer uploading one of their own documents. + * + * The reference let the app upload through the very same `file/create` the + * admin panel used, behind the customer guard (routes/api.php:95). The + * target's copy of that route sits behind an ADMIN guard + * (AdminUploadsController), which no app token can pass — so without this + * route the page could not upload at all. + * + * It reuses AdminUploadsService unchanged, so the size limit, the MIME + * whitelist and the stored-name scheme are identical on both paths. The + * uploader is recorded as null rather than as an admin, because + * `UploadedFile.uploadedByAdminUserId` is an AdminUser FK and this caller is + * a customer; `purpose` carries the document kind instead. + */ + upload(file2, purpose, _me) { + return this.uploads.store(file2, purpose ?? "customer-document", null); + } + /** Ports POST major/complete. The customer is the token's. */ + complete(dto, me) { + return this.profile.complete(me.id, dto); + } + }; + exports2.CustomerProfileController = CustomerProfileController; + __decorate4([ + (0, customer_guards_1.CustomerAuth)(), + (0, common_12.Post)("documents"), + (0, common_12.HttpCode)(200), + (0, common_12.UseInterceptors)((0, platform_express_1.FileInterceptor)("file")), + __param3(0, (0, common_12.UploadedFile)()), + __param3(1, (0, common_2.Body)("purpose")), + __param3(2, (0, customer_guards_1.CurrentCustomer)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [Object, Object, Object]), + __metadata3("design:returntype", void 0) + ], CustomerProfileController.prototype, "upload", null); + __decorate4([ + (0, customer_guards_1.CustomerAuth)(), + (0, common_12.Post)("complete-profile"), + (0, common_12.HttpCode)(200), + __param3(0, (0, common_2.Body)()), + __param3(1, (0, customer_guards_1.CurrentCustomer)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [complete_profile_dto_1.CompleteProfileDto, Object]), + __metadata3("design:returntype", void 0) + ], CustomerProfileController.prototype, "complete", null); + exports2.CustomerProfileController = CustomerProfileController = __decorate4([ + (0, common_12.Controller)("customer/me"), + __metadata3("design:paramtypes", [ + customer_profile_service_1.CustomerProfileService, + admin_uploads_service_1.AdminUploadsService + ]) + ], CustomerProfileController); + } +}); + +// dist/modules/customer-profile/customer-profile.module.js +var require_customer_profile_module = __commonJS({ + "dist/modules/customer-profile/customer-profile.module.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.CustomerProfileModule = void 0; + var common_12 = require_common(); + var prisma_module_1 = require_prisma_module(); + var sms_module_1 = require_sms_module(); + var admin_uploads_module_1 = require_admin_uploads_module(); + var customer_profile_controller_1 = require_customer_profile_controller(); + var customer_profile_service_1 = require_customer_profile_service(); + var CustomerProfileModule = class CustomerProfileModule { + }; + exports2.CustomerProfileModule = CustomerProfileModule; + exports2.CustomerProfileModule = CustomerProfileModule = __decorate4([ + (0, common_12.Module)({ + imports: [prisma_module_1.PrismaModule, sms_module_1.SmsModule, admin_uploads_module_1.AdminUploadsModule], + controllers: [customer_profile_controller_1.CustomerProfileController], + providers: [customer_profile_service_1.CustomerProfileService] + }) + ], CustomerProfileModule); + } +}); + +// dist/modules/customer-report/customer-report.service.js +var require_customer_report_service = __commonJS({ + "dist/modules/customer-report/customer-report.service.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + var CustomerReportService_1; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.CustomerReportService = void 0; + var common_12 = require_common(); + var client_1 = require("@prisma/client"); + var prisma_service_1 = require_prisma_service(); + var CustomerReportService = class CustomerReportService { + static { + CustomerReportService_1 = this; + } + prisma; + constructor(prisma) { + this.prisma = prisma; + } + /** The reference's page size (report/index.vue:96). */ + static LIMIT = 50; + /** + * "کلیات حساب" — one row per ledger entry, oldest first, each carrying the + * running balance AFTER it. + * + * The running balance is why this cannot be a plain paginated query: a row's + * `tahesab_T` depends on every entry before it. Entries are therefore folded + * in chronological order from the beginning of the account, and the requested + * page is sliced out afterwards. At the row counts this app deals with + * (one customer's own documents) that is cheap; if a customer ever + * accumulates enough entries for it to matter, the fold should move into a + * stored running total. Noted in ISSUES.md as REP-02. + * + * The reference's own list arrives oldest-first and is then `.reverse()`d for + * display (report/index.vue:191), so the app shows newest first. Same here. + */ + async summary(customerId, page = 1) { + const entries = await this.prisma.ledgerEntry.findMany({ + // Only confirmed entries move a balance — the same rule the cached + // Customer.goldDebtGrams / fiatDebtRial follow, so the statement's final + // balance agrees with the figure shown everywhere else in the app. + where: { customerId, confirmed: true }, + orderBy: [{ createdAt: "asc" }, { id: "asc" }], + include: { order: { select: { orderNumber: true } } } + }); + let goldRunning = new client_1.Prisma.Decimal(0); + let rialRunning = new client_1.Prisma.Decimal(0); + const rows = entries.map((e, index) => { + const amount = new client_1.Prisma.Decimal(e.amount); + const isGold = e.currency === "GOLD_GRAM"; + const signed = e.direction === "DEBIT" ? amount : amount.negated(); + if (isGold) + goldRunning = goldRunning.plus(signed); + else + rialRunning = rialRunning.plus(signed); + return { + id: index + 1, + // The reference keyed the detail lookup on factor_Code; here it is the + // entry's own id, so the detail call needs nothing else from the client. + factor_Code: e.id, + sh_factor: e.order?.orderNumber ?? e.id.slice(-8), + tarikh: e.createdAt.toISOString(), + // Gold in / out, as separate positive columns — the reference showed + // them on two lines (Accounting.vue:21-22). + geram_v: isGold && e.direction === "CREDIT" ? amount.toFixed(3) : "0.000", + geram_kh: isGold && e.direction === "DEBIT" ? amount.toFixed(3) : "0.000", + rial_v: !isGold && e.direction === "CREDIT" ? amount.toFixed(2) : "0.00", + rial_kh: !isGold && e.direction === "DEBIT" ? amount.toFixed(2) : "0.00", + // Running balances. The reference colours these by sign + // (Accounting.vue:29, :46), so the sign must be preserved. + tahesab_T: goldRunning.toFixed(3), + tahesab_P: rialRunning.toFixed(2), + // Present so the UI binds, but the target has no foreign-currency + // ledger — always zero rather than invented. REP-01. + rial_V2: "0.00", + rial_Kh2: "0.00", + tahesabP2: "0.00", + sourceType: e.sourceType, + note: e.note + }; + }); + rows.reverse(); + const start = (Math.max(1, page) - 1) * CustomerReportService_1.LIMIT; + return { + result: rows.slice(start, start + CustomerReportService_1.LIMIT), + count: rows.length, + page: Math.max(1, page), + limit: CustomerReportService_1.LIMIT + }; + } + /** + * "جزئیات حساب" — the per-document detail. + * + * With no id, this is the whole list the second tab renders (the reference's + * `my_detail`); with one, it is the single document behind the «جزئیات» link + * (its `GetHesabDetailByFactorCode`). + * + * Scoped on customerId in the WHERE clause, so an id belonging to someone + * else simply returns nothing — ids stay unenumerable, and no id is trusted + * from the client beyond being a filter. + */ + async detail(customerId, entryId, page = 1) { + const where = { + customerId, + confirmed: true, + ...entryId ? { id: entryId } : {} + }; + const entries = await this.prisma.ledgerEntry.findMany({ + where, + orderBy: [{ createdAt: "desc" }, { id: "desc" }], + ...entryId ? {} : { + skip: (Math.max(1, page) - 1) * CustomerReportService_1.LIMIT, + take: CustomerReportService_1.LIMIT + }, + include: { + order: { + select: { + orderNumber: true, + totalGoldWeightGrams: true, + wageWeightGrams: true + } + } + } + }); + const result = entries.map((e) => { + const amount = new client_1.Prisma.Decimal(e.amount); + const isGold = e.currency === "GOLD_GRAM"; + return { + factor_Code: e.id, + sh_factor: e.order?.orderNumber ?? e.id.slice(-8), + tarikh: e.createdAt.toISOString(), + /// Net weight. For a melted-gold repayment the ledger keeps the weight + /// as handed over, BEFORE karat normalization (schema.prisma:685), which + /// is what a paper document states; `amount` is the normalized figure. + vazn: isGold ? (e.originalValue ?? amount).toFixed(3) : "0.000", + /// Purity, for the documents that carry one. + ayar: e.karatPerMille ?? null, + no: SOURCE_LABELS[e.sourceType] ?? e.sourceType, + /// Hallmark and lab — the target ledger carries both, from the same + /// legacy `ang` / `lab` columns the reference's document form had. + sh_sharti: e.hallmark, + name_az: e.labName, + /// Gold in / out for this document, normalized to 750 as the reference's + /// tabdil750 columns were. + tabdil750_v: isGold && e.direction === "CREDIT" ? amount.toFixed(3) : "0.000", + tabdil750_kh: isGold && e.direction === "DEBIT" ? amount.toFixed(3) : "0.000", + /// Unit price at the time, for the documents that have one. + fi: e.pricePerGramRialAtEntry ? e.pricePerGramRialAtEntry.toFixed(2) : null, + fiF: e.priceRial ? e.priceRial.toFixed(2) : null, + /// The making charge the order carried, where this entry came from one. + Darsad_Talayee: e.order?.wageWeightGrams ? e.order.wageWeightGrams.toFixed(3) : null, + accountName: e.accountName, + bankName: e.bankName, + transferTo: e.transferTo, + receivedAt: e.receivedAt ? e.receivedAt.toISOString() : null, + note: e.note, + /// No equivalent in the target — shown as null, never invented. REP-01. + nO_kar: null, + jens_felez: null, + Darsad: null, + maliatDarsad: null + }; + }); + return { result, page: Math.max(1, page), limit: CustomerReportService_1.LIMIT }; + } + /** + * "مانده" — the current balances. + * + * Ports the shape of TahesabController::mandeh, which answered a `result` + * array of `{arz, mandeh}`. The reference multiplied the external system's + * figures by -1 to flip its sign convention; here the cached customer + * balances are already in the app's own convention (positive = owes), so no + * flip is applied and the figure matches every other screen. + */ + async balances(customerId) { + const c = await this.prisma.customer.findUnique({ + where: { id: customerId }, + select: { goldDebtGrams: true, fiatDebtRial: true } + }); + return { + status: 200, + message: "", + result: [ + { arz: "IRR", mandeh: (c?.fiatDebtRial ?? new client_1.Prisma.Decimal(0)).toFixed(2) }, + { arz: "gold", mandeh: (c?.goldDebtGrams ?? new client_1.Prisma.Decimal(0)).toFixed(3) } + ] + }; + } + }; + exports2.CustomerReportService = CustomerReportService; + exports2.CustomerReportService = CustomerReportService = CustomerReportService_1 = __decorate4([ + (0, common_12.Injectable)(), + __metadata3("design:paramtypes", [prisma_service_1.PrismaService]) + ], CustomerReportService); + var SOURCE_LABELS = { + ORDER_STOCK: "\u0633\u0641\u0627\u0631\u0634", + ORDER_PRE_ORDER: "\u067E\u06CC\u0634\u200C\u0633\u0641\u0627\u0631\u0634", + REPAYMENT_MELTED_GOLD: "\u0637\u0644\u0627\u06CC \u0622\u0628\u0634\u062F\u0647", + REPAYMENT_TRANSFER: "\u062D\u0648\u0627\u0644\u0647", + REPAYMENT_CHEQUE: "\u0686\u06A9", + REPAYMENT_CASH: "\u0646\u0642\u062F\u06CC", + WALLET_BONUS: "\u0647\u062F\u06CC\u0647", + ADJUSTMENT: "\u0627\u0635\u0644\u0627\u062D\u06CC\u0647" + }; + } +}); + +// dist/modules/customer-report/customer-report.controller.js +var require_customer_report_controller = __commonJS({ + "dist/modules/customer-report/customer-report.controller.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + var __param3 = exports2 && exports2.__param || function(paramIndex, decorator) { + return function(target, key) { + decorator(target, key, paramIndex); + }; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.CustomerReportController = void 0; + var common_12 = require_common(); + var customer_report_service_1 = require_customer_report_service(); + var customer_guards_1 = require_customer_guards(); + var CustomerReportController = class CustomerReportController { + report; + constructor(report) { + this.report = report; + } + /** "کلیات حساب" — the running statement. */ + summary(me, page) { + return this.report.summary(me.id, Number(page) || 1); + } + /** "جزئیات حساب" — the whole detail list (the second tab). */ + detailList(me, page) { + return this.report.detail(me.id, void 0, Number(page) || 1); + } + /** The current balances, for the statement header. */ + balances(me) { + return this.report.balances(me.id); + } + /** + * One document. Declared last so the literal routes above are matched first — + * Nest resolves in declaration order. + */ + detail(id, me) { + return this.report.detail(me.id, id); + } + }; + exports2.CustomerReportController = CustomerReportController; + __decorate4([ + (0, customer_guards_1.CustomerAuth)(), + (0, common_12.Get)(), + __param3(0, (0, customer_guards_1.CurrentCustomer)()), + __param3(1, (0, common_12.Query)("page")), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [Object, String]), + __metadata3("design:returntype", void 0) + ], CustomerReportController.prototype, "summary", null); + __decorate4([ + (0, customer_guards_1.CustomerAuth)(), + (0, common_12.Get)("detail"), + __param3(0, (0, customer_guards_1.CurrentCustomer)()), + __param3(1, (0, common_12.Query)("page")), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [Object, String]), + __metadata3("design:returntype", void 0) + ], CustomerReportController.prototype, "detailList", null); + __decorate4([ + (0, customer_guards_1.CustomerAuth)(), + (0, common_12.Get)("balances"), + __param3(0, (0, customer_guards_1.CurrentCustomer)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [Object]), + __metadata3("design:returntype", void 0) + ], CustomerReportController.prototype, "balances", null); + __decorate4([ + (0, customer_guards_1.CustomerAuth)(), + (0, common_12.Get)(":id"), + __param3(0, (0, common_12.Param)("id")), + __param3(1, (0, customer_guards_1.CurrentCustomer)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [String, Object]), + __metadata3("design:returntype", void 0) + ], CustomerReportController.prototype, "detail", null); + exports2.CustomerReportController = CustomerReportController = __decorate4([ + (0, common_12.Controller)("customer/me/report"), + __metadata3("design:paramtypes", [customer_report_service_1.CustomerReportService]) + ], CustomerReportController); + } +}); + +// dist/modules/customer-report/customer-report.module.js +var require_customer_report_module = __commonJS({ + "dist/modules/customer-report/customer-report.module.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.CustomerReportModule = void 0; + var common_12 = require_common(); + var prisma_module_1 = require_prisma_module(); + var customer_report_controller_1 = require_customer_report_controller(); + var customer_report_service_1 = require_customer_report_service(); + var CustomerReportModule = class CustomerReportModule { + }; + exports2.CustomerReportModule = CustomerReportModule; + exports2.CustomerReportModule = CustomerReportModule = __decorate4([ + (0, common_12.Module)({ + imports: [prisma_module_1.PrismaModule], + controllers: [customer_report_controller_1.CustomerReportController], + providers: [customer_report_service_1.CustomerReportService] + }) + ], CustomerReportModule); + } +}); + +// ../../packages/shared-types/dist/index.js +var require_dist3 = __commonJS({ + "../../packages/shared-types/dist/index.js"(exports2) { + "use strict"; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.PreOrderStatus = exports2.OrderStatus = exports2.LedgerSourceType = exports2.LedgerDirection = exports2.LedgerCurrency = exports2.GRAMS_PER_MESGHAL = exports2.STANDARD_KARAT_PER_MILLE = void 0; + exports2.STANDARD_KARAT_PER_MILLE = 750; + exports2.GRAMS_PER_MESGHAL = 4.331802; + var LedgerCurrency; + (function(LedgerCurrency2) { + LedgerCurrency2["GOLD_GRAM"] = "GOLD_GRAM"; + LedgerCurrency2["RIAL"] = "RIAL"; + })(LedgerCurrency || (exports2.LedgerCurrency = LedgerCurrency = {})); + var LedgerDirection; + (function(LedgerDirection2) { + LedgerDirection2["DEBIT"] = "DEBIT"; + LedgerDirection2["CREDIT"] = "CREDIT"; + })(LedgerDirection || (exports2.LedgerDirection = LedgerDirection = {})); + var LedgerSourceType; + (function(LedgerSourceType2) { + LedgerSourceType2["ORDER_STOCK"] = "ORDER_STOCK"; + LedgerSourceType2["ORDER_PRE_ORDER"] = "ORDER_PRE_ORDER"; + LedgerSourceType2["REPAYMENT_MELTED_GOLD"] = "REPAYMENT_MELTED_GOLD"; + LedgerSourceType2["REPAYMENT_TRANSFER"] = "REPAYMENT_TRANSFER"; + LedgerSourceType2["REPAYMENT_CHEQUE"] = "REPAYMENT_CHEQUE"; + LedgerSourceType2["REPAYMENT_CASH"] = "REPAYMENT_CASH"; + LedgerSourceType2["WALLET_BONUS"] = "WALLET_BONUS"; + LedgerSourceType2["ADJUSTMENT"] = "ADJUSTMENT"; + })(LedgerSourceType || (exports2.LedgerSourceType = LedgerSourceType = {})); + var OrderStatus; + (function(OrderStatus2) { + OrderStatus2["SUSPENDED"] = "SUSPENDED"; + OrderStatus2["PLACED"] = "PLACED"; + OrderStatus2["ADMIN_APPROVED"] = "ADMIN_APPROVED"; + OrderStatus2["SOURCING"] = "SOURCING"; + OrderStatus2["PREPARING"] = "PREPARING"; + OrderStatus2["DELIVERED_TO_AGENT"] = "DELIVERED_TO_AGENT"; + OrderStatus2["DELIVERED_TO_CUSTOMER"] = "DELIVERED_TO_CUSTOMER"; + OrderStatus2["CANCELLED"] = "CANCELLED"; + })(OrderStatus || (exports2.OrderStatus = OrderStatus = {})); + var PreOrderStatus; + (function(PreOrderStatus2) { + PreOrderStatus2["PLACED"] = "PLACED"; + PreOrderStatus2["ADMIN_APPROVED"] = "ADMIN_APPROVED"; + PreOrderStatus2["WORKSHOP_APPROVED"] = "WORKSHOP_APPROVED"; + PreOrderStatus2["READY_TO_SHIP"] = "READY_TO_SHIP"; + PreOrderStatus2["DELIVERED"] = "DELIVERED"; + PreOrderStatus2["CANCELLED"] = "CANCELLED"; + })(PreOrderStatus || (exports2.PreOrderStatus = PreOrderStatus = {})); + } +}); + +// dist/modules/accounting/ledger.service.js +var require_ledger_service = __commonJS({ + "dist/modules/accounting/ledger.service.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.LedgerService = void 0; + var common_12 = require_common(); + var client_1 = require("@prisma/client"); + var shared_types_1 = require_dist3(); + var prisma_service_1 = require_prisma_service(); + var LedgerService = class LedgerService { + prisma; + constructor(prisma) { + this.prisma = prisma; + } + /** Posts a ledger entry and, if confirmed, applies it to the cached customer balance. */ + async postEntry(input, tx = this.prisma) { + const amount = new client_1.Prisma.Decimal(input.amount); + if (amount.lte(0)) { + throw new common_12.BadRequestException("Ledger entry amount must be positive"); + } + const confirmed = input.confirmed ?? false; + const entry = await tx.ledgerEntry.create({ + data: { + customerId: input.customerId, + currency: input.currency, + direction: input.direction, + sourceType: input.sourceType, + amount, + orderId: input.orderId, + karatPerMille: input.karatPerMille, + pricePerGramRialAtEntry: input.pricePerGramRialAtEntry !== void 0 ? new client_1.Prisma.Decimal(input.pricePerGramRialAtEntry) : void 0, + note: input.note, + confirmed, + confirmedAt: confirmed ? /* @__PURE__ */ new Date() : null, + postedByAdminUserId: input.postedByAdminUserId + } + }); + if (confirmed) { + await this.applyToBalance(input.customerId, input.currency, input.direction, amount, tx); + } + return entry; + } + /** + * Marks a pending repayment entry confirmed. Legacy's confirmed toggle + * (Adm\CashController::toggleConfiremCash) is bidirectional and used for + * post-hoc correction/rejection — legacy Cash rows are auto-confirmed + * (confirmed=1) at creation, not created pending. This system intentionally + * requires an explicit confirmation step before a repayment counts (see + * postGoldRepayment for the full rationale) — that is a deliberate design + * change from legacy, not a faithful mirror of it. + */ + async confirmEntry(entryId, adminUserId, tx = this.prisma) { + const entry = await tx.ledgerEntry.findUnique({ where: { id: entryId } }); + if (!entry) + throw new common_12.NotFoundException("Ledger entry not found"); + if (entry.confirmed) + return entry; + const updated = await tx.ledgerEntry.update({ + where: { id: entryId }, + data: { confirmed: true, confirmedAt: /* @__PURE__ */ new Date(), postedByAdminUserId: adminUserId ?? entry.postedByAdminUserId } + }); + await this.applyToBalance(entry.customerId, entry.currency, entry.direction, entry.amount, tx); + return updated; + } + async applyToBalance(customerId, currency, direction, amount, tx) { + const signedDelta = direction === client_1.LedgerDirection.DEBIT ? amount : amount.neg(); + await tx.customer.update({ + where: { id: customerId }, + data: currency === client_1.LedgerCurrency.GOLD_GRAM ? { goldDebtGrams: { increment: signedDelta } } : { fiatDebtRial: { increment: signedDelta } } + }); + } + /** + * Posts the dual-currency debt for a stock order once it reaches + * DELIVERED_TO_AGENT: GOLD_GRAM debt = order.totalGoldWeightGrams, RIAL debt + * = order.flatRialCharge. Both entries are self-confirming — the status + * transition itself is the confirming event (mirrors legacy sts>4, which + * first becomes true at sts=5 "تحویل مسئول فروش" / delivered to agent, not + * sts=6 delivered to customer). + */ + async postOrderDebt(orderId, tx = this.prisma) { + const order = await tx.order.findUnique({ where: { id: orderId } }); + if (!order) + throw new common_12.NotFoundException("Order not found"); + await this.postEntry({ + customerId: order.customerId, + currency: client_1.LedgerCurrency.GOLD_GRAM, + direction: client_1.LedgerDirection.DEBIT, + sourceType: client_1.LedgerSourceType.ORDER_STOCK, + amount: order.totalGoldWeightGrams, + orderId: order.id, + confirmed: true, + note: "Gold-weight debt on delivery" + }, tx); + if (new client_1.Prisma.Decimal(order.flatRialCharge).gt(0)) { + await this.postEntry({ + customerId: order.customerId, + currency: client_1.LedgerCurrency.RIAL, + direction: client_1.LedgerDirection.DEBIT, + sourceType: client_1.LedgerSourceType.ORDER_STOCK, + amount: order.flatRialCharge, + orderId: order.id, + confirmed: true, + note: "Flat Rial charge (e.g. stones) on delivery" + }, tx); + } + } + /** + * Normalizes a physical repayment instrument into gold grams and posts a + * pending (unconfirmed) CREDIT entry against the customer's gold debt. + * Formulas preserved verbatim from legacy CashController::store. Note: + * legacy auto-confirms (confirmed=1) every Cash row at creation — posting + * this as pending instead is an intentional design change, not a mirror of + * legacy's default. + */ + async postGoldRepayment(params, tx = this.prisma) { + const raw = new client_1.Prisma.Decimal(params.rawValue); + let goldGramsValue; + let sourceType; + switch (params.instrument) { + case "MELTED_GOLD": { + if (!params.karatPerMille) { + throw new common_12.BadRequestException("karatPerMille is required for a melted-gold repayment"); + } + goldGramsValue = raw.mul(params.karatPerMille).div(shared_types_1.STANDARD_KARAT_PER_MILLE); + sourceType = client_1.LedgerSourceType.REPAYMENT_MELTED_GOLD; + break; + } + case "CASH": { + if (!params.pricePerGramRial) { + throw new common_12.BadRequestException("pricePerGramRial is required for a cash repayment"); + } + const pricePerGram = new client_1.Prisma.Decimal(params.pricePerGramRial); + goldGramsValue = raw.div(pricePerGram.div(shared_types_1.GRAMS_PER_MESGHAL)); + sourceType = client_1.LedgerSourceType.REPAYMENT_CASH; + break; + } + case "TRANSFER": + goldGramsValue = raw; + sourceType = client_1.LedgerSourceType.REPAYMENT_TRANSFER; + break; + case "CHEQUE": + goldGramsValue = raw; + sourceType = client_1.LedgerSourceType.REPAYMENT_CHEQUE; + break; + } + return this.postEntry({ + customerId: params.customerId, + currency: client_1.LedgerCurrency.GOLD_GRAM, + direction: client_1.LedgerDirection.CREDIT, + sourceType, + amount: goldGramsValue, + karatPerMille: params.karatPerMille, + pricePerGramRialAtEntry: params.pricePerGramRial, + note: params.note, + confirmed: false, + // pending until an admin confirms — intentional deviation from legacy, which auto-confirms on creation + postedByAdminUserId: params.postedByAdminUserId + }, tx); + } + /** + * Direct Rial-debt repayment. Not present in the legacy system (its + * rial_remain had no repayment offset at all — a gap flagged during + * migration analysis) but supported here since the new ledger is symmetric. + */ + async postRialRepayment(params, tx = this.prisma) { + return this.postEntry({ + customerId: params.customerId, + currency: client_1.LedgerCurrency.RIAL, + direction: client_1.LedgerDirection.CREDIT, + sourceType: client_1.LedgerSourceType.REPAYMENT_CASH, + amount: params.amount, + note: params.note, + confirmed: false, + postedByAdminUserId: params.postedByAdminUserId + }, tx); + } + async postAdjustment(params, tx = this.prisma) { + return this.postEntry({ + customerId: params.customerId, + currency: params.currency, + direction: params.direction, + sourceType: client_1.LedgerSourceType.ADJUSTMENT, + amount: params.amount, + note: params.note, + confirmed: true, + postedByAdminUserId: params.postedByAdminUserId + }, tx); + } + async getBalance(customerId) { + const customer = await this.prisma.customer.findUnique({ + where: { id: customerId }, + select: { id: true, goldDebtGrams: true, fiatDebtRial: true, goldCreditLimitGrams: true } + }); + if (!customer) + throw new common_12.NotFoundException("Customer not found"); + return { + customerId: customer.id, + goldDebtGrams: customer.goldDebtGrams, + fiatDebtRial: customer.fiatDebtRial, + goldCreditLimitGrams: customer.goldCreditLimitGrams + }; + } + /** + * Recomputes cached balances from confirmed LedgerEntry rows and overwrites + * the cache — a self-healing reconciliation, run periodically or on demand, + * unlike legacy's recompute-on-every-write (Customer::updateRemain). + */ + async reconcileBalance(customerId) { + const [goldDebits, goldCredits, rialDebits, rialCredits] = await Promise.all([ + this.sumConfirmed(customerId, client_1.LedgerCurrency.GOLD_GRAM, client_1.LedgerDirection.DEBIT), + this.sumConfirmed(customerId, client_1.LedgerCurrency.GOLD_GRAM, client_1.LedgerDirection.CREDIT), + this.sumConfirmed(customerId, client_1.LedgerCurrency.RIAL, client_1.LedgerDirection.DEBIT), + this.sumConfirmed(customerId, client_1.LedgerCurrency.RIAL, client_1.LedgerDirection.CREDIT) + ]); + return this.prisma.customer.update({ + where: { id: customerId }, + data: { + goldDebtGrams: goldDebits.sub(goldCredits), + fiatDebtRial: rialDebits.sub(rialCredits) + } + }); + } + async sumConfirmed(customerId, currency, direction) { + const result = await this.prisma.ledgerEntry.aggregate({ + where: { customerId, currency, direction, confirmed: true }, + _sum: { amount: true } + }); + return result._sum.amount ?? new client_1.Prisma.Decimal(0); + } + }; + exports2.LedgerService = LedgerService; + exports2.LedgerService = LedgerService = __decorate4([ + (0, common_12.Injectable)(), + __metadata3("design:paramtypes", [prisma_service_1.PrismaService]) + ], LedgerService); + } +}); + +// dist/modules/orders/orders.service.js +var require_orders_service = __commonJS({ + "dist/modules/orders/orders.service.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + var OrdersService_1; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.OrdersService = void 0; + var common_12 = require_common(); + var client_1 = require("@prisma/client"); + var prisma_service_1 = require_prisma_service(); + var catalog_service_1 = require_catalog_service(); + var ledger_service_1 = require_ledger_service(); + var OrdersService = OrdersService_1 = class OrdersService { + prisma; + catalog; + ledger; + logger = new common_12.Logger(OrdersService_1.name); + constructor(prisma, catalog, ledger) { + this.prisma = prisma; + this.catalog = catalog; + this.ledger = ledger; + } + /** + * Places a stock order. Per line item: + * w = rashWeightGrams * (1 + (wagePercent - discountPercent) / 100) + * mirrors legacy ApiMobileController::setOrder. Debt is NOT posted here — + * only once the order reaches DELIVERED_TO_AGENT (see updateStatus). + */ + /** + * @param customerId taken from the caller's token by the controller, NEVER + * from the request body. The body's `customerId` is ignored; see the note + * on CreateOrderDto. + */ + async createOrder(dto, customerId) { + const customer = await this.prisma.customer.findUnique({ where: { id: customerId } }); + if (!customer) + throw new common_12.NotFoundException(`Customer ${customerId} not found`); + if (dto.customerId && dto.customerId !== customerId) { + this.logger.warn(`Order body carried customerId=${dto.customerId} but the token is ${customerId}; the token wins.`); + } + const computedItems = await Promise.all(dto.items.map(async (item) => { + const variant = await this.catalog.getVariantOrThrow(item.productVariantId); + const rashWeight = new client_1.Prisma.Decimal(variant.rashWeightGrams); + const wagePercent = new client_1.Prisma.Decimal(item.wagePercent); + const discountPercent = new client_1.Prisma.Decimal(item.discountPercent ?? 0); + const netPercent = wagePercent.sub(discountPercent); + const computedWeightGrams = rashWeight.mul(netPercent.div(100).add(1)); + return { + productVariantId: variant.id, + quantity: item.quantity, + rashWeightGramsSnapshot: rashWeight, + wagePercentSnapshot: wagePercent, + discountPercentSnapshot: discountPercent, + computedWeightGrams, + flatRialCharge: new client_1.Prisma.Decimal(item.flatRialCharge ?? 0) + }; + })); + const totalGoldWeightGrams = computedItems.reduce((sum, item) => sum.add(item.computedWeightGrams.mul(item.quantity)), new client_1.Prisma.Decimal(0)); + const totalRashWeightGrams = computedItems.reduce((sum, item) => sum.add(item.rashWeightGramsSnapshot.mul(item.quantity)), new client_1.Prisma.Decimal(0)); + const wageWeightGrams = totalGoldWeightGrams.sub(totalRashWeightGrams); + const flatRialCharge = computedItems.reduce((sum, item) => sum.add(item.flatRialCharge.mul(item.quantity)), new client_1.Prisma.Decimal(0)); + const isSuspendedOverCreditLimit = totalGoldWeightGrams.add(customer.goldDebtGrams).gt(customer.goldCreditLimitGrams); + const initialStatus = isSuspendedOverCreditLimit ? client_1.OrderStatus.SUSPENDED : client_1.OrderStatus.PLACED; + return this.prisma.$transaction(async (tx) => { + const order = await tx.order.create({ + data: { + orderNumber: this.generateOrderNumber(), + customerId, + branchId: dto.branchId, + channel: dto.channel ?? client_1.OrderChannel.RETAIL, + status: initialStatus, + totalGoldWeightGrams, + wageWeightGrams, + flatRialCharge, + isSuspendedOverCreditLimit, + items: { + create: computedItems.map((item) => ({ + productVariantId: item.productVariantId, + quantity: item.quantity, + rashWeightGramsSnapshot: item.rashWeightGramsSnapshot, + wagePercentSnapshot: item.wagePercentSnapshot, + discountPercentSnapshot: item.discountPercentSnapshot, + computedWeightGrams: item.computedWeightGrams, + flatRialCharge: item.flatRialCharge + })) + }, + statusEvents: { + create: { toStatus: initialStatus, note: isSuspendedOverCreditLimit ? "Credit limit exceeded" : void 0 } + } + }, + include: { items: true, statusEvents: true } + }); + return order; + }); + } + async updateStatus(orderId, newStatus, actorAdminUserId, note) { + return this.prisma.$transaction(async (tx) => { + const order = await tx.order.findUnique({ where: { id: orderId } }); + if (!order) + throw new common_12.NotFoundException(`Order ${orderId} not found`); + const updated = await tx.order.update({ + where: { id: orderId }, + data: { + status: newStatus, + deliveredAt: newStatus === client_1.OrderStatus.DELIVERED_TO_CUSTOMER ? /* @__PURE__ */ new Date() : order.deliveredAt + } + }); + await tx.orderStatusEvent.create({ + data: { orderId, fromStatus: order.status, toStatus: newStatus, note, actorAdminUserId } + }); + if (newStatus === client_1.OrderStatus.DELIVERED_TO_AGENT && !order.deliveredAt) { + await this.ledger.postOrderDebt(orderId, tx); + } + return updated; + }); + } + /** Unscoped read. Only reachable from the admin-guarded route. */ + getById(orderId) { + return this.prisma.order.findUnique({ + where: { id: orderId }, + include: { items: true, statusEvents: true, ledgerEntries: true } + }); + } + /** + * A customer reading one of their own orders. + * + * Someone else's order is reported as 404, not 403: a 403 would confirm the + * id exists, which is the same enumeration leak the id-addressed customer + * routes had. + */ + async getOwnedById(orderId, customerId) { + const order = await this.prisma.order.findFirst({ + where: { id: orderId, customerId }, + include: { items: true, statusEvents: true, ledgerEntries: true } + }); + if (!order) + throw new common_12.NotFoundException("\u0633\u0641\u0627\u0631\u0634 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F"); + return order; + } + generateOrderNumber() { + const random = Math.random().toString(36).slice(2, 8).toUpperCase(); + return `ORD-${Date.now()}-${random}`; + } + }; + exports2.OrdersService = OrdersService; + exports2.OrdersService = OrdersService = OrdersService_1 = __decorate4([ + (0, common_12.Injectable)(), + __metadata3("design:paramtypes", [ + prisma_service_1.PrismaService, + catalog_service_1.CatalogService, + ledger_service_1.LedgerService + ]) + ], OrdersService); + } +}); + +// dist/modules/orders/dto/create-order.dto.js +var require_create_order_dto = __commonJS({ + "dist/modules/orders/dto/create-order.dto.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.CreateOrderDto = exports2.CreateOrderItemInputDto = void 0; + var class_transformer_1 = require_cjs2(); + var client_1 = require("@prisma/client"); + var class_validator_1 = require_cjs(); + var CreateOrderItemInputDto = class { + productVariantId; + quantity; + /** Making-charge percentage negotiated for this order (may differ from the variant default). */ + wagePercent; + discountPercent; + /** Flat Rial charge per unit, independent of gold weight (e.g. stones). */ + flatRialCharge; + }; + exports2.CreateOrderItemInputDto = CreateOrderItemInputDto; + __decorate4([ + (0, class_validator_1.IsString)(), + __metadata3("design:type", String) + ], CreateOrderItemInputDto.prototype, "productVariantId", void 0); + __decorate4([ + (0, class_validator_1.IsInt)(), + (0, class_validator_1.Min)(1), + __metadata3("design:type", Number) + ], CreateOrderItemInputDto.prototype, "quantity", void 0); + __decorate4([ + (0, class_validator_1.IsNumber)(), + __metadata3("design:type", Number) + ], CreateOrderItemInputDto.prototype, "wagePercent", void 0); + __decorate4([ + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsNumber)(), + __metadata3("design:type", Number) + ], CreateOrderItemInputDto.prototype, "discountPercent", void 0); + __decorate4([ + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsNumber)(), + __metadata3("design:type", Number) + ], CreateOrderItemInputDto.prototype, "flatRialCharge", void 0); + var CreateOrderDto = class { + /** + * IGNORED BY THE SERVER. The customer is taken from the caller's token. + * + * It was required, and the route was public, so anyone could place an order + * on any customer's account — which lands on their gold debt and their + * ledger. Logged as SEC-02 in docs/parity/ISSUES.md. + * + * Kept as an optional field only so the two apps, which still send it, do not + * fail validation mid-flight. OrdersService logs a mismatch and always uses + * the token. Removed from the DTO and from both apps in Phase 2. + */ + customerId; + branchId; + /** + * Which sales channel placed the order. Legacy had no such field because the + * two channels lived in separate tables (orders vs orders_orders); the one + * Order model carries the distinction here. Defaults to RETAIL. + */ + channel; + items; + }; + exports2.CreateOrderDto = CreateOrderDto; + __decorate4([ + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + __metadata3("design:type", String) + ], CreateOrderDto.prototype, "customerId", void 0); + __decorate4([ + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + __metadata3("design:type", String) + ], CreateOrderDto.prototype, "branchId", void 0); + __decorate4([ + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsEnum)(client_1.OrderChannel), + __metadata3("design:type", String) + ], CreateOrderDto.prototype, "channel", void 0); + __decorate4([ + (0, class_validator_1.IsArray)(), + (0, class_validator_1.ArrayMinSize)(1), + (0, class_validator_1.ValidateNested)({ each: true }), + (0, class_transformer_1.Type)(() => CreateOrderItemInputDto), + __metadata3("design:type", Array) + ], CreateOrderDto.prototype, "items", void 0); + } +}); + +// dist/modules/orders/dto/update-order-status.dto.js +var require_update_order_status_dto = __commonJS({ + "dist/modules/orders/dto/update-order-status.dto.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.UpdateOrderStatusDto = void 0; + var class_validator_1 = require_cjs(); + var client_1 = require("@prisma/client"); + var UpdateOrderStatusDto = class { + status; + note; + actorAdminUserId; + }; + exports2.UpdateOrderStatusDto = UpdateOrderStatusDto; + __decorate4([ + (0, class_validator_1.IsEnum)(client_1.OrderStatus), + __metadata3("design:type", String) + ], UpdateOrderStatusDto.prototype, "status", void 0); + __decorate4([ + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + __metadata3("design:type", String) + ], UpdateOrderStatusDto.prototype, "note", void 0); + __decorate4([ + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + __metadata3("design:type", String) + ], UpdateOrderStatusDto.prototype, "actorAdminUserId", void 0); + } +}); + +// dist/modules/orders/orders.controller.js +var require_orders_controller = __commonJS({ + "dist/modules/orders/orders.controller.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + var __param3 = exports2 && exports2.__param || function(paramIndex, decorator) { + return function(target, key) { + decorator(target, key, paramIndex); + }; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.OrdersController = void 0; + var common_12 = require_common(); + var orders_service_1 = require_orders_service(); + var create_order_dto_1 = require_create_order_dto(); + var update_order_status_dto_1 = require_update_order_status_dto(); + var guards_1 = require_guards2(); + var customer_guards_1 = require_customer_guards(); + var OrdersController = class OrdersController { + orders; + constructor(orders) { + this.orders = orders; + } + /** The customer comes from the token; the body's `customerId` is ignored. */ + create(dto, me) { + return this.orders.createOrder(dto, me.id); + } + /** + * Unscoped read for operators. + * + * Declared BEFORE the `:id` route below: Nest matches in declaration order, + * so a literal segment that could also be read as a parameter has to come + * first or it is never reached. + */ + getAsAdmin(id) { + return this.orders.getById(id); + } + /** + * A customer reading one of their own orders. Scoped by token; someone + * else's order is a 404 rather than a 403, so ids stay unenumerable. + * + * Mounted under /orders/:id (not /customer/me/orders/:id) so the existing + * client paths keep working; it is the scoping that matters, not the shape. + */ + get(id, me) { + return this.orders.getOwnedById(id, me.id); + } + updateStatus(id, dto) { + return this.orders.updateStatus(id, dto.status, dto.actorAdminUserId, dto.note); + } + }; + exports2.OrdersController = OrdersController; + __decorate4([ + (0, customer_guards_1.CustomerAuth)(), + (0, common_12.Post)(), + __param3(0, (0, common_12.Body)()), + __param3(1, (0, customer_guards_1.CurrentCustomer)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [create_order_dto_1.CreateOrderDto, Object]), + __metadata3("design:returntype", void 0) + ], OrdersController.prototype, "create", null); + __decorate4([ + (0, common_12.Get)("admin/:id"), + (0, guards_1.RequirePermission)("order.list"), + __param3(0, (0, common_12.Param)("id")), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [String]), + __metadata3("design:returntype", void 0) + ], OrdersController.prototype, "getAsAdmin", null); + __decorate4([ + (0, customer_guards_1.CustomerAuth)(), + (0, common_12.Get)(":id"), + __param3(0, (0, common_12.Param)("id")), + __param3(1, (0, customer_guards_1.CurrentCustomer)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [String, Object]), + __metadata3("design:returntype", void 0) + ], OrdersController.prototype, "get", null); + __decorate4([ + (0, common_12.Patch)(":id/status"), + (0, guards_1.RequirePermission)("order.list.sts.submit"), + __param3(0, (0, common_12.Param)("id")), + __param3(1, (0, common_12.Body)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [String, update_order_status_dto_1.UpdateOrderStatusDto]), + __metadata3("design:returntype", void 0) + ], OrdersController.prototype, "updateStatus", null); + exports2.OrdersController = OrdersController = __decorate4([ + (0, common_12.Controller)("orders"), + __metadata3("design:paramtypes", [orders_service_1.OrdersService]) + ], OrdersController); + } +}); + +// dist/modules/accounting/dto/post-gold-repayment.dto.js +var require_post_gold_repayment_dto = __commonJS({ + "dist/modules/accounting/dto/post-gold-repayment.dto.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.PostGoldRepaymentDto = void 0; + var class_validator_1 = require_cjs(); + var PostGoldRepaymentDto = class { + customerId; + instrument; + rawValue; + karatPerMille; + pricePerGramRial; + note; + }; + exports2.PostGoldRepaymentDto = PostGoldRepaymentDto; + __decorate4([ + (0, class_validator_1.IsString)(), + __metadata3("design:type", String) + ], PostGoldRepaymentDto.prototype, "customerId", void 0); + __decorate4([ + (0, class_validator_1.IsIn)(["MELTED_GOLD", "TRANSFER", "CHEQUE", "CASH"]), + __metadata3("design:type", String) + ], PostGoldRepaymentDto.prototype, "instrument", void 0); + __decorate4([ + (0, class_validator_1.IsNumber)(), + (0, class_validator_1.IsPositive)(), + __metadata3("design:type", Number) + ], PostGoldRepaymentDto.prototype, "rawValue", void 0); + __decorate4([ + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsNumber)(), + __metadata3("design:type", Number) + ], PostGoldRepaymentDto.prototype, "karatPerMille", void 0); + __decorate4([ + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsNumber)(), + (0, class_validator_1.IsPositive)(), + __metadata3("design:type", Number) + ], PostGoldRepaymentDto.prototype, "pricePerGramRial", void 0); + __decorate4([ + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + __metadata3("design:type", String) + ], PostGoldRepaymentDto.prototype, "note", void 0); + } +}); + +// dist/modules/accounting/dto/post-rial-repayment.dto.js +var require_post_rial_repayment_dto = __commonJS({ + "dist/modules/accounting/dto/post-rial-repayment.dto.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.PostRialRepaymentDto = void 0; + var class_validator_1 = require_cjs(); + var PostRialRepaymentDto = class { + customerId; + amount; + note; + }; + exports2.PostRialRepaymentDto = PostRialRepaymentDto; + __decorate4([ + (0, class_validator_1.IsString)(), + __metadata3("design:type", String) + ], PostRialRepaymentDto.prototype, "customerId", void 0); + __decorate4([ + (0, class_validator_1.IsNumber)(), + (0, class_validator_1.IsPositive)(), + __metadata3("design:type", Number) + ], PostRialRepaymentDto.prototype, "amount", void 0); + __decorate4([ + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + __metadata3("design:type", String) + ], PostRialRepaymentDto.prototype, "note", void 0); + } +}); + +// dist/modules/accounting/accounting.controller.js +var require_accounting_controller = __commonJS({ + "dist/modules/accounting/accounting.controller.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + var __param3 = exports2 && exports2.__param || function(paramIndex, decorator) { + return function(target, key) { + decorator(target, key, paramIndex); + }; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.AccountingController = void 0; + var common_12 = require_common(); + var ledger_service_1 = require_ledger_service(); + var post_gold_repayment_dto_1 = require_post_gold_repayment_dto(); + var post_rial_repayment_dto_1 = require_post_rial_repayment_dto(); + var guards_1 = require_guards2(); + var AccountingController = class AccountingController { + ledger; + constructor(ledger) { + this.ledger = ledger; + } + getBalance(customerId) { + return this.ledger.getBalance(customerId); + } + reconcile(customerId) { + return this.ledger.reconcileBalance(customerId); + } + postGoldRepayment(dto) { + return this.ledger.postGoldRepayment(dto); + } + postRialRepayment(dto) { + return this.ledger.postRialRepayment(dto); + } + confirmEntry(entryId) { + return this.ledger.confirmEntry(entryId); + } + }; + exports2.AccountingController = AccountingController; + __decorate4([ + (0, common_12.Get)("customers/:customerId/balance"), + (0, guards_1.RequirePermission)("wallet.list"), + __param3(0, (0, common_12.Param)("customerId")), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [String]), + __metadata3("design:returntype", void 0) + ], AccountingController.prototype, "getBalance", null); + __decorate4([ + (0, common_12.Post)("customers/:customerId/reconcile"), + (0, guards_1.RequirePermission)("wallet.charge"), + __param3(0, (0, common_12.Param)("customerId")), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [String]), + __metadata3("design:returntype", void 0) + ], AccountingController.prototype, "reconcile", null); + __decorate4([ + (0, common_12.Post)("repayments/gold"), + (0, guards_1.RequirePermission)("document.create"), + __param3(0, (0, common_12.Body)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [post_gold_repayment_dto_1.PostGoldRepaymentDto]), + __metadata3("design:returntype", void 0) + ], AccountingController.prototype, "postGoldRepayment", null); + __decorate4([ + (0, common_12.Post)("repayments/rial"), + (0, guards_1.RequirePermission)("document.create"), + __param3(0, (0, common_12.Body)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [post_rial_repayment_dto_1.PostRialRepaymentDto]), + __metadata3("design:returntype", void 0) + ], AccountingController.prototype, "postRialRepayment", null); + __decorate4([ + (0, common_12.Post)("entries/:entryId/confirm"), + (0, guards_1.RequirePermission)("document.list.confirm"), + __param3(0, (0, common_12.Param)("entryId")), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [String]), + __metadata3("design:returntype", void 0) + ], AccountingController.prototype, "confirmEntry", null); + exports2.AccountingController = AccountingController = __decorate4([ + (0, common_12.Controller)("accounting"), + __metadata3("design:paramtypes", [ledger_service_1.LedgerService]) + ], AccountingController); + } +}); + +// dist/modules/accounting/accounting.module.js +var require_accounting_module = __commonJS({ + "dist/modules/accounting/accounting.module.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.AccountingModule = void 0; + var common_12 = require_common(); + var ledger_service_1 = require_ledger_service(); + var accounting_controller_1 = require_accounting_controller(); + var AccountingModule = class AccountingModule { + }; + exports2.AccountingModule = AccountingModule; + exports2.AccountingModule = AccountingModule = __decorate4([ + (0, common_12.Module)({ + controllers: [accounting_controller_1.AccountingController], + providers: [ledger_service_1.LedgerService], + exports: [ledger_service_1.LedgerService] + }) + ], AccountingModule); + } +}); + +// dist/modules/orders/orders.module.js +var require_orders_module = __commonJS({ + "dist/modules/orders/orders.module.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.OrdersModule = void 0; + var common_12 = require_common(); + var orders_service_1 = require_orders_service(); + var orders_controller_1 = require_orders_controller(); + var catalog_module_1 = require_catalog_module(); + var accounting_module_1 = require_accounting_module(); + var OrdersModule = class OrdersModule { + }; + exports2.OrdersModule = OrdersModule; + exports2.OrdersModule = OrdersModule = __decorate4([ + (0, common_12.Module)({ + imports: [catalog_module_1.CatalogModule, accounting_module_1.AccountingModule], + controllers: [orders_controller_1.OrdersController], + providers: [orders_service_1.OrdersService], + exports: [orders_service_1.OrdersService] + }) + ], OrdersModule); + } +}); + +// dist/modules/pricing/pricing.service.js +var require_pricing_service = __commonJS({ + "dist/modules/pricing/pricing.service.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.PricingService = void 0; + var common_12 = require_common(); + var prisma_service_1 = require_prisma_service(); + var PricingService = class PricingService { + prisma; + constructor(prisma) { + this.prisma = prisma; + } + async recordQuote(karatPerMille, pricePerGramRial, source) { + return this.prisma.goldPriceQuote.create({ + data: { karatPerMille, pricePerGramRial, source } + }); + } + async getLatestQuote(karatPerMille) { + const quote = await this.prisma.goldPriceQuote.findFirst({ + where: { karatPerMille }, + orderBy: { quotedAt: "desc" } + }); + if (!quote) + throw new common_12.NotFoundException(`No gold price quote recorded for ${karatPerMille}\u2030`); + return quote; + } + }; + exports2.PricingService = PricingService; + exports2.PricingService = PricingService = __decorate4([ + (0, common_12.Injectable)(), + __metadata3("design:paramtypes", [prisma_service_1.PrismaService]) + ], PricingService); + } +}); + +// dist/modules/pricing/pricing.controller.js +var require_pricing_controller = __commonJS({ + "dist/modules/pricing/pricing.controller.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + var __param3 = exports2 && exports2.__param || function(paramIndex, decorator) { + return function(target, key) { + decorator(target, key, paramIndex); + }; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.PricingController = void 0; + var common_12 = require_common(); + var pricing_service_1 = require_pricing_service(); + var guards_1 = require_guards2(); + var PricingController = class PricingController { + pricing; + constructor(pricing) { + this.pricing = pricing; + } + getLatest(karatPerMille) { + return this.pricing.getLatestQuote(Number(karatPerMille) || 750); + } + }; + exports2.PricingController = PricingController; + __decorate4([ + (0, common_12.Get)("latest"), + __param3(0, (0, common_12.Query)("karatPerMille")), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [String]), + __metadata3("design:returntype", void 0) + ], PricingController.prototype, "getLatest", null); + exports2.PricingController = PricingController = __decorate4([ + (0, guards_1.Public)(), + (0, common_12.Controller)("pricing"), + __metadata3("design:paramtypes", [pricing_service_1.PricingService]) + ], PricingController); + } +}); + +// dist/modules/pricing/pricing.module.js +var require_pricing_module = __commonJS({ + "dist/modules/pricing/pricing.module.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.PricingModule = void 0; + var common_12 = require_common(); + var pricing_service_1 = require_pricing_service(); + var pricing_controller_1 = require_pricing_controller(); + var PricingModule = class PricingModule { + }; + exports2.PricingModule = PricingModule; + exports2.PricingModule = PricingModule = __decorate4([ + (0, common_12.Module)({ + controllers: [pricing_controller_1.PricingController], + providers: [pricing_service_1.PricingService], + exports: [pricing_service_1.PricingService] + }) + ], PricingModule); + } +}); + // dist/modules/auth/auth.service.js var require_auth_service = __commonJS({ "dist/modules/auth/auth.service.js"(exports2) { @@ -84705,240 +87135,6 @@ var require_auth_controller = __commonJS({ } }); -// ../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/auth_header.js -var require_auth_header = __commonJS({ - "../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/auth_header.js"(exports2, module2) { - "use strict"; - var re = /(\S+)\s+(\S+)/; - function parseAuthHeader(hdrValue) { - if (typeof hdrValue !== "string") { - return null; - } - var matches = hdrValue.match(re); - return matches && { scheme: matches[1], value: matches[2] }; - } - module2.exports = { - parse: parseAuthHeader - }; - } -}); - -// ../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/helpers/assign.js -var require_assign = __commonJS({ - "../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/helpers/assign.js"(exports2, module2) { - module2.exports = function(target, varArgs) { - if (target == null) { - throw new TypeError("Cannot convert undefined or null to object"); - } - var to = Object(target); - for (var index = 1; index < arguments.length; index++) { - var nextSource = arguments[index]; - if (nextSource != null) { - for (var nextKey in nextSource) { - if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { - to[nextKey] = nextSource[nextKey]; - } - } - } - } - return to; - }; - } -}); - -// ../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/verify_jwt.js -var require_verify_jwt = __commonJS({ - "../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/verify_jwt.js"(exports2, module2) { - var jwt = require_jsonwebtoken(); - module2.exports = function(token, secretOrKey, options, callback) { - return jwt.verify(token, secretOrKey, options, callback); - }; - } -}); - -// ../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/strategy.js -var require_strategy2 = __commonJS({ - "../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/strategy.js"(exports2, module2) { - var passport = require_lib6(); - var auth_hdr = require_auth_header(); - var util2 = require("util"); - var url = require("url"); - var assign = require_assign(); - function JwtStrategy(options, verify) { - passport.Strategy.call(this); - this.name = "jwt"; - this._secretOrKeyProvider = options.secretOrKeyProvider; - if (options.secretOrKey) { - if (this._secretOrKeyProvider) { - throw new TypeError("JwtStrategy has been given both a secretOrKey and a secretOrKeyProvider"); - } - this._secretOrKeyProvider = function(request, rawJwtToken, done) { - done(null, options.secretOrKey); - }; - } - if (!this._secretOrKeyProvider) { - throw new TypeError("JwtStrategy requires a secret or key"); - } - this._verify = verify; - if (!this._verify) { - throw new TypeError("JwtStrategy requires a verify callback"); - } - this._jwtFromRequest = options.jwtFromRequest; - if (!this._jwtFromRequest) { - throw new TypeError("JwtStrategy requires a function to retrieve jwt from requests (see option jwtFromRequest)"); - } - this._passReqToCallback = options.passReqToCallback; - var jsonWebTokenOptions = options.jsonWebTokenOptions || {}; - this._verifOpts = assign({}, jsonWebTokenOptions, { - audience: options.audience, - issuer: options.issuer, - algorithms: options.algorithms, - ignoreExpiration: !!options.ignoreExpiration - }); - } - util2.inherits(JwtStrategy, passport.Strategy); - JwtStrategy.JwtVerifier = require_verify_jwt(); - JwtStrategy.prototype.authenticate = function(req, options) { - var self2 = this; - var token = self2._jwtFromRequest(req); - if (!token) { - return self2.fail(new Error("No auth token")); - } - this._secretOrKeyProvider(req, token, function(secretOrKeyError, secretOrKey) { - if (secretOrKeyError) { - self2.fail(secretOrKeyError); - } else { - JwtStrategy.JwtVerifier(token, secretOrKey, self2._verifOpts, function(jwt_err, payload) { - if (jwt_err) { - return self2.fail(jwt_err); - } else { - var verified = function(err, user, info) { - if (err) { - return self2.error(err); - } else if (!user) { - return self2.fail(info); - } else { - return self2.success(user, info); - } - }; - try { - if (self2._passReqToCallback) { - self2._verify(req, payload, verified); - } else { - self2._verify(payload, verified); - } - } catch (ex) { - self2.error(ex); - } - } - }); - } - }); - }; - module2.exports = JwtStrategy; - } -}); - -// ../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/extract_jwt.js -var require_extract_jwt = __commonJS({ - "../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/extract_jwt.js"(exports2, module2) { - "use strict"; - var url = require("url"); - var auth_hdr = require_auth_header(); - var AUTH_HEADER = "authorization"; - var LEGACY_AUTH_SCHEME = "JWT"; - var BEARER_AUTH_SCHEME = "bearer"; - var extractors = {}; - extractors.fromHeader = function(header_name) { - return function(request) { - var token = null; - if (request.headers[header_name]) { - token = request.headers[header_name]; - } - return token; - }; - }; - extractors.fromBodyField = function(field_name) { - return function(request) { - var token = null; - if (request.body && Object.prototype.hasOwnProperty.call(request.body, field_name)) { - token = request.body[field_name]; - } - return token; - }; - }; - extractors.fromUrlQueryParameter = function(param_name) { - return function(request) { - var token = null, parsed_url = url.parse(request.url, true); - if (parsed_url.query && Object.prototype.hasOwnProperty.call(parsed_url.query, param_name)) { - token = parsed_url.query[param_name]; - } - return token; - }; - }; - extractors.fromAuthHeaderWithScheme = function(auth_scheme) { - var auth_scheme_lower = auth_scheme.toLowerCase(); - return function(request) { - var token = null; - if (request.headers[AUTH_HEADER]) { - var auth_params = auth_hdr.parse(request.headers[AUTH_HEADER]); - if (auth_params && auth_scheme_lower === auth_params.scheme.toLowerCase()) { - token = auth_params.value; - } - } - return token; - }; - }; - extractors.fromAuthHeaderAsBearerToken = function() { - return extractors.fromAuthHeaderWithScheme(BEARER_AUTH_SCHEME); - }; - extractors.fromExtractors = function(extractors2) { - if (!Array.isArray(extractors2)) { - throw new TypeError("extractors.fromExtractors expects an array"); - } - return function(request) { - var token = null; - var index = 0; - while (!token && index < extractors2.length) { - token = extractors2[index].call(this, request); - index++; - } - return token; - }; - }; - extractors.versionOneCompatibility = function(options) { - var authScheme = options.authScheme || LEGACY_AUTH_SCHEME, bodyField = options.tokenBodyField || "auth_token", queryParam = options.tokenQueryParameterName || "auth_token"; - return function(request) { - var authHeaderExtractor = extractors.fromAuthHeaderWithScheme(authScheme); - var token = authHeaderExtractor(request); - if (!token) { - var bodyExtractor = extractors.fromBodyField(bodyField); - token = bodyExtractor(request); - } - if (!token) { - var queryExtractor = extractors.fromUrlQueryParameter(queryParam); - token = queryExtractor(request); - } - return token; - }; - }; - module2.exports = extractors; - } -}); - -// ../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/index.js -var require_lib8 = __commonJS({ - "../../node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/index.js"(exports2, module2) { - "use strict"; - var Strategy = require_strategy2(); - var ExtractJwt = require_extract_jwt(); - module2.exports = { - Strategy, - ExtractJwt - }; - } -}); - // dist/modules/auth/acl.constants.js var require_acl_constants = __commonJS({ "dist/modules/auth/acl.constants.js"(exports2) { @@ -85132,18 +87328,23 @@ var require_jwt_strategy = __commonJS({ var passport_1 = require_passport(); var passport_jwt_1 = require_lib8(); var prisma_service_1 = require_prisma_service(); + var env_12 = require_env(); var acl_constants_1 = require_acl_constants(); + var APP_TOKEN_TYPES = /* @__PURE__ */ new Set(["customer", "sales-expert"]); var JwtStrategy = class JwtStrategy extends (0, passport_1.PassportStrategy)(passport_jwt_1.Strategy, "jwt") { prisma; constructor(prisma) { super({ jwtFromRequest: passport_jwt_1.ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, - secretOrKey: process.env.JWT_SECRET ?? "dev-only-insecure-secret" + secretOrKey: (0, env_12.envOr)("JWT_SECRET", "dev-only-insecure-secret") }); this.prisma = prisma; } async validate(payload) { + if (payload?.typ && APP_TOKEN_TYPES.has(payload.typ)) { + throw new common_12.UnauthorizedException(); + } const user = await this.prisma.adminUser.findUnique({ where: { id: payload.sub }, include: { userType: { include: { permissions: true } } } @@ -85188,6 +87389,7 @@ var require_auth_module = __commonJS({ var jwt_1 = (init_dist(), __toCommonJS(dist_exports)); var passport_1 = require_passport(); var prisma_module_1 = require_prisma_module(); + var env_12 = require_env(); var auth_controller_1 = require_auth_controller(); var auth_service_1 = require_auth_service(); var jwt_strategy_1 = require_jwt_strategy(); @@ -85201,11 +87403,15 @@ var require_auth_module = __commonJS({ prisma_module_1.PrismaModule, passport_1.PassportModule, jwt_1.JwtModule.register({ - secret: process.env.JWT_SECRET ?? "dev-only-insecure-secret", + // envOr, not `??`: docker-compose passes every optional variable as + // `${X:-}`, so an unconfigured one arrives as "" rather than undefined. + // An empty expiresIn makes jsonwebtoken throw on EVERY sign, which 500s + // admin login in Docker only. See DEPLOY-02. + secret: (0, env_12.envOr)("JWT_SECRET", "dev-only-insecure-secret"), // jsonwebtoken types expiresIn as a template-literal union, which a plain // `string` from process.env does not satisfy; the cast is the documented escape. signOptions: { - expiresIn: process.env.JWT_EXPIRES_IN ?? "12h" + expiresIn: (0, env_12.envOr)("JWT_EXPIRES_IN", "12h") } }) ], @@ -85502,8 +87708,8 @@ var require_admin_orders_service = __commonJS({ id: o.customer.id, // Legacy split names into fname/lname; the new schema has one fullName. // Both are exposed so ported templates binding either keep working. - fname: o.customer.fullName, - lname: "", + fname: o.customer.firstName ?? o.customer.fullName, + lname: o.customer.lastName ?? "", fullName: o.customer.fullName, phone: o.customer.mobile }, @@ -85660,13 +87866,12 @@ var require_admin_orders_service = __commonJS({ }; })); const order = await this.orders.createOrder({ - customerId: payload.customer_id, items, // The admin builder serves the omde (major/*) flow, so orders it creates // belong to the wholesale channel — legacy wrote them to the `orders` // table, which MajorOrderController::index listed. channel: client_1.OrderChannel.WHOLESALE - }); + }, payload.customer_id); const target = this.statusFromLegacyCode(payload.sts); if (target && target !== client_1.OrderStatus.PLACED) { await this.orders.updateStatus(order.id, target, actorAdminUserId, payload.note); @@ -86269,8 +88474,9 @@ var require_admin_preorders_service = __commonJS({ workshop: p.workshop ? { id: p.workshop.id, name: p.workshop.name } : null, customer: { id: p.customer.id, - fname: p.customer.fullName, - lname: "", + // D0 — real name parts + fname: p.customer.firstName ?? p.customer.fullName, + lname: p.customer.lastName ?? "", name: p.customer.fullName, fullName: p.customer.fullName, mobile: p.customer.mobile @@ -87107,12 +89313,16 @@ var require_admin_customers_service = __commonJS({ var bcrypt = __importStar2(require_umd()); var prisma_service_1 = require_prisma_service(); var table_service_1 = require_table_service(); + var digits_1 = require_digits(); + var sms_service_12 = require_sms_service(); exports2.CUSTOMER_TYPE_STR = { SALES_AGENT: " \u06A9\u0627\u0631\u0634\u0646\u0627\u0633 \u0641\u0631\u0648\u0634", + // legacy value only — see CustomerKind's schema comment APP_SHOPKEEPER: "\u0645\u063A\u0627\u0632\u0647 \u062F\u0627\u0631 \u0627\u067E\u0644\u06CC\u06A9\u06CC\u0634\u0646", WALK_IN_SHOPKEEPER: " \u0645\u063A\u0627\u0632\u0647 \u062F\u0627\u0631 \u062D\u0636\u0648\u0631\u06CC", BANKER: "\u0628\u0646\u06A9\u062F\u0627\u0631", - WORKSHOP_SUPERVISOR: "\u0633\u0631\u067E\u0631\u0633\u062A \u06A9\u0627\u0631\u06AF\u0627\u0647" + WORKSHOP_SUPERVISOR: "\u0633\u0631\u067E\u0631\u0633\u062A \u06A9\u0627\u0631\u06AF\u0627\u0647", + RETAIL: "\u0645\u0634\u062A\u0631\u06CC \u062A\u06A9" }; var LEGACY_TYPE_ID = { SALES_AGENT: 1, @@ -87123,7 +89333,7 @@ var require_admin_customers_service = __commonJS({ }; var legacyTypeToKind = (t) => Object.keys(LEGACY_TYPE_ID).find((k) => LEGACY_TYPE_ID[k] === Number(t)); exports2.legacyTypeToKind = legacyTypeToKind; - var kindToLegacyType = (k) => LEGACY_TYPE_ID[k]; + var kindToLegacyType = (k) => LEGACY_TYPE_ID[k] ?? null; exports2.kindToLegacyType = kindToLegacyType; function verifyStepOne(step) { switch (step) { @@ -87155,13 +89365,41 @@ var require_admin_customers_service = __commonJS({ } prisma; table; - constructor(prisma, table) { + sms; + logger = new common_12.Logger(AdminCustomersService_1.name); + constructor(prisma, table, sms) { this.prisma = prisma; this.table = table; + this.sms = sms; + } + /** + * The welcome SMS the reference sends after every new customer is created + * (AdminController.php:378-387). Byte-identical, including the line order and + * the trailing PWA link; the commented-out Android/iOS lines are not sent + * there and are not sent here. + * + * D20 — awaiting it would let a slow or unconfigured gateway hold up the + * HTTP response, and `send()` already swallows every error, so the result is + * only logged. The reference behaves the same way: `Sms::Send` is fired + * inline and its return value discarded, so a failed message never blocked + * the creation. + */ + sendWelcomeSms(mobile, firstName, lastName) { + const body = `\u0647\u0646\u0632\u0627 \u06AF\u0644\u062F +\u0647\u0645\u06A9\u0627\u0631 \u06AF\u0631\u0627\u0645\u06CC ${firstName} ${lastName} +\u0646\u0627\u0645 \u06A9\u0627\u0631\u0628\u0631\u06CC : ${mobile} +\u0644\u06CC\u0646\u06A9 pwa +https://app.henzagold.com`; + void this.sms.send(mobile, body).catch((e) => { + this.logger.error(`Welcome SMS to ${mobile} failed: ${String(e)}`); + }); } static ALLOWED = [ "id", "fullName", + "firstName", + "lastName", + "makingChargeOffsetPercent", "mobile", "nationalCode", "cityId", @@ -87184,8 +89422,11 @@ var require_admin_customers_service = __commonJS({ /** Translates legacy filter field names onto real schema paths. */ static FIELD_ALIASES = { name: "fullName", - fname: "fullName", - lname: "fullName", + // D0 — these now have real columns to point at. Sorting or filtering on + // the last name used to silently act on the whole name. + fname: "firstName", + lname: "lastName", + percent: "makingChargeOffsetPercent", city_id: "cityId", active: "isActive", gram_level: "goldCreditLimitGrams", @@ -87248,7 +89489,10 @@ var require_admin_customers_service = __commonJS({ this.table.assertNotExport(query); const aliased = this.applyAliases(query); const { where, orderBy, skip, take, page, limit } = this.table.build(aliased, { - searchable: ["fullName", "mobile", "nationalCode"], + // D0 — `fullName` is kept in sync by the trigger so it still matches, but + // the parts are searched too: a surname-only query is the common case and + // it must not depend on how the whole name happens to be spaced. + searchable: ["fullName", "firstName", "lastName", "mobile", "nationalCode"], allowed: AdminCustomersService_1.ALLOWED, defaultOrderBy: { createdAt: "desc" } }); @@ -87271,10 +89515,15 @@ var require_admin_customers_service = __commonJS({ toRow(c) { return { id: c.id, - // Legacy stored fname/lname separately; the new schema has one fullName. + // D0 — the schema now has the real parts, as the reference always did + // (2020_10_01_120430_customers_table.php:18-19). Before this, the edit + // form showed the WHOLE name in the "first name" box and saved an empty + // last name. `firstName`/`lastName` are backfilled and kept in sync with + // `fullName` by the customer_sync_full_name trigger, so the fallbacks + // below only matter for a row written before the migration ran. name: c.fullName, - fname: c.fullName, - lname: "", + fname: c.firstName ?? c.fullName, + lname: c.lastName ?? "", mobile: c.mobile, national_code: c.nationalCode, city_id: c.cityId, @@ -87283,8 +89532,12 @@ var require_admin_customers_service = __commonJS({ gram_level: c.goldCreditLimitGrams, remain: c.goldDebtGrams, rial_remain: c.fiatDebtRial, - // legacy `percent` came from the referring agent's commission rate - percent: c.referredByAgent?.wagePercent ?? null, + // D9 — legacy `percent` is a column ON THE CUSTOMER + // (customer_add.php:32), the per-customer making-charge offset. The port + // read the referring AGENT's commission rate instead, which is a + // different number entirely (CUST-02). The legacy key is kept so the + // ported table models and forms bind unchanged. + percent: c.makingChargeOffsetPercent, // legacy kept these as two INDEPENDENT switches — see toggleSts/toggleActive active: c.isActive ? 1 : -1, sts: c.canPlaceOrders ? 1 : -1, @@ -87344,10 +89597,16 @@ var require_admin_customers_service = __commonJS({ this.table.assertNotExport(query); const aliases = { name: "fullName", - lname: "fullName", + // D0/D17 — real columns on AdminUser now, as on Customer. + fname: "firstName", + lname: "lastName", caj: "createdAt", active: "isActive", - percent: "wagePercent" + percent: "wagePercent", + // D17 — these live on the related SalesExpertProfile, so the grid's two + // filters have to reach through the relation. + city_id: "salesExpertProfile.cityId", + gram_level: "salesExpertProfile.gramLevel" }; const mapField = (f) => f ? aliases[f] ?? f : f; const aliased = { @@ -87362,8 +89621,21 @@ var require_admin_customers_service = __commonJS({ order: query.order ? `${query.order.startsWith("-") ? "-" : "+"}${mapField(query.order.replace(/^[+-]/, ""))}` : void 0 }; const { where, orderBy, skip, take, page, limit } = this.table.build(aliased, { - searchable: ["fullName", "mobile"], - allowed: ["id", "fullName", "mobile", "isActive", "createdAt", "wagePercent", "role"], + searchable: ["fullName", "firstName", "lastName", "mobile"], + allowed: [ + "id", + "fullName", + "firstName", + "lastName", + "mobile", + "isActive", + "createdAt", + "wagePercent", + "role", + // D17 — the grid's «شهر» and «سطح اعتبار» filters. + "salesExpertProfile.cityId", + "salesExpertProfile.gramLevel" + ], defaultOrderBy: { createdAt: "desc" } }); const scoped = { @@ -87375,7 +89647,14 @@ var require_admin_customers_service = __commonJS({ orderBy, skip, take, - include: { branch: true, _count: { select: { referredCustomers: true } } } + // D17 — a LEFT JOIN by nature in Prisma: an expert without a profile + // still comes back, with `salesExpertProfile` null, so a missing row + // degrades to empty cells rather than dropping the agent from the grid. + include: { + branch: true, + salesExpertProfile: { include: { city: true } }, + _count: { select: { referredCustomers: true } } + } }), this.prisma.adminUser.count({ where: scoped }) ]); @@ -87387,11 +89666,15 @@ var require_admin_customers_service = __commonJS({ return { id: r.id, name: r.fullName, - fname: r.fullName, - lname: "", + // D0 requirement 7 — the real parts, as on Customer. This used to be + // `fname: r.fullName, lname: ''`, the same defect the customer forms had. + fname: r.firstName ?? r.fullName, + lname: r.lastName ?? "", mobile: r.mobile, - city_id: null, - city: null, + // D17 — real values now, so the grid's city column and its filter work. + // Null-safe: an expert with no profile row simply shows empty. + city_id: r.salesExpertProfile?.cityId ?? null, + city: r.salesExpertProfile?.city ? { id: r.salesExpertProfile.city.id, title: r.salesExpertProfile.city.name } : null, percent: r.wagePercent, grams_sell: agg._sum.totalGoldWeightGrams ?? 0, child: r._count.referredCustomers, @@ -87401,24 +89684,93 @@ var require_admin_customers_service = __commonJS({ caj: r.createdAt, uaj: r.createdAt, branch: r.branch ? { id: r.branch.id, name: r.branch.name } : null, - // Legacy columns an AdminUser has no counterpart for. - gram_level: null, + // D17 — from SalesExpertProfile. Null-safe for an expert with no row. + gram_level: r.salesExpertProfile?.gramLevel ?? null, + phone: r.salesExpertProfile?.phone ?? null, + ncode: r.salesExpertProfile?.nationalCode ?? null, + // Still genuinely absent: a sales expert is an AdminUser and has no + // ledger of its own, so it carries no gram or rial debt (D10). remain: null, rial_remain: null, - phone: null, - address: null + address: r.address ?? null }; })); return this.table.respond(mapped, total, query, page, limit); } + /** + * The sales agent's own detail screen ("مشاهده اطلاعات" on ReagentList.vue). + * + * A sales agent is an AdminUser now, not a Customer, so it has no debt/ + * ledger of its own — "سند های مالی" doesn't apply the way it does for a + * wholesale customer. This shows the agent's profile plus the customers + * they referred and the orders those customers placed instead. + */ + async reagentDetail(id) { + const agent = await this.prisma.adminUser.findUnique({ + where: { id, role: "SALES_EXPERT" }, + include: { branch: true, avatarFile: true } + }); + if (!agent) + throw new common_12.NotFoundException("\u06A9\u0627\u0631\u0634\u0646\u0627\u0633 \u0641\u0631\u0648\u0634 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F"); + const referred = await this.prisma.customer.findMany({ + where: { referredByAgentId: id }, + orderBy: { createdAt: "desc" }, + include: { city: true } + }); + const orders = await this.prisma.order.findMany({ + where: { customer: { referredByAgentId: id } }, + orderBy: { createdAt: "desc" }, + take: 100, + include: { customer: true } + }); + const gramsSoldAgg = await this.prisma.order.aggregate({ + where: { customer: { referredByAgentId: id }, ...AdminCustomersService_1.SOLD }, + _sum: { totalGoldWeightGrams: true } + }); + return { + id: agent.id, + name: agent.fullName, + mobile: agent.mobile, + percent: agent.wagePercent, + active: agent.isActive ? 1 : -1, + branch: agent.branch ? { id: agent.branch.id, name: agent.branch.name } : null, + image: agent.avatarFile ? { id: agent.avatarFile.id, image: agent.avatarFile.url } : null, + caj: agent.createdAt, + grams_sell: gramsSoldAgg._sum.totalGoldWeightGrams ?? 0, + referred_customers: referred.map((c) => ({ + id: c.id, + name: c.fullName, + mobile: c.mobile, + city: c.city ? { id: c.city.id, title: c.city.name } : null, + type_str: exports2.CUSTOMER_TYPE_STR[c.kind], + active: c.isActive ? 1 : -1, + caj: c.createdAt + })), + orders: orders.map((o) => ({ + id: o.id, + name: o.orderNumber, + customer: o.customer.fullName, + w: o.totalGoldWeightGrams, + status: o.status, + caj: o.createdAt + })) + }; + } + /** Orders that reached debt-recognition, reused by reagentDetail(). */ + static SOLD = { + status: { in: ["DELIVERED_TO_AGENT", "DELIVERED_TO_CUSTOMER"] } + }; /** * Legacy `POST single-customer` — the retail ("تک") customer list. * - * Legacy read this from a different table than the wholesale list - * (`orders_orders`-era SingleCustomerOrders lived alongside a separate customer - * record). The redesigned schema has one Customer model, so both lists read the - * same rows; only the column set and actions differ, exactly as the two legacy - * table models did. Same gap noted for orders in docs/ADMIN-PORT-PLAN.md. + * Legacy read this from a COMPLETELY SEPARATE table (SingleCustomer / `login`) + * that could never contain a wholesale/agent/shopkeeper/banker row — the + * separation was structural, not a WHERE-clause discipline. The redesigned + * schema folded every kind into one Customer model, so this list MUST filter + * on `kind: RETAIL` to reproduce that same separation; without it, every + * Customer row (including a mis-created sales agent — see admin-users + * module's header comment) leaked in here. Filtered explicitly rather than + * inferred, so a future creation-path bug can't quietly widen this list again. */ async listSingle(query, branchId) { this.table.assertNotExport(query); @@ -87428,9 +89780,12 @@ var require_admin_customers_service = __commonJS({ allowed: [...AdminCustomersService_1.ALLOWED, "branch.name"], defaultOrderBy: { createdAt: "desc" } }); + const scoped = { + AND: [where, { kind: client_1.CustomerKind.RETAIL }] + }; const [rows, total] = await Promise.all([ this.prisma.customer.findMany({ - where, + where: scoped, orderBy, skip, take, @@ -87440,7 +89795,7 @@ var require_admin_customers_service = __commonJS({ referredByAgent: { include: { branch: true } } } }), - this.prisma.customer.count({ where }) + this.prisma.customer.count({ where: scoped }) ]); return this.table.respond(rows.map((r) => ({ ...this.toRow(r), @@ -87449,17 +89804,69 @@ var require_admin_customers_service = __commonJS({ branch: r.referredByAgent?.branch ? { id: r.referredByAgent.branch.id, name: r.referredByAgent.branch.name } : null })), total, query, page, limit); } - /** Legacy `POST customer/create` (omdeSave) and `POST/PUT user/profile`. */ - async create(data) { - const fullName = String(data.name ?? data.fname ?? "").trim(); - const mobile = String(data.mobile ?? "").trim(); - const nationalCode = String(data.national_code ?? data.nationalCode ?? "").trim(); + /** + * Legacy `POST single` — singleCustomer.vue, the retail ("تک") customer + * form. This endpoint never existed before (singleCustomer.vue posted to + * a path nothing served — see this method's git history / the bug report + * that led here); its edit path happened to work only because `update()` + * is generic enough to patch any Customer row, agent or otherwise. + */ + async createSingle(data) { + const firstName = String(data.fname ?? data.name ?? "").trim(); + const lastName = String(data.lname ?? "").trim(); + const fullName = [firstName, lastName].filter(Boolean).join(" "); + const mobile = String(data.mobile ?? data.phone ?? "").trim(); if (!fullName || !mobile) { throw new common_12.BadRequestException("\u0646\u0627\u0645 \u0648 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A"); } return this.prisma.customer.create({ data: { fullName, + firstName: firstName || null, + lastName: lastName || null, + mobile, + nationalCode: mobile, + // schema requires it and it is unique; singleCustomer.vue collects no ncode + passwordHash: "", + cityId: data.city_id || null, + kind: client_1.CustomerKind.RETAIL + } + }); + } + /** + * Legacy `POST customer/create` (omdeSave) and `POST/PUT user/profile`. + * + * Sales agents (`type`/`kind` SALES_AGENT) are REJECTED here — a sales + * agent is an AdminUser (role SALES_EXPERT) now, created via + * `POST admin/users`, not a Customer row. bulkCustomer.vue no longer offers + * that option; this guard is defense-in-depth against any other caller + * (e.g. reg.vue, or a stale client) still sending type=1. + */ + async create(data) { + const firstName = String(data.fname ?? data.name ?? "").trim(); + const lastName = String(data.lname ?? "").trim(); + const fullName = [firstName, lastName].filter(Boolean).join(" "); + const mobile = (0, digits_1.faToEn)(String(data.mobile ?? "").trim()); + const nationalCode = String(data.national_code ?? data.nationalCode ?? "").trim(); + if (!fullName || !mobile) { + throw new common_12.BadRequestException("\u0646\u0627\u0645 \u0648 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A"); + } + if (data.percent === void 0 || data.percent === null || data.percent === "") { + throw new common_12.BadRequestException("\u0648\u0627\u0631\u062F \u06A9\u0631\u062F\u0646 percent \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A"); + } + const kind = (0, exports2.legacyTypeToKind)(data.type) ?? client_1.CustomerKind.APP_SHOPKEEPER; + if (kind === client_1.CustomerKind.SALES_AGENT) { + throw new common_12.BadRequestException("\u06A9\u0627\u0631\u0634\u0646\u0627\u0633 \u0641\u0631\u0648\u0634 \u0627\u0632 \u0627\u06CC\u0646 \u0641\u0631\u0645 \u062B\u0628\u062A \u0646\u0645\u06CC\u200C\u0634\u0648\u062F. \u0627\u0632 \u0628\u062E\u0634 \xAB\u0627\u0641\u0632\u0648\u062F\u0646 \u0645\u062F\u06CC\u0631\xBB \u0628\u0627 \u0646\u0642\u0634 \xAB\u06A9\u0627\u0631\u0634\u0646\u0627\u0633 \u0641\u0631\u0648\u0634\xBB \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u06A9\u0646\u06CC\u062F."); + } + const created = await this.prisma.customer.create({ + data: { + fullName, + firstName: firstName || null, + lastName: lastName || null, + // D9 — per-customer making-charge offset. Required above, so this is + // always a real value; the reference's own form default is 3 + // (AdminController.php:338) and is applied client-side. + makingChargeOffsetPercent: new client_1.Prisma.Decimal(String(data.percent)), mobile, nationalCode: nationalCode || mobile, // schema requires it and it is unique @@ -87472,7 +89879,7 @@ var require_admin_customers_service = __commonJS({ goldCreditLimitGrams: data.gram_level ? new client_1.Prisma.Decimal(String(data.gram_level)) : new client_1.Prisma.Decimal(0), // legacy `type` — defaults to 2 (مغازه دار اپلیکیشن), matching the // main omde list, when the form omits it. - kind: (0, exports2.legacyTypeToKind)(data.type) ?? client_1.CustomerKind.APP_SHOPKEEPER, + kind, phone: data.phone ?? null, address: data.address ?? null, storeName: data.store_name ?? null, @@ -87481,21 +89888,38 @@ var require_admin_customers_service = __commonJS({ memberCode: data.mcode ?? null, // legacy `image_id` from MyUpload's `ok` payload avatarFileId: data.image_id || null, + // The three document photos the wholesale form collects. These were + // already read back in toRow() but never persisted, so every upload was + // silently discarded on save. + nationalIdImageUrl: data.national_id_image || null, + businessLicenseUrl: data.business_license || null, + shopImageUrl: data.shop_image || null, // The sales agent who introduced this customer. The form's dropdown was // populated from a working endpoint and the selection then thrown away. referredByAgentId: data.reagent_id || null } }); + this.sendWelcomeSms(mobile, firstName, lastName); + return created; } async update(id, data) { const existing = await this.prisma.customer.findUnique({ where: { id } }); if (!existing) throw new common_12.NotFoundException("\u0645\u0634\u062A\u0631\u06CC \u06CC\u0627\u0641\u062A \u0646\u0634\u062F"); const patch = {}; - if (data.name !== void 0) + if (data.fname !== void 0 || data.lname !== void 0) { + const firstName = String(data.fname ?? existing.firstName ?? "").trim(); + const lastName = String(data.lname ?? existing.lastName ?? "").trim(); + patch.firstName = firstName || null; + patch.lastName = lastName || null; + } else if (data.name !== void 0) { patch.fullName = String(data.name); + } + if (data.percent !== void 0 && data.percent !== null && data.percent !== "") { + patch.makingChargeOffsetPercent = new client_1.Prisma.Decimal(String(data.percent)); + } if (data.mobile !== void 0) - patch.mobile = String(data.mobile); + patch.mobile = (0, digits_1.faToEn)(String(data.mobile)); if (data.national_code !== void 0) patch.nationalCode = String(data.national_code); if (data.city_id !== void 0) { @@ -87508,6 +89932,9 @@ var require_admin_customers_service = __commonJS({ const kind = (0, exports2.legacyTypeToKind)(data.type); if (!kind) throw new common_12.BadRequestException("\u0646\u0648\u0639 \u0645\u0634\u062A\u0631\u06CC \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A"); + if (kind === client_1.CustomerKind.SALES_AGENT) { + throw new common_12.BadRequestException("\u06A9\u0627\u0631\u0634\u0646\u0627\u0633 \u0641\u0631\u0648\u0634 \u0627\u0632 \u0627\u06CC\u0646 \u0641\u0631\u0645 \u0648\u06CC\u0631\u0627\u06CC\u0634 \u0646\u0645\u06CC\u200C\u0634\u0648\u062F. \u0627\u0632 \u0628\u062E\u0634 \xAB\u0627\u0641\u0632\u0648\u062F\u0646 \u0645\u062F\u06CC\u0631\xBB \u0628\u0627 \u0646\u0642\u0634 \xAB\u06A9\u0627\u0631\u0634\u0646\u0627\u0633 \u0641\u0631\u0648\u0634\xBB \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u06A9\u0646\u06CC\u062F."); + } patch.kind = kind; } if (data.phone !== void 0) @@ -87531,6 +89958,15 @@ var require_admin_customers_service = __commonJS({ if (data.image_id !== void 0) { patch.avatarFile = data.image_id ? { connect: { id: String(data.image_id) } } : { disconnect: true }; } + if (data.national_id_image !== void 0) { + patch.nationalIdImageUrl = data.national_id_image || null; + } + if (data.business_license !== void 0) { + patch.businessLicenseUrl = data.business_license || null; + } + if (data.shop_image !== void 0) { + patch.shopImageUrl = data.shop_image || null; + } return this.prisma.customer.update({ where: { id }, data: patch }); } /** @@ -87707,10 +90143,13 @@ var require_admin_customers_service = __commonJS({ if (data.gram_level === void 0) { throw new common_12.BadRequestException("\u0645\u0642\u062F\u0627\u0631 \u0633\u0637\u062D \u0627\u0639\u062A\u0628\u0627\u0631 \u0627\u0631\u0633\u0627\u0644 \u0646\u0634\u062F\u0647 \u0627\u0633\u062A"); } - return this.prisma.customer.update({ - where: { id }, - data: { goldCreditLimitGrams: new client_1.Prisma.Decimal(data.gram_level) } - }); + const patch = { + goldCreditLimitGrams: new client_1.Prisma.Decimal(data.gram_level) + }; + if (data.percent !== void 0 && data.percent !== null && String(data.percent) !== "") { + patch.makingChargeOffsetPercent = new client_1.Prisma.Decimal(String(data.percent)); + } + return this.prisma.customer.update({ where: { id }, data: patch }); } }; exports2.AdminCustomersService = AdminCustomersService; @@ -87718,7 +90157,8 @@ var require_admin_customers_service = __commonJS({ (0, common_12.Injectable)(), __metadata3("design:paramtypes", [ prisma_service_1.PrismaService, - table_service_1.TableService + table_service_1.TableService, + sms_service_12.SmsService ]) ], AdminCustomersService); } @@ -87775,6 +90215,10 @@ var require_admin_customers_controller = __commonJS({ listSingle(query, admin) { return this.customers.listSingle(query, admin.branchId ?? void 0); } + /** Legacy `POST single` — singleCustomer.vue, the retail customer form. */ + createSingle(data) { + return this.customers.createSingle(data); + } /** * Legacy `GET query/reagent` — the sales-expert picker used by the customer * form. Returns a plain array of {id, name}, as legacy did. @@ -87796,6 +90240,10 @@ var require_admin_customers_controller = __commonJS({ listReagents(query) { return this.customers.listReagents(query); } + /** A sales agent's own detail screen — see reagentDetail()'s header comment. */ + reagentDetail(id) { + return this.customers.reagentDetail(id); + } /** Legacy `GET single/order/{id}` — that customer's orders. */ customerOrders(id) { return this.customers.customerOrders(id); @@ -87886,6 +90334,15 @@ var require_admin_customers_controller = __commonJS({ __metadata3("design:paramtypes", [table_query_dto_1.TableQueryDto, Object]), __metadata3("design:returntype", void 0) ], AdminCustomersController.prototype, "listSingle", null); + __decorate4([ + (0, common_12.Post)("single"), + (0, common_12.HttpCode)(200), + (0, guards_1.RequirePermission)("persons.create"), + __param3(0, (0, common_12.Body)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [Object]), + __metadata3("design:returntype", void 0) + ], AdminCustomersController.prototype, "createSingle", null); __decorate4([ (0, common_12.Get)("query/reagent"), (0, guards_1.RequirePermission)("persons.create"), @@ -87929,6 +90386,14 @@ var require_admin_customers_controller = __commonJS({ __metadata3("design:paramtypes", [table_query_dto_1.TableQueryDto]), __metadata3("design:returntype", void 0) ], AdminCustomersController.prototype, "listReagents", null); + __decorate4([ + (0, common_12.Get)("reagent/:id"), + (0, guards_1.RequirePermission)("persons.list.sellers.view"), + __param3(0, (0, common_12.Param)("id")), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [String]), + __metadata3("design:returntype", void 0) + ], AdminCustomersController.prototype, "reagentDetail", null); __decorate4([ (0, common_12.Get)(":id/orders"), (0, guards_1.RequirePermission)("persons.list.client.omde.view.orders"), @@ -88166,8 +90631,9 @@ var require_admin_comments_service = __commonJS({ customer: { id: c.customer.id, name: c.customer.fullName, - fname: c.customer.fullName, - lname: "", + // D0 — real name parts + fname: c.customer.firstName ?? c.customer.fullName, + lname: c.customer.lastName ?? "", mobile: c.customer.mobile }, caj: c.createdAt, @@ -88514,7 +90980,7 @@ var require_admin_products_service = __commonJS({ brand: true, category: true, images: { orderBy: { sortOrder: "asc" } }, - variants: true, + variants: { include: { image: true } }, collections: { include: { collection: true } } } }); @@ -88533,7 +90999,16 @@ var require_admin_products_service = __commonJS({ indicator_id: p.images.find((i) => i.role === "FEATURED")?.url ?? null, mobile_pic: p.images.find((i) => i.role === "FEATURED_MOBILE")?.url ?? null, images_gallery: p.images.filter((i) => i.role === "GALLERY").map((i) => i.url), - images_mobile: p.images.filter((i) => i.role === "GALLERY_MOBILE").map((i) => i.url) + images_mobile: p.images.filter((i) => i.role === "GALLERY_MOBILE").map((i) => i.url), + // The same images again, but carrying their ids — the variant image + // picker needs something to select. The four URL-only fields above stay + // exactly as they were so the form's existing bindings keep working. + gallery: p.images.map((i) => ({ + id: i.id, + url: i.url, + role: i.role, + sortOrder: i.sortOrder + })) }; } variantRow(v) { @@ -88547,6 +91022,11 @@ var require_admin_products_service = __commonJS({ qty: v.qty, is_pre_order: v.isPreOrder, is_active: v.isActive, + // The gallery image picked for this variant. `image_id` is what the form + // posts back; `image` is denormalised so the table can render a thumbnail + // without a second lookup. Only populated where the query included it. + image_id: v.imageId, + image: v.image ? { id: v.image.id, url: v.image.url } : null, // Legacy had a separate rial making-charge column; the schema models wage // as a percentage only. Returned as null rather than faked. rial_pay: null, @@ -88568,7 +91048,7 @@ var require_admin_products_service = __commonJS({ orderBy, skip, take, - include: { product: true } + include: { product: true, image: true } }), this.prisma.productVariant.count({ where: scoped }) ]); @@ -88588,7 +91068,7 @@ var require_admin_products_service = __commonJS({ brand: true, category: true, images: { orderBy: { sortOrder: "asc" } }, - variants: true + variants: { include: { image: true } } } }); return rows.map((p) => ({ @@ -88627,20 +91107,64 @@ var require_admin_products_service = __commonJS({ } /** Legacy `GET product/{id}/variant`. */ async variantsOf(productId) { - const rows = await this.prisma.productVariant.findMany({ where: { productId } }); + const rows = await this.prisma.productVariant.findMany({ + where: { productId }, + include: { image: true } + }); return rows.map((v) => this.variantRow(v)); } - /** Shape of one inline variant row posted by the product form. */ - variantCreateData(v) { + /** + * Shape of one inline variant row posted by the product form. + * + * `image_id` is resolved against `allowedImageIds` — the ids of the images + * belonging to *this* product — so a payload can never point a variant at + * another product's image. An absent/empty value clears the picture. + */ + variantCreateData(v, allowedImageIds) { + const rawImageId = v.image_id ?? v.imageId; + const imageId = rawImageId ? String(rawImageId) : null; + if (imageId && !allowedImageIds.has(imageId)) { + throw new common_12.BadRequestException("\u062A\u0635\u0648\u06CC\u0631 \u0627\u0646\u062A\u062E\u0627\u0628\u200C\u0634\u062F\u0647 \u0645\u062A\u0639\u0644\u0642 \u0628\u0647 \u0627\u06CC\u0646 \u0645\u062D\u0635\u0648\u0644 \u0646\u06CC\u0633\u062A"); + } return { title: String(v.title ?? ""), rashWeightGrams: new client_1.Prisma.Decimal(String(v.grams ?? 0)), defaultWagePercent: new client_1.Prisma.Decimal(String(v.percentage_pay ?? 0)), qty: Number(v.qty ?? 0), isPreOrder: Boolean(v.is_pre_order), - isActive: v.is_active !== void 0 ? Boolean(v.is_active) : true + isActive: v.is_active !== void 0 ? Boolean(v.is_active) : true, + imageId }; } + /** + * Builds a commercial SKU for a newly created variant. + * + * Derived from the product's own code (falling back to a slice of its id when + * no code was entered) plus a running number, e.g. `RING-001-01`. Only ever + * called for variants that have no SKU yet — an existing one is never + * rewritten, because it may already be in use in stock/sales systems. + * + * `taken` carries the SKUs handed out earlier in the same request so two new + * variants in one submit cannot collide before either is written. + */ + async generateSku(productCode, productId, taken) { + const base = (productCode?.trim() || productId.slice(-6)).toUpperCase().replace(/\s+/g, "-"); + const existing = await this.prisma.productVariant.findMany({ + where: { sku: { startsWith: `${base}-` } }, + select: { sku: true } + }); + for (const row of existing) + if (row.sku) + taken.add(row.sku); + for (let n = 1; n <= 9999; n++) { + const candidate = `${base}-${String(n).padStart(2, "0")}`; + if (!taken.has(candidate)) { + taken.add(candidate); + return candidate; + } + } + return `${base}-${Date.now()}`; + } /** * Legacy had 4 distinct upload slots (تصویر شاخص / تصویر شاخص موبایل / * گالری تصاویر سایت / گالری تصاویر موبایل — AddProduct.vue's `indicator_id`, @@ -88679,21 +91203,36 @@ var require_admin_products_service = __commonJS({ const cinyIds = Array.isArray(data.ciny_ids) ? data.ciny_ids : []; const variants = Array.isArray(data.variants) ? data.variants : []; const images = this.imageCreateData(data); - return this.prisma.product.create({ + const productCode = data.product_code || null; + const created = await this.prisma.product.create({ data: { title, description: data.body_html ?? data.description ?? null, - productCode: data.product_code || null, + productCode, showType: data.show_type !== void 0 ? Number(data.show_type) : 3, isActive: data.is_active !== void 0 ? Boolean(data.is_active) : true, brandId: data.brand_id || null, categoryId: cats[0] ?? null, collections: cinyIds.length ? { create: cinyIds.map((collectionId) => ({ collectionId })) } : void 0, - variants: variants.length ? { create: variants.map((v) => this.variantCreateData(v)) } : void 0, images: images.length ? { create: images } : void 0 }, - include: { variants: true, images: true } + include: { images: true } }); + if (variants.length) { + const allowed = new Set(created.images.map((i) => i.id)); + const takenSkus = /* @__PURE__ */ new Set(); + for (const v of variants) { + const payload = this.variantCreateData(v, allowed); + await this.prisma.productVariant.create({ + data: { + ...payload, + productId: created.id, + sku: await this.generateSku(productCode, created.id, takenSkus) + } + }); + } + } + return this.findOne(created.id); } /** Legacy `PUT product/update/{id}`. */ async update(id, data) { @@ -88719,11 +91258,19 @@ var require_admin_products_service = __commonJS({ patch.category = catId ? { connect: { id: catId } } : { disconnect: true }; } await this.prisma.product.update({ where: { id }, data: patch }); + await this.syncImages(id, data); if (Array.isArray(data.variants)) { const posted = data.variants; const keptIds = []; + const currentImages = await this.prisma.productImage.findMany({ + where: { productId: id }, + select: { id: true } + }); + const allowed = new Set(currentImages.map((i) => i.id)); + const takenSkus = /* @__PURE__ */ new Set(); + const productCode = data.product_code !== void 0 ? data.product_code || null : existing.productCode; for (const v of posted) { - const payload = this.variantCreateData(v); + const payload = this.variantCreateData(v, allowed); if (v.id) { await this.prisma.productVariant.update({ where: { id: String(v.id) }, @@ -88732,7 +91279,11 @@ var require_admin_products_service = __commonJS({ keptIds.push(String(v.id)); } else { const created = await this.prisma.productVariant.create({ - data: { ...payload, productId: id } + data: { + ...payload, + productId: id, + sku: await this.generateSku(productCode, id, takenSkus) + } }); keptIds.push(created.id); } @@ -88742,16 +91293,61 @@ var require_admin_products_service = __commonJS({ data: { isActive: false } }); } - if (["indicator_id", "mobile_pic", "images", "images_mobile"].some((k) => k in data)) { - const images = this.imageCreateData(data); - await this.prisma.productImage.deleteMany({ where: { productId: id } }); - if (images.length) { - await this.prisma.productImage.createMany({ - data: images.map((img) => ({ ...img, productId: id })) - }); + return this.findOne(id); + } + /** + * Applies the 4 posted image slots to a product WITHOUT churning row ids. + * + * This used to be a delete-everything-and-recreate, which was fine while + * nothing referenced an image. ProductVariant.imageId now does, so a rewrite + * would silently blank every variant's picture on each save (the FK is + * ON DELETE SET NULL). Instead each posted (url, role) pair is matched + * against the rows already stored: survivors keep their id and only have + * their sortOrder refreshed, genuinely new URLs are inserted, and rows the + * form dropped are deleted. + * + * The form always posts all 4 image fields together (even as empty/absent), + * so any one of them being present means this is a real submit rather than a + * partial PATCH that happens to omit the others. + */ + async syncImages(productId, data) { + if (!["indicator_id", "mobile_pic", "images", "images_mobile"].some((k) => k in data)) + return; + const desired = this.imageCreateData(data); + const current = await this.prisma.productImage.findMany({ where: { productId } }); + const pools = /* @__PURE__ */ new Map(); + for (const row of current) { + const key = `${row.role}\0${row.url}`; + const pool = pools.get(key); + if (pool) + pool.push(row); + else + pools.set(key, [row]); + } + const survivingIds = /* @__PURE__ */ new Set(); + for (const img of desired) { + const key = `${img.role}\0${img.url}`; + const match = pools.get(key)?.shift(); + if (match) { + survivingIds.add(match.id); + if (match.sortOrder !== img.sortOrder) { + await this.prisma.productImage.update({ + where: { id: match.id }, + data: { sortOrder: img.sortOrder } + }); + } + } else { + await this.prisma.productImage.create({ data: { ...img, productId } }); } } - return this.findOne(id); + const removedIds = current.filter((row) => !survivingIds.has(row.id)).map((row) => row.id); + if (removedIds.length) { + await this.prisma.productVariant.updateMany({ + where: { imageId: { in: removedIds } }, + data: { imageId: null } + }); + await this.prisma.productImage.deleteMany({ where: { id: { in: removedIds } } }); + } } /** * Legacy `POST delete/product/{id}` — a DELETE expressed as POST. @@ -88774,8 +91370,11 @@ var require_admin_products_service = __commonJS({ await this.prisma.$transaction([ this.prisma.collectionProduct.deleteMany({ where: { productId: id } }), this.prisma.comment.deleteMany({ where: { productId: id } }), - this.prisma.productImage.deleteMany({ where: { productId: id } }), + // Variants reference images (ProductVariant.imageId), so they go first — + // the FK is ON DELETE SET NULL, but deleting in dependency order keeps + // this correct regardless of how the constraint is enforced. this.prisma.productVariant.deleteMany({ where: { productId: id } }), + this.prisma.productImage.deleteMany({ where: { productId: id } }), this.prisma.product.delete({ where: { id } }) ]); return { success: true }; @@ -89730,8 +92329,9 @@ var require_admin_documents_service = __commonJS({ customer: { id: e.customer.id, name: e.customer.fullName, - fname: e.customer.fullName, - lname: "", + // D0 — real name parts + fname: e.customer.firstName ?? e.customer.fullName, + lname: e.customer.lastName ?? "", mobile: e.customer.mobile }, posted_by: e.postedByAdminUser ? e.postedByAdminUser.fullName : null, @@ -89937,9 +92537,15 @@ var require_admin_documents_service = __commonJS({ const rows = await this.prisma.customer.findMany({ where: { isActive: true }, orderBy: { fullName: "asc" }, - select: { id: true, fullName: true, mobile: true } + select: { id: true, fullName: true, firstName: true, lastName: true, mobile: true } }); - return rows.map((c) => ({ id: c.id, name: c.fullName, fname: c.fullName, lname: "", mobile: c.mobile })); + return rows.map((c) => ({ + id: c.id, + name: c.fullName, + fname: c.firstName ?? c.fullName, + lname: c.lastName ?? "", + mobile: c.mobile + })); } }; exports2.AdminDocumentsService = AdminDocumentsService; @@ -90752,6 +93358,7 @@ var require_admin_users_service = __commonJS({ var bcrypt = __importStar2(require_umd()); var prisma_service_1 = require_prisma_service(); var table_service_1 = require_table_service(); + var digits_1 = require_digits(); var AdminUsersService = class AdminUsersService { static { AdminUsersService_1 = this; @@ -90811,7 +93418,7 @@ var require_admin_users_service = __commonJS({ orderBy, skip, take, - include: { userType: true, branch: true } + include: { userType: true, branch: true, avatarFile: true } }), this.prisma.adminUser.count({ where }) ]); @@ -90822,8 +93429,10 @@ var require_admin_users_service = __commonJS({ return { id: u.id, name: u.fullName, - fname: u.fullName, - lname: "", + // D0 requirement 7 — the real parts. The fallback only matters for a row + // written before the 20260922180000 migration backfilled them. + fname: u.firstName ?? u.fullName, + lname: u.lastName ?? "", mobile: u.mobile, role: u.role, ut: u.userTypeId, @@ -90833,56 +93442,128 @@ var require_admin_users_service = __commonJS({ percent: u.wagePercent, active: u.isActive ? 1 : -1, is_active: u.isActive, - caj: u.createdAt + caj: u.createdAt, + // Sales-agent fields (ReagentCustomer.vue) — set only for role=SALES_EXPERT, + // null for every other admin. Same shapes admin-customers.service.ts used + // when this data still lived on Customer, so the form binds unchanged. + address: u.address, + store_name: u.storeName, + pasazh_name: u.pasazhName, + mcode: u.memberCode, + image_id: u.avatarFileId, + image: u.avatarFile ? { id: u.avatarFile.id, image: u.avatarFile.url } : null, + // D17 — from SalesExpertProfile. Null for a non-expert, and null-safe for + // an expert whose profile row is somehow missing. + city_id: u.salesExpertProfile?.cityId ?? null, + city: u.salesExpertProfile?.city ? { id: u.salesExpertProfile.city.id, title: u.salesExpertProfile.city.name } : null, + gram_level: u.salesExpertProfile?.gramLevel ?? null, + phone: u.salesExpertProfile?.phone ?? null, + ncode: u.salesExpertProfile?.nationalCode ?? null }; } async findOne(id) { const u = await this.prisma.adminUser.findUnique({ where: { id }, - include: { userType: true, branch: true } + // D17 — the edit form needs the sales-expert fields too. + include: { + userType: true, + branch: true, + avatarFile: true, + salesExpertProfile: { include: { city: true } } + } }); if (!u) throw new common_12.NotFoundException("\u06A9\u0627\u0631\u0628\u0631 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F"); return this.toRow(u); } - /** Legacy `POST users`. */ + /** + * Legacy `POST users`. + * + * `role` is REQUIRED — it used to default to SALES_EXPERT whenever the + * caller omitted it, which silently turned every admin created without an + * explicit role into a sales agent regardless of the access level ("نوع + * کاربری") actually chosen. UserCreate.vue now always sends one. + */ async create(data) { - const fullName = String(data.name ?? data.fname ?? "").trim(); - const mobile = String(data.mobile ?? "").trim(); + const firstName = String(data.fname ?? data.name ?? "").trim(); + const lastName = String(data.lname ?? "").trim(); + const fullName = [firstName, lastName].filter(Boolean).join(" "); + const mobile = (0, digits_1.faToEn)(String(data.mobile ?? "").trim()); const password = String(data.password ?? ""); + const role = data.role; if (!fullName) throw new common_12.BadRequestException("\u0646\u0627\u0645 \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A"); if (!mobile) throw new common_12.BadRequestException("\u0645\u0648\u0628\u0627\u06CC\u0644 \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A"); if (!password) throw new common_12.BadRequestException("\u0631\u0645\u0632 \u0639\u0628\u0648\u0631 \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A"); + if (!role) + throw new common_12.BadRequestException("\u0646\u0642\u0634 \u06A9\u0627\u0631\u0628\u0631 \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A"); const existing = await this.prisma.adminUser.findUnique({ where: { mobile } }); if (existing) throw new common_12.BadRequestException("\u0627\u06CC\u0646 \u0634\u0645\u0627\u0631\u0647 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0642\u0628\u0644\u0627\u064B \u062B\u0628\u062A \u0634\u062F\u0647 \u0627\u0633\u062A"); return this.prisma.adminUser.create({ data: { fullName, + firstName: firstName || null, + lastName: lastName || null, mobile, passwordHash: await bcrypt.hash(password, 10), - role: data.role ?? client_1.AdminRole.SALES_EXPERT, + role, userTypeId: data.ut !== void 0 && data.ut !== null ? Number(data.ut) : null, branchId: data.branch_id || null, wagePercent: data.percent !== void 0 && data.percent !== null ? new client_1.Prisma.Decimal(String(data.percent)) : null, - isActive: data.is_active !== void 0 ? Boolean(data.is_active) : true + isActive: data.is_active !== void 0 ? Boolean(data.is_active) : true, + // Sales-agent-only fields (ReagentCustomer.vue); undefined for every + // other role, which Prisma leaves at column default (null). + address: data.address || null, + storeName: data.store_name || null, + pasazhName: data.pasazh_name || null, + memberCode: data.mcode || null, + avatarFileId: data.image_id || null, + // D17 — a sales expert always gets its profile row, so the grid's city + // and credit-limit columns have somewhere to read from immediately. + ...role === "SALES_EXPERT" ? { salesExpertProfile: { create: this.salesExpertProfileData(data) } } : {} }, - include: { userType: true, branch: true } + include: { + userType: true, + branch: true, + avatarFile: true, + salesExpertProfile: { include: { city: true } } + } }); } + /** + * The SalesExpertProfile columns as posted by ReagentCustomer.vue (D17). + * + * `phone` and `nationalCode` are stored exactly as the reference does: + * optional, not unique, no format rule — the reference validates only + * `mobile` and `percent` on this form (AdminController.php:354). + */ + salesExpertProfileData(data) { + return { + cityId: data.city_id || null, + gramLevel: data.gram_level !== void 0 && data.gram_level !== null && data.gram_level !== "" ? new client_1.Prisma.Decimal(String(data.gram_level)) : new client_1.Prisma.Decimal(0), + phone: data.phone || null, + nationalCode: data.ncode || null + }; + } /** Legacy `PUT users/{id}`. */ async update(id, data) { const existing = await this.prisma.adminUser.findUnique({ where: { id } }); if (!existing) throw new common_12.NotFoundException("\u06A9\u0627\u0631\u0628\u0631 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F"); const patch = {}; - if (data.name !== void 0) + if (data.fname !== void 0 || data.lname !== void 0) { + const firstName = String(data.fname ?? existing.firstName ?? "").trim(); + const lastName = String(data.lname ?? existing.lastName ?? "").trim(); + patch.firstName = firstName || null; + patch.lastName = lastName || null; + } else if (data.name !== void 0) { patch.fullName = String(data.name); + } if (data.mobile !== void 0) { - const mobile = String(data.mobile); + const mobile = (0, digits_1.faToEn)(String(data.mobile)); if (mobile !== existing.mobile) { const clash = await this.prisma.adminUser.findUnique({ where: { mobile } }); if (clash) @@ -90905,10 +93586,44 @@ var require_admin_users_service = __commonJS({ } if (data.is_active !== void 0) patch.isActive = Boolean(data.is_active); + if (data.address !== void 0) + patch.address = data.address || null; + if (data.store_name !== void 0) + patch.storeName = data.store_name || null; + if (data.pasazh_name !== void 0) + patch.pasazhName = data.pasazh_name || null; + if (data.mcode !== void 0) + patch.memberCode = data.mcode || null; + if (data.image_id !== void 0) { + patch.avatarFile = data.image_id ? { connect: { id: String(data.image_id) } } : { disconnect: true }; + } + const effectiveRole = patch.role ?? existing.role; + const touchesProfile = data.city_id !== void 0 || data.gram_level !== void 0 || data.phone !== void 0 || data.ncode !== void 0; + if (effectiveRole === "SALES_EXPERT" && (touchesProfile || patch.role !== void 0)) { + const profile = this.salesExpertProfileData(data); + patch.salesExpertProfile = { + upsert: { + create: profile, + // Only the keys the caller actually sent, so an untouched field is + // not overwritten with a default on a partial (diff-send) save. + update: { + ...data.city_id !== void 0 ? { cityId: profile.cityId } : {}, + ...data.gram_level !== void 0 ? { gramLevel: profile.gramLevel } : {}, + ...data.phone !== void 0 ? { phone: profile.phone } : {}, + ...data.ncode !== void 0 ? { nationalCode: profile.nationalCode } : {} + } + } + }; + } return this.prisma.adminUser.update({ where: { id }, data: patch, - include: { userType: true, branch: true } + include: { + userType: true, + branch: true, + avatarFile: true, + salesExpertProfile: { include: { city: true } } + } }); } /** @@ -92149,15 +94864,46 @@ var require_admin_misc_controller = __commonJS({ removeBrand(id) { return this.misc.removeBrand(id); } + // The mobile home slider ("اسلایدر موبایل") is stored under the same + // `Setting` table as every other `const/:key` entry (key: mobile_main_slider), + // but it gets its own permission (`custom.config.statics.slider`, per + // acl.constants.ts / permissionConst.ts) rather than the broad `config.statics` + // that guards the rest of the "مدیریت ثابت ها" screen — uploading/replacing the + // public site banner is more sensitive than editing brand/tag lists, so it + // should not be reachable by everyone who can merely open that screen. + // Routes declared ahead of `const/:key` so Nest doesn't match `const/slider` + // (the legacy REST shape) to the generic key handler instead. + getSlider() { + return this.misc.getConst("mobile_main_slider"); + } + setSlider(body) { + return this.misc.setConst("mobile_main_slider", body.val !== void 0 ? body.val : body); + } + removeSlider() { + return this.misc.removeConst("mobile_main_slider"); + } getConst(key) { + this.assertNotSliderKey(key); return this.misc.getConst(key); } setConst(key, body) { + this.assertNotSliderKey(key); return this.misc.setConst(key, body.val !== void 0 ? body.val : body); } removeConst(key) { + this.assertNotSliderKey(key); return this.misc.removeConst(key); } + // Blocks the generic `config.statics`-gated routes from reaching the slider + // setting by its raw key — otherwise anyone with `config.statics` could + // bypass the dedicated `const/slider` routes (guarded by the stricter + // `custom.config.statics.slider`) simply by calling `const/mobile_main_slider` + // directly, since the underlying Setting row is keyed the same either way. + assertNotSliderKey(key) { + if (key === "mobile_main_slider") { + throw new common_12.ForbiddenException("\u0627\u06CC\u0646 \u062A\u0646\u0638\u06CC\u0645 \u0641\u0642\u0637 \u0627\u0632 \u0645\u0633\u06CC\u0631 \u0627\u062E\u062A\u0635\u0627\u0635\u06CC \u062E\u0648\u062F \u0642\u0627\u0628\u0644 \u062F\u0633\u062A\u0631\u0633\u06CC \u0627\u0633\u062A"); + } + } }; exports2.AdminMiscController = AdminMiscController; __decorate4([ @@ -92339,6 +95085,28 @@ var require_admin_misc_controller = __commonJS({ __metadata3("design:paramtypes", [String]), __metadata3("design:returntype", void 0) ], AdminMiscController.prototype, "removeBrand", null); + __decorate4([ + (0, common_12.Get)("const/slider"), + (0, guards_1.RequirePermission)("custom.config.statics.slider"), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", []), + __metadata3("design:returntype", void 0) + ], AdminMiscController.prototype, "getSlider", null); + __decorate4([ + (0, common_12.Put)("const/slider"), + (0, guards_1.RequirePermission)("custom.config.statics.slider"), + __param3(0, (0, common_12.Body)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [Object]), + __metadata3("design:returntype", void 0) + ], AdminMiscController.prototype, "setSlider", null); + __decorate4([ + (0, common_12.Delete)("const/slider"), + (0, guards_1.RequirePermission)("custom.config.statics.slider"), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", []), + __metadata3("design:returntype", void 0) + ], AdminMiscController.prototype, "removeSlider", null); __decorate4([ (0, common_12.Get)("const/:key"), (0, guards_1.RequirePermission)("config.statics"), @@ -92371,6 +95139,49 @@ var require_admin_misc_controller = __commonJS({ } }); +// dist/modules/admin-misc/public-slider.controller.js +var require_public_slider_controller = __commonJS({ + "dist/modules/admin-misc/public-slider.controller.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.PublicSliderController = void 0; + var common_12 = require_common(); + var admin_misc_service_1 = require_admin_misc_service(); + var guards_1 = require_guards2(); + var PublicSliderController = class PublicSliderController { + misc; + constructor(misc) { + this.misc = misc; + } + async getSlider() { + const value = await this.misc.getConst("mobile_main_slider"); + return Array.isArray(value) ? value : []; + } + }; + exports2.PublicSliderController = PublicSliderController; + __decorate4([ + (0, common_12.Get)("slider"), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", []), + __metadata3("design:returntype", Promise) + ], PublicSliderController.prototype, "getSlider", null); + exports2.PublicSliderController = PublicSliderController = __decorate4([ + (0, guards_1.Public)(), + (0, common_12.Controller)("home"), + __metadata3("design:paramtypes", [admin_misc_service_1.AdminMiscService]) + ], PublicSliderController); + } +}); + // dist/modules/admin-misc/admin-misc.module.js var require_admin_misc_module = __commonJS({ "dist/modules/admin-misc/admin-misc.module.js"(exports2) { @@ -92386,6 +95197,7 @@ var require_admin_misc_module = __commonJS({ var common_12 = require_common(); var prisma_module_1 = require_prisma_module(); var admin_misc_controller_1 = require_admin_misc_controller(); + var public_slider_controller_1 = require_public_slider_controller(); var admin_misc_service_1 = require_admin_misc_service(); var AdminMiscModule = class AdminMiscModule { }; @@ -92393,285 +95205,13 @@ var require_admin_misc_module = __commonJS({ exports2.AdminMiscModule = AdminMiscModule = __decorate4([ (0, common_12.Module)({ imports: [prisma_module_1.PrismaModule], - controllers: [admin_misc_controller_1.AdminMiscController], + controllers: [admin_misc_controller_1.AdminMiscController, public_slider_controller_1.PublicSliderController], providers: [admin_misc_service_1.AdminMiscService] }) ], AdminMiscModule); } }); -// dist/modules/admin-uploads/upload-path.js -var require_upload_path = __commonJS({ - "dist/modules/admin-uploads/upload-path.js"(exports2) { - "use strict"; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.getUploadDir = getUploadDir; - exports2.mimeForUpload = mimeForUpload; - exports2.resolveStoredUpload = resolveStoredUpload; - var node_fs_1 = require("node:fs"); - var node_path_12 = require("node:path"); - function getUploadDir() { - return (0, node_path_12.resolve)(process.env.UPLOAD_DIR ?? (0, node_path_12.join)(process.cwd(), "storage", "uploads")); - } - var UPLOAD_MIME = { - ".png": "image/png", - ".jpg": "image/jpeg", - ".jpeg": "image/jpeg", - ".gif": "image/gif", - ".webp": "image/webp", - ".avif": "image/avif", - ".svg": "image/svg+xml", - ".pdf": "application/pdf" - }; - function mimeForUpload(filePath) { - const dot = filePath.lastIndexOf("."); - const ext = dot >= 0 ? filePath.slice(dot).toLowerCase() : ""; - return UPLOAD_MIME[ext]; - } - function resolveStoredUpload(rawName) { - const safe = (0, node_path_12.basename)(rawName); - if (!safe || safe === "." || safe === "..") - return null; - const dir = getUploadDir(); - const candidates = [(0, node_path_12.join)(dir, safe), (0, node_path_12.join)(dir, "uploads", safe)]; - for (const candidate of candidates) { - const resolved = (0, node_path_12.resolve)(candidate); - const rel = (0, node_path_12.relative)(dir, resolved); - if (!rel || rel.startsWith("..") || (0, node_path_12.isAbsolute)(rel)) - continue; - if ((0, node_fs_1.existsSync)(resolved) && (0, node_fs_1.statSync)(resolved).isFile()) - return resolved; - } - return null; - } - } -}); - -// dist/modules/admin-uploads/admin-uploads.service.js -var require_admin_uploads_service = __commonJS({ - "dist/modules/admin-uploads/admin-uploads.service.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - var __metadata3 = exports2 && exports2.__metadata || function(k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); - }; - var AdminUploadsService_1; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.AdminUploadsService = void 0; - var common_12 = require_common(); - var node_crypto_1 = require("node:crypto"); - var promises_1 = require("node:fs/promises"); - var node_path_12 = require("node:path"); - var prisma_service_1 = require_prisma_service(); - var upload_path_1 = require_upload_path(); - var AdminUploadsService = class AdminUploadsService { - static { - AdminUploadsService_1 = this; - } - prisma; - /** Matches legacy's flat public/storage directory. */ - dir = (0, upload_path_1.getUploadDir)(); - /** Mirrors legacy's `type` field: 1 = image, 2 = any other document. */ - static IMAGE_MIME = /^image\/(jpeg|png|gif|webp|svg\+xml)$/; - static ALLOWED = /^(image\/(jpeg|png|gif|webp|svg\+xml)|application\/pdf)$/; - /** 8 MB. Legacy relied on PHP's upload_max_filesize; this makes it explicit. */ - static MAX_BYTES = 8 * 1024 * 1024; - constructor(prisma) { - this.prisma = prisma; - } - async store(file2, purpose, actorAdminUserId) { - if (!file2) - throw new common_12.BadRequestException("\u0641\u0627\u06CC\u0644\u06CC \u0627\u0631\u0633\u0627\u0644 \u0646\u0634\u062F\u0647 \u0627\u0633\u062A"); - if (file2.size > AdminUploadsService_1.MAX_BYTES) { - throw new common_12.BadRequestException("\u062D\u062C\u0645 \u0641\u0627\u06CC\u0644 \u0628\u06CC\u0634 \u0627\u0632 \u062D\u062F \u0645\u062C\u0627\u0632 \u0627\u0633\u062A (\u06F8 \u0645\u06AF\u0627\u0628\u0627\u06CC\u062A)"); - } - if (!AdminUploadsService_1.ALLOWED.test(file2.mimetype)) { - throw new common_12.BadRequestException("\u0646\u0648\u0639 \u0641\u0627\u06CC\u0644 \u0645\u062C\u0627\u0632 \u0646\u06CC\u0633\u062A"); - } - const ext = (0, node_path_12.extname)(file2.originalname).toLowerCase(); - const stored = (0, node_crypto_1.createHash)("md5").update(`${Date.now()}${file2.originalname}${(0, node_crypto_1.randomUUID)()}`).digest("hex") + ext; - await (0, promises_1.mkdir)(this.dir, { recursive: true }); - await (0, promises_1.writeFile)((0, node_path_12.join)(this.dir, stored), file2.buffer); - return this.prisma.uploadedFile.create({ - data: { - url: `/uploads/${stored}`, - originalName: file2.originalname, - mimeType: file2.mimetype, - sizeBytes: file2.size, - purpose: purpose ?? null, - uploadedByAdminUserId: actorAdminUserId - } - }); - } - async findOne(id) { - const f = await this.prisma.uploadedFile.findUnique({ where: { id } }); - if (!f) - throw new common_12.NotFoundException("\u0641\u0627\u06CC\u0644 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F"); - return f; - } - /** True when the stored file is an image, for callers that care. */ - static isImage(mime) { - return AdminUploadsService_1.IMAGE_MIME.test(mime); - } - }; - exports2.AdminUploadsService = AdminUploadsService; - exports2.AdminUploadsService = AdminUploadsService = AdminUploadsService_1 = __decorate4([ - (0, common_12.Injectable)(), - __metadata3("design:paramtypes", [prisma_service_1.PrismaService]) - ], AdminUploadsService); - } -}); - -// dist/modules/admin-uploads/admin-uploads.controller.js -var require_admin_uploads_controller = __commonJS({ - "dist/modules/admin-uploads/admin-uploads.controller.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - var __metadata3 = exports2 && exports2.__metadata || function(k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); - }; - var __param3 = exports2 && exports2.__param || function(paramIndex, decorator) { - return function(target, key) { - decorator(target, key, paramIndex); - }; - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.AdminUploadsController = void 0; - var common_12 = require_common(); - var platform_express_1 = require_platform_express(); - var admin_uploads_service_1 = require_admin_uploads_service(); - var guards_1 = require_guards2(); - var common_2 = require_common(); - var AdminUploadsController = class AdminUploadsController { - uploads; - constructor(uploads) { - this.uploads = uploads; - } - create(file2, purpose, admin) { - return this.uploads.store(file2, purpose, admin.id ?? null); - } - findOne(id) { - return this.uploads.findOne(id); - } - }; - exports2.AdminUploadsController = AdminUploadsController; - __decorate4([ - (0, common_12.Post)("create"), - (0, common_12.HttpCode)(200), - (0, common_12.UseInterceptors)((0, platform_express_1.FileInterceptor)("file")), - __param3(0, (0, common_12.UploadedFile)()), - __param3(1, (0, common_2.Body)("purpose")), - __param3(2, (0, guards_1.CurrentAdmin)()), - __metadata3("design:type", Function), - __metadata3("design:paramtypes", [Object, Object, Object]), - __metadata3("design:returntype", void 0) - ], AdminUploadsController.prototype, "create", null); - __decorate4([ - (0, common_12.Get)(":id"), - __param3(0, (0, common_12.Param)("id")), - __metadata3("design:type", Function), - __metadata3("design:paramtypes", [String]), - __metadata3("design:returntype", void 0) - ], AdminUploadsController.prototype, "findOne", null); - exports2.AdminUploadsController = AdminUploadsController = __decorate4([ - (0, common_12.Controller)("admin/files"), - __metadata3("design:paramtypes", [admin_uploads_service_1.AdminUploadsService]) - ], AdminUploadsController); - } -}); - -// dist/modules/admin-uploads/public-uploads.controller.js -var require_public_uploads_controller = __commonJS({ - "dist/modules/admin-uploads/public-uploads.controller.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - var __metadata3 = exports2 && exports2.__metadata || function(k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); - }; - var __param3 = exports2 && exports2.__param || function(paramIndex, decorator) { - return function(target, key) { - decorator(target, key, paramIndex); - }; - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.PublicUploadsController = void 0; - var common_12 = require_common(); - var node_fs_1 = require("node:fs"); - var guards_1 = require_guards2(); - var upload_path_1 = require_upload_path(); - var PublicUploadsController = class PublicUploadsController { - stream(filename) { - const file2 = (0, upload_path_1.resolveStoredUpload)(filename); - if (!file2) - throw new common_12.NotFoundException("\u0641\u0627\u06CC\u0644 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F"); - return new common_12.StreamableFile((0, node_fs_1.createReadStream)(file2), { - type: (0, upload_path_1.mimeForUpload)(file2) ?? "application/octet-stream", - disposition: "inline" - }); - } - }; - exports2.PublicUploadsController = PublicUploadsController; - __decorate4([ - (0, common_12.Get)(":filename"), - (0, common_12.Header)("Cache-Control", "public, max-age=86400"), - __param3(0, (0, common_12.Param)("filename")), - __metadata3("design:type", Function), - __metadata3("design:paramtypes", [String]), - __metadata3("design:returntype", common_12.StreamableFile) - ], PublicUploadsController.prototype, "stream", null); - exports2.PublicUploadsController = PublicUploadsController = __decorate4([ - (0, guards_1.Public)(), - (0, common_12.Controller)("uploads") - ], PublicUploadsController); - } -}); - -// dist/modules/admin-uploads/admin-uploads.module.js -var require_admin_uploads_module = __commonJS({ - "dist/modules/admin-uploads/admin-uploads.module.js"(exports2) { - "use strict"; - var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - }; - Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.AdminUploadsModule = void 0; - var common_12 = require_common(); - var prisma_module_1 = require_prisma_module(); - var admin_uploads_controller_1 = require_admin_uploads_controller(); - var public_uploads_controller_1 = require_public_uploads_controller(); - var admin_uploads_service_1 = require_admin_uploads_service(); - var AdminUploadsModule = class AdminUploadsModule { - }; - exports2.AdminUploadsModule = AdminUploadsModule; - exports2.AdminUploadsModule = AdminUploadsModule = __decorate4([ - (0, common_12.Module)({ - imports: [prisma_module_1.PrismaModule], - controllers: [admin_uploads_controller_1.AdminUploadsController, public_uploads_controller_1.PublicUploadsController], - providers: [admin_uploads_service_1.AdminUploadsService], - exports: [admin_uploads_service_1.AdminUploadsService] - }) - ], AdminUploadsModule); - } -}); - // dist/modules/admin-dashboard/admin-dashboard.service.js var require_admin_dashboard_service = __commonJS({ "dist/modules/admin-dashboard/admin-dashboard.service.js"(exports2) { @@ -93138,6 +95678,281 @@ var require_admin_dashboard_module = __commonJS({ } }); +// dist/modules/sales-agent/sales-agent.service.js +var require_sales_agent_service = __commonJS({ + "dist/modules/sales-agent/sales-agent.service.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.SalesAgentService = void 0; + var common_12 = require_common(); + var client_1 = require("@prisma/client"); + var prisma_service_1 = require_prisma_service(); + var SalesAgentService = class SalesAgentService { + prisma; + constructor(prisma) { + this.prisma = prisma; + } + /** + * Only a SALES_EXPERT may use these routes. Every other admin role — an + * accountant, a super admin — gets a clean 403 rather than a half-working + * dashboard, because none of the reads below mean anything for them. + */ + assertIsSalesExpert(admin) { + if (admin.role !== "SALES_EXPERT") { + throw new common_12.ForbiddenException("\u0627\u06CC\u0646 \u0628\u062E\u0634 \u062A\u0646\u0647\u0627 \u0628\u0631\u0627\u06CC \u06A9\u0627\u0631\u0634\u0646\u0627\u0633 \u0641\u0631\u0648\u0634 \u0627\u0633\u062A."); + } + } + /** + * Total gold sold, credited to this agent. + * + * Legacy kept this denormalised in `customers.grams_sell`, recomputed by + * `CustomerController::updateGramSell` as + * SUM(orders.input) WHERE customer_id = X OR reagent_id = X + * with NO order-status filter. Computed live here instead of denormalised. + * + * Two deliberate fidelity notes: + * - The `OR customer_id = X` arm is structurally empty now: an agent is an + * AdminUser and cannot own an Order, so only the referred-customer arm can + * ever contribute. Kept as a comment rather than silently dropped. + * - No status filter, matching legacy. The admin panel disagrees with itself + * here — `AdminCustomersService.listReagents` aggregates unfiltered while + * `reagentDetail` filters on the SOLD statuses — so this matches + * listReagents (the grid figure) and legacy. If those three are ever + * reconciled, this is the fourth call site. + */ + async gramsSoldFor(agentId) { + const agg = await this.prisma.order.aggregate({ + where: { customer: { referredByAgentId: agentId } }, + _sum: { totalGoldWeightGrams: true } + }); + return agg._sum.totalGoldWeightGrams ?? new client_1.Prisma.Decimal(0); + } + /** + * The agent's own profile card. Replaces the balance card the customer apps + * show for a real customer, which an agent has no data for. + */ + async me(admin) { + this.assertIsSalesExpert(admin); + const agent = await this.prisma.adminUser.findUnique({ + where: { id: admin.id }, + select: { + id: true, + fullName: true, + mobile: true, + wagePercent: true, + branch: { select: { id: true, name: true } }, + _count: { select: { referredCustomers: true } } + } + }); + if (!agent) + throw new common_12.NotFoundException("\u06A9\u0627\u0631\u0634\u0646\u0627\u0633 \u0641\u0631\u0648\u0634 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F"); + return { + id: agent.id, + fullName: agent.fullName, + mobile: agent.mobile, + wagePercent: agent.wagePercent, + branch: agent.branch, + referredCustomerCount: agent._count.referredCustomers, + gramsSoldGrams: await this.gramsSoldFor(agent.id) + }; + } + /** + * The agent's referred customers — legacy `POST /my-child`. + * + * Search covers the same columns legacy's myChild did (name, mobile, phone, + * store_name, pasazh_name). + * + * NO isActive filter, deliberately: legacy had `Customer::active()` commented + * out on the line directly above the live query + * (ApiMobileController.php:187-188), and its picker renders a per-row + * activate/deactivate control, which only makes sense when inactive rows are + * listed. Archived customers are excluded though — legacy's own login path + * (WebApp\AuthController::userAuth) treats is_archive as disqualifying. + * + * The select is deliberately narrow. This is customer PII reached through an + * agent's token, so it carries only what the picker renders — no + * nationalCode, no passwordHash. + */ + async myCustomers(admin, search) { + this.assertIsSalesExpert(admin); + const term = search?.trim(); + const where = { + referredByAgentId: admin.id, + isArchived: false, + ...term ? { + OR: [ + { fullName: { contains: term, mode: "insensitive" } }, + { mobile: { contains: term } }, + { phone: { contains: term } }, + { storeName: { contains: term, mode: "insensitive" } }, + { pasazhName: { contains: term, mode: "insensitive" } } + ] + } : {} + }; + return this.prisma.customer.findMany({ + where, + orderBy: { createdAt: "desc" }, + select: { + id: true, + fullName: true, + mobile: true, + phone: true, + storeName: true, + address: true, + isActive: true, + goldDebtGrams: true, + fiatDebtRial: true + } + }); + } + /** + * Authorises entering "acting as this customer" mode and records it. + * + * The customer-facing endpoints are @Public() (a pre-existing condition — see + * customers.controller.ts), so this is not a hard security boundary: a + * determined caller could talk to those endpoints directly. What it does buy: + * + * 1. The SERVER decides who may be impersonated, for the legitimate flow — + * the client cannot widen its own book of business. + * 2. An audit row. Without it, staff could place gold-debt-incurring orders + * in a customer's name leaving no trace, which is not an acceptable + * property for a feature that writes to a ledger. Legacy had no such + * record; this is a deliberate improvement on the reference. + */ + async verifyActingAs(admin, customerId) { + this.assertIsSalesExpert(admin); + const customer = await this.prisma.customer.findUnique({ + where: { id: customerId }, + select: { id: true, fullName: true, referredByAgentId: true, isArchived: true } + }); + if (!customer || customer.isArchived || customer.referredByAgentId !== admin.id) { + throw new common_12.ForbiddenException("\u0627\u06CC\u0646 \u0645\u0634\u062A\u0631\u06CC \u062F\u0631 \u0641\u0647\u0631\u0633\u062A \u0634\u0645\u0627 \u0646\u06CC\u0633\u062A."); + } + await this.prisma.customerActivityLog.create({ + data: { + customerId: customer.id, + action: "agent.acting_as", + detail: `\u06A9\u0627\u0631\u0634\u0646\u0627\u0633 \u0641\u0631\u0648\u0634 ${admin.fullName} (${admin.id}) \u0628\u0647\u200C\u062C\u0627\u06CC \u0627\u06CC\u0646 \u0645\u0634\u062A\u0631\u06CC \u0648\u0627\u0631\u062F \u0634\u062F` + } + }); + return { id: customer.id, fullName: customer.fullName }; + } + }; + exports2.SalesAgentService = SalesAgentService; + exports2.SalesAgentService = SalesAgentService = __decorate4([ + (0, common_12.Injectable)(), + __metadata3("design:paramtypes", [prisma_service_1.PrismaService]) + ], SalesAgentService); + } +}); + +// dist/modules/sales-agent/sales-agent.controller.js +var require_sales_agent_controller = __commonJS({ + "dist/modules/sales-agent/sales-agent.controller.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + var __metadata3 = exports2 && exports2.__metadata || function(k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; + var __param3 = exports2 && exports2.__param || function(paramIndex, decorator) { + return function(target, key) { + decorator(target, key, paramIndex); + }; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.SalesAgentController = void 0; + var common_12 = require_common(); + var sales_agent_service_1 = require_sales_agent_service(); + var guards_1 = require_guards2(); + var SalesAgentController = class SalesAgentController { + salesAgent; + constructor(salesAgent) { + this.salesAgent = salesAgent; + } + me(admin) { + return this.salesAgent.me(admin); + } + myCustomers(admin, search) { + return this.salesAgent.myCustomers(admin, search); + } + verifyActingAs(admin, body) { + return this.salesAgent.verifyActingAs(admin, body.customerId); + } + }; + exports2.SalesAgentController = SalesAgentController; + __decorate4([ + (0, common_12.Get)("me"), + __param3(0, (0, guards_1.CurrentAdmin)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [Object]), + __metadata3("design:returntype", void 0) + ], SalesAgentController.prototype, "me", null); + __decorate4([ + (0, common_12.Get)("my-customers"), + __param3(0, (0, guards_1.CurrentAdmin)()), + __param3(1, (0, common_12.Query)("search")), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [Object, String]), + __metadata3("design:returntype", void 0) + ], SalesAgentController.prototype, "myCustomers", null); + __decorate4([ + (0, common_12.Post)("verify-acting-as"), + (0, common_12.HttpCode)(200), + __param3(0, (0, guards_1.CurrentAdmin)()), + __param3(1, (0, common_12.Body)()), + __metadata3("design:type", Function), + __metadata3("design:paramtypes", [Object, Object]), + __metadata3("design:returntype", void 0) + ], SalesAgentController.prototype, "verifyActingAs", null); + exports2.SalesAgentController = SalesAgentController = __decorate4([ + (0, common_12.Controller)("sales-agent"), + __metadata3("design:paramtypes", [sales_agent_service_1.SalesAgentService]) + ], SalesAgentController); + } +}); + +// dist/modules/sales-agent/sales-agent.module.js +var require_sales_agent_module = __commonJS({ + "dist/modules/sales-agent/sales-agent.module.js"(exports2) { + "use strict"; + var __decorate4 = exports2 && exports2.__decorate || function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + Object.defineProperty(exports2, "__esModule", { value: true }); + exports2.SalesAgentModule = void 0; + var common_12 = require_common(); + var sales_agent_service_1 = require_sales_agent_service(); + var sales_agent_controller_1 = require_sales_agent_controller(); + var SalesAgentModule = class SalesAgentModule { + }; + exports2.SalesAgentModule = SalesAgentModule; + exports2.SalesAgentModule = SalesAgentModule = __decorate4([ + (0, common_12.Module)({ + controllers: [sales_agent_controller_1.SalesAgentController], + providers: [sales_agent_service_1.SalesAgentService], + exports: [sales_agent_service_1.SalesAgentService] + }) + ], SalesAgentModule); + } +}); + // dist/app.module.js var require_app_module = __commonJS({ "dist/app.module.js"(exports2) { @@ -93154,6 +95969,11 @@ var require_app_module = __commonJS({ var prisma_module_1 = require_prisma_module(); var catalog_module_1 = require_catalog_module(); var customers_module_1 = require_customers_module(); + var customer_auth_module_1 = require_customer_auth_module(); + var sales_expert_app_module_1 = require_sales_expert_app_module(); + var customer_profile_module_1 = require_customer_profile_module(); + var customer_report_module_1 = require_customer_report_module(); + var sms_module_1 = require_sms_module(); var orders_module_1 = require_orders_module(); var accounting_module_1 = require_accounting_module(); var pricing_module_1 = require_pricing_module(); @@ -93175,6 +95995,7 @@ var require_app_module = __commonJS({ var admin_misc_module_1 = require_admin_misc_module(); var admin_uploads_module_1 = require_admin_uploads_module(); var admin_dashboard_module_1 = require_admin_dashboard_module(); + var sales_agent_module_1 = require_sales_agent_module(); var AppModule = class AppModule { }; exports2.AppModule = AppModule; @@ -93202,6 +96023,12 @@ var require_app_module = __commonJS({ admin_dashboard_module_1.AdminDashboardModule, catalog_module_1.CatalogModule, customers_module_1.CustomersModule, + sms_module_1.SmsModule, + customer_auth_module_1.CustomerAuthModule, + sales_expert_app_module_1.SalesExpertAppModule, + customer_profile_module_1.CustomerProfileModule, + customer_report_module_1.CustomerReportModule, + sales_agent_module_1.SalesAgentModule, orders_module_1.OrdersModule, accounting_module_1.AccountingModule, pricing_module_1.PricingModule @@ -93498,17 +96325,30 @@ var node_path_1 = require("node:path"); var app_module_1 = require_app_module(); var bootstrap_1 = require_bootstrap(); var prisma_exception_filter_1 = require_prisma_exception_filter(); +var sms_service_1 = require_sms_service(); +var env_1 = require_env(); +var customer_auth_service_1 = require_customer_auth_service(); var EXTRA_MIME_TYPES = { ".avif": "image/avif" }; async function bootstrap() { + (0, sms_service_1.assertSmsConfigured)(); + if (!(0, env_1.envOrNull)("JWT_CUSTOMER_SECRET")) { + const sharedWithAdmin = Boolean((0, env_1.envOrNull)("JWT_SECRET")); + console.warn("\n" + "=".repeat(72) + "\n \u26A0 JWT_CUSTOMER_SECRET \u062A\u0646\u0638\u06CC\u0645 \u0646\u0634\u062F\u0647 \u0627\u0633\u062A\n \u26A0 JWT_CUSTOMER_SECRET IS NOT SET.\n" + (sharedWithAdmin ? " \u26A0 Customer/expert app tokens are being signed with JWT_SECRET,\n \u26A0 the SAME key as admin tokens. The two token realms are no\n \u26A0 longer cryptographically separated.\n" : " \u26A0 Neither JWT_SECRET nor JWT_CUSTOMER_SECRET is set, so app\n \u26A0 tokens are signed with a well-known development string.\n") + " \u26A0 Set JWT_CUSTOMER_SECRET to a distinct random value before release.\n" + "=".repeat(72) + "\n"); + } + if ((0, customer_auth_service_1.masterOtpEnabled)()) { + const code = (0, customer_auth_service_1.masterOtpCode)() ?? ""; + const typable = /^\d{4}$/.test(code); + console.warn("\n" + "=".repeat(72) + "\n \u26A0 OTP_MASTER_CODE \u0641\u0639\u0627\u0644 \u0627\u0633\u062A \u2014 \u0642\u0628\u0644 \u0627\u0632 \u0627\u0646\u062A\u0634\u0627\u0631 \u0627\u0635\u0644\u06CC \u062D\u0630\u0641 \u0634\u0648\u062F\n \u26A0 OTP_MASTER_CODE IS ACTIVE: any active customer can sign in with\n \u26A0 this fixed code. Remove it from the environment before release.\n" + (typable ? "" : " \u26A0\n \u26A0 NOTE: this code is not 4 digits, so it CANNOT be entered in the\n \u26A0 customer apps \u2014 their OTP box has exactly 4 boxes. Use a\n \u26A0 4-digit code for the apps; this one works over the API only.\n") + "=".repeat(72) + "\n"); + } await (0, bootstrap_1.prepareDatabase)(); const app = await core_1.NestFactory.create(app_module_1.AppModule); app.setGlobalPrefix("api"); app.useGlobalPipes(new common_1.ValidationPipe({ whitelist: true, transform: true })); app.useGlobalFilters(new prisma_exception_filter_1.PrismaExceptionFilter()); app.enableCors(); - app.useStaticAssets(process.env.UPLOAD_DIR ?? (0, node_path_1.join)(process.cwd(), "storage", "uploads"), { + app.useStaticAssets((0, env_1.envOr)("UPLOAD_DIR", (0, node_path_1.join)(process.cwd(), "storage", "uploads")), { prefix: "/uploads/", index: false, dotfiles: "deny", @@ -93518,7 +96358,7 @@ async function bootstrap() { res.setHeader("Content-Type", mime); } }); - const port = Number(process.env.PORT ?? 3e3); + const port = Number((0, env_1.envOr)("PORT", "3000")); try { await app.listen(port, "0.0.0.0"); } catch (err) { diff --git a/apps/api/release/migrations/20260921090000_add_variant_image/migration.sql b/apps/api/release/migrations/20260921090000_add_variant_image/migration.sql new file mode 100644 index 0000000..ae407e7 --- /dev/null +++ b/apps/api/release/migrations/20260921090000_add_variant_image/migration.sql @@ -0,0 +1,11 @@ +-- AlterTable +ALTER TABLE "ProductVariant" ADD COLUMN "imageId" TEXT; + +-- CreateIndex +CREATE INDEX "ProductVariant_imageId_idx" ON "ProductVariant"("imageId"); + +-- AddForeignKey +-- ON DELETE SET NULL: removing a gallery image must never cascade into the +-- variant, because OrderItem rows reference variants and losing one would +-- break order history. The variant simply loses its picture. +ALTER TABLE "ProductVariant" ADD CONSTRAINT "ProductVariant_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "ProductImage"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/apps/api/release/migrations/20260921120000_agent_is_admin_user/migration.sql b/apps/api/release/migrations/20260921120000_agent_is_admin_user/migration.sql new file mode 100644 index 0000000..d52ab9a --- /dev/null +++ b/apps/api/release/migrations/20260921120000_agent_is_admin_user/migration.sql @@ -0,0 +1,36 @@ +-- Sales agents ("کارشناس فروش") are modelled as AdminUser rows +-- (role=SALES_EXPERT), not Customer rows — see admin-users.service.ts and +-- admin-customers.service.ts's listReagents(). ReagentCustomer.vue used to +-- post agent data at the Customer endpoints instead, so agents ended up as +-- Customer rows (kind SALES_AGENT), which then leaked into every +-- Customer-backed list, including "لیست مشتریان تک" (listSingle had no kind +-- filter at all). Fixed at the application layer alongside this migration; +-- this migration only adds what that fix needs: +-- +-- 1. CustomerKind.RETAIL — legacy kept retail ("تک") customers in an +-- entirely separate table (SingleCustomer/`login`) that could never +-- contain a wholesale/agent/shopkeeper/banker row. The redesigned +-- schema folded everything into one Customer model, so listSingle() +-- needs an explicit kind to filter on to reproduce that separation. +-- 2. AdminUser gets the handful of Customer-only columns +-- (ReagentCustomer.vue) that a sales agent's form actually collects: +-- avatar, address, store/pasazh name, member code. Only ever set when +-- role=SALES_EXPERT; every other AdminUser leaves them null. + +-- AlterEnum +ALTER TYPE "CustomerKind" ADD VALUE IF NOT EXISTS 'RETAIL'; + +-- AlterTable +ALTER TABLE "AdminUser" + ADD COLUMN "avatarFileId" TEXT, + ADD COLUMN "address" TEXT, + ADD COLUMN "storeName" TEXT, + ADD COLUMN "pasazhName" TEXT, + ADD COLUMN "memberCode" TEXT; + +-- CreateIndex +CREATE INDEX "AdminUser_avatarFileId_idx" ON "AdminUser"("avatarFileId"); + +-- AddForeignKey +ALTER TABLE "AdminUser" ADD CONSTRAINT "AdminUser_avatarFileId_fkey" + FOREIGN KEY ("avatarFileId") REFERENCES "UploadedFile"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/apps/api/release/migrations/20260922000000_customer_otp/migration.sql b/apps/api/release/migrations/20260922000000_customer_otp/migration.sql new file mode 100644 index 0000000..3db98fe --- /dev/null +++ b/apps/api/release/migrations/20260922000000_customer_otp/migration.sql @@ -0,0 +1,40 @@ +-- Customer OTP login challenges. +-- +-- Additive only: creates one new table and its indexes. No existing table, +-- column or row is touched. +-- +-- Ports henza_server's SMS login (ApiMobileController.php:39-78). The reference +-- kept the code in plain text on the customer row with no expiry and no attempt +-- limit; this stores a bcrypt hash and adds both, per docs/parity/DECISIONS.md +-- D6 / T1. + +CREATE TABLE IF NOT EXISTS "CustomerOtp" ( + "id" TEXT NOT NULL, + "mobile" TEXT NOT NULL, + "customerId" TEXT NOT NULL, + "codeHash" TEXT NOT NULL, + "expiresAt" TIMESTAMP(3) NOT NULL, + "attempts" INTEGER NOT NULL DEFAULT 0, + "consumedAt" TIMESTAMP(3), + "requestIp" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "CustomerOtp_pkey" PRIMARY KEY ("id") +); + +CREATE INDEX IF NOT EXISTS "CustomerOtp_mobile_createdAt_idx" + ON "CustomerOtp"("mobile", "createdAt"); +CREATE INDEX IF NOT EXISTS "CustomerOtp_customerId_idx" + ON "CustomerOtp"("customerId"); +CREATE INDEX IF NOT EXISTS "CustomerOtp_expiresAt_idx" + ON "CustomerOtp"("expiresAt"); + +DO $$ +BEGIN + ALTER TABLE "CustomerOtp" + ADD CONSTRAINT "CustomerOtp_customerId_fkey" + FOREIGN KEY ("customerId") REFERENCES "Customer"("id") + ON DELETE CASCADE ON UPDATE CASCADE; +EXCEPTION + WHEN duplicate_object THEN NULL; +END $$; diff --git a/apps/api/release/migrations/20260922120000_customer_name_parts/migration.sql b/apps/api/release/migrations/20260922120000_customer_name_parts/migration.sql new file mode 100644 index 0000000..60ecd93 --- /dev/null +++ b/apps/api/release/migrations/20260922120000_customer_name_parts/migration.sql @@ -0,0 +1,97 @@ +-- D0 — split Customer.fullName into firstName / lastName (ADDITIVE). +-- +-- Reference: henza_server has two separate columns from the start +-- database/migrations/2020_10_01_120430_customers_table.php:18-19 +-- $table->string('fname')->nullable(); +-- $table->string('lname')->nullable(); +-- The target collapsed them into one `fullName`, so the admin edit form shows +-- the whole name in the "first name" box and saves an empty last name +-- (admin-customers.service.ts:231-233 returns `fname: c.fullName, lname: ''`). +-- +-- NOTHING IS DROPPED. `fullName` stays and is kept in sync as +-- `firstName || ' ' || lastName`, so every existing reader keeps working +-- (D0 requirement 4). +-- +-- D13 / storefront impact: `Customer` is a SHARED table — both the B2B app and +-- the retail storefront read it. This migration only ADDS two nullable columns +-- and leaves `fullName` populated and authoritative, so no storefront query, +-- view or report changes behaviour. No storefront column is touched. + +-- 1. The two new columns. Nullable: a customer may legitimately have no +-- surname, and the reference's own columns are nullable. +ALTER TABLE "Customer" ADD COLUMN IF NOT EXISTS "firstName" TEXT; +ALTER TABLE "Customer" ADD COLUMN IF NOT EXISTS "lastName" TEXT; + +-- 2. Backfill: first whitespace-separated token -> firstName, the REST -> +-- lastName (D0 requirement 2). Multi-space and leading/trailing whitespace +-- are normalised first so " علی محمدی " splits correctly. +-- +-- Rows with a single token get firstName = that token and lastName = NULL. +-- Rows with more than two tokens put every remaining token in lastName; +-- they are ALSO listed in the review report (see the companion script) so a +-- human can correct them — the split cannot be guessed safely +-- (D0 requirement 3). +UPDATE "Customer" +SET + "firstName" = split_part(btrim(regexp_replace("fullName", '\s+', ' ', 'g')), ' ', 1), + "lastName" = NULLIF( + substr( + btrim(regexp_replace("fullName", '\s+', ' ', 'g')), + length(split_part(btrim(regexp_replace("fullName", '\s+', ' ', 'g')), ' ', 1)) + 2 + ), + '' + ) +WHERE "firstName" IS NULL + AND "fullName" IS NOT NULL + AND btrim("fullName") <> ''; + +-- 3. Keep `fullName` in sync from now on. +-- +-- Writers may update either the parts or the whole; this trigger makes the +-- parts authoritative whenever they are the thing that changed, and +-- otherwise leaves an explicit `fullName` write alone. That way old code +-- that still writes only `fullName` is not broken (D0 requirement 4). +-- Precedence when BOTH the parts and fullName change in the SAME statement: +-- THE PARTS WIN. fullName is rebuilt from them and the submitted fullName is +-- discarded. Rationale: after D0 the parts are the field the forms edit, so +-- they carry the operator's intent; fullName is the derived, legacy-facing +-- value. Choosing the opposite would silently undo an edit made in the two +-- boxes the operator actually typed into. +-- The first branch below tests the parts, so it wins by construction. +CREATE OR REPLACE FUNCTION customer_sync_full_name() RETURNS trigger AS $$ +BEGIN + -- The name parts changed (or are being set on insert): rebuild fullName. + -- This is checked FIRST, so when both change in one statement the parts win. + IF (TG_OP = 'INSERT' AND (NEW."firstName" IS NOT NULL OR NEW."lastName" IS NOT NULL)) + OR (TG_OP = 'UPDATE' + AND (NEW."firstName" IS DISTINCT FROM OLD."firstName" + OR NEW."lastName" IS DISTINCT FROM OLD."lastName")) THEN + NEW."fullName" := btrim( + coalesce(NEW."firstName", '') || ' ' || coalesce(NEW."lastName", '') + ); + + -- Only fullName changed: derive the parts from it, so the two never drift. + ELSIF TG_OP = 'UPDATE' AND NEW."fullName" IS DISTINCT FROM OLD."fullName" + AND NEW."fullName" IS NOT NULL AND btrim(NEW."fullName") <> '' THEN + NEW."firstName" := split_part(btrim(regexp_replace(NEW."fullName", '\s+', ' ', 'g')), ' ', 1); + NEW."lastName" := NULLIF( + substr( + btrim(regexp_replace(NEW."fullName", '\s+', ' ', 'g')), + length(split_part(btrim(regexp_replace(NEW."fullName", '\s+', ' ', 'g')), ' ', 1)) + 2 + ), + '' + ); + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +DROP TRIGGER IF EXISTS customer_sync_full_name_trg ON "Customer"; +CREATE TRIGGER customer_sync_full_name_trg + BEFORE INSERT OR UPDATE ON "Customer" + FOR EACH ROW EXECUTE FUNCTION customer_sync_full_name(); + +-- 4. Name search hits both parts in the admin grid. +CREATE INDEX IF NOT EXISTS "Customer_firstName_idx" ON "Customer" ("firstName"); +CREATE INDEX IF NOT EXISTS "Customer_lastName_idx" ON "Customer" ("lastName"); diff --git a/apps/api/release/migrations/20260922120100_customer_making_charge_offset/migration.sql b/apps/api/release/migrations/20260922120100_customer_making_charge_offset/migration.sql new file mode 100644 index 0000000..5d35556 --- /dev/null +++ b/apps/api/release/migrations/20260922120100_customer_making_charge_offset/migration.sql @@ -0,0 +1,49 @@ +-- D9 — add Customer.makingChargeOffsetPercent (ADDITIVE, behaviour-neutral). +-- +-- Reference: `customers.percent`, a real per-customer column. +-- database/migrations/2020_10_19_153228_customer_add.php:32 +-- $table->double('percent')->default(0); +-- +-- What it does in the reference: +-- * it is ADDED to the variant's making charge when the app builds an order +-- line: henza_pwa/src/Mixins/product/index.ts:69-75 +-- getWage(v) = myStore.state.percent + v.percentage_pay +-- and `state.percent` is this column, delivered by POST /me-data +-- (henza_pwa/src/store/myDataStore.ts:29,59,122-127). +-- * it shifts the catalogue making-charge filter bounds: +-- ApiMobileController.php:391-396 and :76-77. +-- * it is NOT part of the price formula itself — the extra percent there +-- comes from ConfigStatic['product_price_extra_percent'] +-- (ProductVariant.php:50,183). +-- +-- Why this name and not `productPercent`: the reference has three unrelated +-- things called `percent` (see docs/parity/MIGRATION-STATE.md). This is the +-- per-customer making-charge offset. It is mapped back to the legacy key +-- `percent` in toRow(), so the admin panel's field name does not change. +-- No duplicate field is created. +-- +-- The target currently reads `referredByAgent.wagePercent` instead, which is +-- the wrong field entirely (CUST-02) — that is corrected in the service layer, +-- not here. +-- +-- D13 / storefront impact: NONE. `customers.percent` is a B2B wholesale field; +-- it is consumed only by the B2B app's cart and catalogue filter. The +-- storefront's own percent is ConfigStatic['percent'] (the wallet cashback +-- rate), a different key in a different table, which this migration does not +-- touch. `Customer` is a shared table, but adding a column with a default that +-- reproduces today's behaviour changes no storefront read. + +-- DEFAULT 0 is deliberate and behaviour-neutral: +-- * it matches the reference's own column default (customer_add.php:32); +-- * with 0 the computed wage is `0 + variant.percentage_pay`, i.e. exactly +-- the making charge alone — which is what the target does today, since it +-- has no such column. So no existing customer's price moves. +-- NOTE: the reference's createCustomer() writes 3, not 0, for NEW customers +-- (AdminController.php:338) and makes the field required on create (:354). +-- That is an application-layer default for new rows, NOT a backfill value, +-- and it is handled in the service, not in this migration. +ALTER TABLE "Customer" + ADD COLUMN IF NOT EXISTS "makingChargeOffsetPercent" DECIMAL(6,3) NOT NULL DEFAULT 0; + +COMMENT ON COLUMN "Customer"."makingChargeOffsetPercent" IS + 'Legacy customers.percent — per-customer making-charge offset, ADDED to ProductVariant.defaultWagePercent when pricing an order line, and used to shift the catalogue making-charge filter bounds. Not the wallet cashback rate (that is the storefront ConfigStatic[percent]) and not the wholesale per-line charge (that is OrderItem.wagePercentSnapshot).'; diff --git a/apps/api/release/migrations/20260922180000_sales_expert_profile_and_admin_name_parts/migration.sql b/apps/api/release/migrations/20260922180000_sales_expert_profile_and_admin_name_parts/migration.sql new file mode 100644 index 0000000..fe6a087 --- /dev/null +++ b/apps/api/release/migrations/20260922180000_sales_expert_profile_and_admin_name_parts/migration.sql @@ -0,0 +1,180 @@ +-- D17 — SalesExpertProfile, and firstName/lastName on AdminUser. +-- +-- Two additive changes in one migration: +-- +-- 1. SalesExpertProfile — the sales-expert fields that legacy kept on the +-- `customers` row (type=1) and that were lost when a sales expert became +-- an AdminUser. A separate table, not columns on AdminUser, because these +-- are meaningful only for role = SALES_EXPERT and would be permanently +-- null for every other admin. +-- +-- 2. AdminUser.firstName / lastName — closes D0 requirement 7, applying the +-- same split the Customer table already got. For ALL admins, not only +-- sales experts. +-- +-- NOTHING IS DROPPED. `fullName` stays and is kept in sync by a trigger. +-- +-- IDEMPOTENT (D14/D15): this must run cleanly under BOTH paths — the Prisma CLI +-- in development, and the app's own runner (src/prisma/migrate.ts) at container +-- boot, which may replay it. Hence IF NOT EXISTS everywhere, CREATE OR REPLACE +-- for the function, DROP ... IF EXISTS before CREATE TRIGGER, and a guarded +-- backfill that only touches rows not already filled. +-- +-- D13 / storefront impact: NONE. AdminUser is an admin-side table the retail +-- storefront never reads, and SalesExpertProfile is new. `customers` is not +-- touched. + +-- ─────────────────────────────────────────────────────────────────────────── +-- 1. SalesExpertProfile +-- ─────────────────────────────────────────────────────────────────────────── + +CREATE TABLE IF NOT EXISTS "SalesExpertProfile" ( + "id" TEXT NOT NULL, + "adminUserId" TEXT NOT NULL, + -- legacy customers.city_id — a filterable column on the sales-expert grid + -- (ReagentListModel.js:14) AND the source of the commission autofill + -- (ReagentCustomer.vue:215-228). + "cityId" TEXT, + -- legacy customers.gram_level — the «سطح اعتبار» column, filterable + -- (ReagentListModel.js:17). + -- + -- DECIMAL(14,4) deliberately matches Customer.goldCreditLimitGrams exactly + -- (schema.prisma:221, verified numeric_precision=14 numeric_scale=4), so the + -- same credit limit has identical precision and rendering on both tables. + "gramLevel" DECIMAL(14,4) NOT NULL DEFAULT 0, + -- legacy customers.phone / ncode — collected by the reference form + -- (ReagentCustomer.vue:95,31) and stored, though nothing reads them back. + -- Added on the owner's instruction so the form matches the reference. + -- + -- Types and constraints copied from the reference, which is deliberately lax: + -- ncode : string(20), NULLABLE, **NOT UNIQUE** + -- (2020_10_01_120430_customers_table.php:22) + -- phone : string(15), NULLABLE, not unique + -- (2020_10_25_155247_customers_cols.php:17) + -- Neither has a server-side validation rule: createCustomer validates only + -- `mobile` (required|unique) and `percent` (required) — AdminController.php:354. + -- Client-side both are `required: false` (ReagentCustomer.vue:31,95). + -- No uniqueness is invented here; that would reject data the reference accepts. + "phone" VARCHAR(15), + "nationalCode" VARCHAR(20), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "SalesExpertProfile_pkey" PRIMARY KEY ("id") +); + +-- One profile per admin user. +CREATE UNIQUE INDEX IF NOT EXISTS "SalesExpertProfile_adminUserId_key" + ON "SalesExpertProfile" ("adminUserId"); + +-- The grid filters on city. +CREATE INDEX IF NOT EXISTS "SalesExpertProfile_cityId_idx" + ON "SalesExpertProfile" ("cityId"); + +DO $$ +BEGIN + ALTER TABLE "SalesExpertProfile" + ADD CONSTRAINT "SalesExpertProfile_adminUserId_fkey" + FOREIGN KEY ("adminUserId") REFERENCES "AdminUser"("id") + ON DELETE CASCADE ON UPDATE CASCADE; +EXCEPTION + WHEN duplicate_object THEN NULL; +END $$; + +DO $$ +BEGIN + ALTER TABLE "SalesExpertProfile" + ADD CONSTRAINT "SalesExpertProfile_cityId_fkey" + FOREIGN KEY ("cityId") REFERENCES "City"("id") + ON DELETE SET NULL ON UPDATE CASCADE; +EXCEPTION + WHEN duplicate_object THEN NULL; +END $$; + +-- Give every EXISTING sales expert a profile row, so the grid's city and +-- credit-limit columns have something to left-join to from day one. +-- +-- Idempotent by construction: `WHERE NOT EXISTS` means a replay inserts +-- nothing, and it never touches a profile an operator has already filled in. +-- +-- The id is derived from the AdminUser id rather than random, so a replay on a +-- database where the row was deleted recreates the same key instead of +-- accumulating duplicates. Values are the column defaults: gramLevel 0 and +-- nulls elsewhere — deliberately neutral, since the reference has no data to +-- migrate here and inventing a credit limit would change behaviour. +INSERT INTO "SalesExpertProfile" ("id", "adminUserId", "gramLevel", "createdAt", "updatedAt") +SELECT 'sep_' || a."id", a."id", 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP +FROM "AdminUser" a +WHERE a."role" = 'SALES_EXPERT' + AND NOT EXISTS ( + SELECT 1 FROM "SalesExpertProfile" s WHERE s."adminUserId" = a."id" + ); + +-- ─────────────────────────────────────────────────────────────────────────── +-- 2. AdminUser.firstName / lastName (D0 requirement 7) +-- ─────────────────────────────────────────────────────────────────────────── + +ALTER TABLE "AdminUser" ADD COLUMN IF NOT EXISTS "firstName" TEXT; +ALTER TABLE "AdminUser" ADD COLUMN IF NOT EXISTS "lastName" TEXT; + +-- Backfill: first whitespace-separated token -> firstName, the REST -> +-- lastName. Identical rule to the Customer migration, so both tables split the +-- same way. Rows with more than two tokens are a guess and are listed by +-- scripts/report-name-split.cjs for a human to correct. +-- +-- `WHERE "firstName" IS NULL` makes the replay a no-op and, just as important, +-- prevents a re-run from overwriting a name a human has since corrected. +UPDATE "AdminUser" +SET + "firstName" = split_part(btrim(regexp_replace("fullName", '\s+', ' ', 'g')), ' ', 1), + "lastName" = NULLIF( + substr( + btrim(regexp_replace("fullName", '\s+', ' ', 'g')), + length(split_part(btrim(regexp_replace("fullName", '\s+', ' ', 'g')), ' ', 1)) + 2 + ), + '' + ) +WHERE "firstName" IS NULL + AND "fullName" IS NOT NULL + AND btrim("fullName") <> ''; + +-- Same two-way sync as Customer. +-- +-- Precedence when BOTH the parts and fullName change in ONE statement: +-- THE PARTS WIN — fullName is rebuilt from them and the submitted fullName is +-- discarded. The parts are what the forms edit, so they carry the operator's +-- intent; fullName is the derived, legacy-facing value. The first branch tests +-- the parts, so it wins by construction. +CREATE OR REPLACE FUNCTION admin_user_sync_full_name() RETURNS trigger AS $$ +BEGIN + IF (TG_OP = 'INSERT' AND (NEW."firstName" IS NOT NULL OR NEW."lastName" IS NOT NULL)) + OR (TG_OP = 'UPDATE' + AND (NEW."firstName" IS DISTINCT FROM OLD."firstName" + OR NEW."lastName" IS DISTINCT FROM OLD."lastName")) THEN + NEW."fullName" := btrim( + coalesce(NEW."firstName", '') || ' ' || coalesce(NEW."lastName", '') + ); + + ELSIF TG_OP = 'UPDATE' AND NEW."fullName" IS DISTINCT FROM OLD."fullName" + AND NEW."fullName" IS NOT NULL AND btrim(NEW."fullName") <> '' THEN + NEW."firstName" := split_part(btrim(regexp_replace(NEW."fullName", '\s+', ' ', 'g')), ' ', 1); + NEW."lastName" := NULLIF( + substr( + btrim(regexp_replace(NEW."fullName", '\s+', ' ', 'g')), + length(split_part(btrim(regexp_replace(NEW."fullName", '\s+', ' ', 'g')), ' ', 1)) + 2 + ), + '' + ); + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +DROP TRIGGER IF EXISTS admin_user_sync_full_name_trg ON "AdminUser"; +CREATE TRIGGER admin_user_sync_full_name_trg + BEFORE INSERT OR UPDATE ON "AdminUser" + FOR EACH ROW EXECUTE FUNCTION admin_user_sync_full_name(); + +-- Name search and sort hit both parts. +CREATE INDEX IF NOT EXISTS "AdminUser_firstName_idx" ON "AdminUser" ("firstName"); +CREATE INDEX IF NOT EXISTS "AdminUser_lastName_idx" ON "AdminUser" ("lastName"); diff --git a/apps/api/release/migrations/20260923120000_sales_expert_trading_customer/migration.sql b/apps/api/release/migrations/20260923120000_sales_expert_trading_customer/migration.sql new file mode 100644 index 0000000..b1745ee --- /dev/null +++ b/apps/api/release/migrations/20260923120000_sales_expert_trading_customer/migration.sql @@ -0,0 +1,52 @@ +-- Sales-expert app login: the expert's own trading account. +-- +-- WHY THIS COLUMN EXISTS +-- ---------------------- +-- In the reference a sales agent WAS a Customer row (customers.type = 1), so +-- when the agent placed an order from the app, setOrder did +-- $customer = Customer::findOrFail(auth()->id()); +-- $gramLevel = $customer->gram_level; +-- (ApiMobileController.php:534-535) — the order was simply that agent's own +-- order, against that agent's own credit limit and ledger. +-- +-- This system models a sales expert as an AdminUser (role SALES_EXPERT, D17), +-- which has no ledger, no gold debt and no credit limit. An AdminUser therefore +-- cannot be the subject of an Order: Order.customerId is a required FK to +-- Customer, and every ledger entry hangs off a Customer. +-- +-- Rather than reverse D17 (the owner ruled the expert stays an AdminUser), each +-- expert gets ONE linked Customer row that represents only their own trading +-- position. Orders the expert places for themselves land on that row, exactly +-- as the reference's single row did. The expert's REFERRED customers remain +-- separate rows pointing at the AdminUser via Customer.referredByAgentId — +-- that relationship is unchanged. +-- +-- PURELY ADDITIVE +-- --------------- +-- One nullable column plus a unique index. No existing row is modified, no +-- data is backfilled and nothing is deleted. A profile with a NULL +-- tradingCustomerId simply has no trading account yet; the API creates it +-- lazily on first login, so existing experts keep working untouched. + +ALTER TABLE "SalesExpertProfile" + ADD COLUMN IF NOT EXISTS "tradingCustomerId" TEXT; + +COMMENT ON COLUMN "SalesExpertProfile"."tradingCustomerId" IS + 'The Customer row that carries this expert''s OWN orders, debt and credit limit. Their referred customers are separate rows linked by Customer.referredByAgentId. Null until the expert first signs into the app, when it is created from the profile (gramLevel -> goldCreditLimitGrams, wagePercent -> makingChargeOffsetPercent).'; + +-- One trading account per expert, and no two experts may share one. +CREATE UNIQUE INDEX IF NOT EXISTS "SalesExpertProfile_tradingCustomerId_key" + ON "SalesExpertProfile" ("tradingCustomerId"); + +-- SET NULL, not CASCADE: if the trading Customer is ever removed, the expert +-- and their profile must survive — deleting a customer must never delete an +-- admin account. +DO $$ +BEGIN + ALTER TABLE "SalesExpertProfile" + ADD CONSTRAINT "SalesExpertProfile_tradingCustomerId_fkey" + FOREIGN KEY ("tradingCustomerId") REFERENCES "Customer"("id") + ON DELETE SET NULL ON UPDATE CASCADE; +EXCEPTION + WHEN duplicate_object THEN NULL; +END $$; diff --git a/apps/api/release/vendor/.prisma/client/edge.js b/apps/api/release/vendor/.prisma/client/edge.js index 7b1c9bb..1e32528 100644 --- a/apps/api/release/vendor/.prisma/client/edge.js +++ b/apps/api/release/vendor/.prisma/client/edge.js @@ -101,6 +101,8 @@ exports.Prisma.CityScalarFieldEnum = { exports.Prisma.AdminUserScalarFieldEnum = { id: 'id', fullName: 'fullName', + firstName: 'firstName', + lastName: 'lastName', mobile: 'mobile', passwordHash: 'passwordHash', role: 'role', @@ -108,12 +110,31 @@ exports.Prisma.AdminUserScalarFieldEnum = { branchId: 'branchId', wagePercent: 'wagePercent', isActive: 'isActive', - createdAt: 'createdAt' + createdAt: 'createdAt', + avatarFileId: 'avatarFileId', + address: 'address', + storeName: 'storeName', + pasazhName: 'pasazhName', + memberCode: 'memberCode' +}; + +exports.Prisma.SalesExpertProfileScalarFieldEnum = { + id: 'id', + adminUserId: 'adminUserId', + cityId: 'cityId', + gramLevel: 'gramLevel', + phone: 'phone', + nationalCode: 'nationalCode', + tradingCustomerId: 'tradingCustomerId', + createdAt: 'createdAt', + updatedAt: 'updatedAt' }; exports.Prisma.CustomerScalarFieldEnum = { id: 'id', fullName: 'fullName', + firstName: 'firstName', + lastName: 'lastName', mobile: 'mobile', nationalCode: 'nationalCode', passwordHash: 'passwordHash', @@ -129,6 +150,7 @@ exports.Prisma.CustomerScalarFieldEnum = { goldCreditLimitGrams: 'goldCreditLimitGrams', goldDebtGrams: 'goldDebtGrams', fiatDebtRial: 'fiatDebtRial', + makingChargeOffsetPercent: 'makingChargeOffsetPercent', verificationStep: 'verificationStep', nationalIdImageUrl: 'nationalIdImageUrl', businessLicenseUrl: 'businessLicenseUrl', @@ -224,7 +246,8 @@ exports.Prisma.ProductVariantScalarFieldEnum = { defaultWagePercent: 'defaultWagePercent', qty: 'qty', isPreOrder: 'isPreOrder', - isActive: 'isActive' + isActive: 'isActive', + imageId: 'imageId' }; exports.Prisma.OrderScalarFieldEnum = { @@ -459,6 +482,18 @@ exports.Prisma.UploadedFileScalarFieldEnum = { createdAt: 'createdAt' }; +exports.Prisma.CustomerOtpScalarFieldEnum = { + id: 'id', + mobile: 'mobile', + customerId: 'customerId', + codeHash: 'codeHash', + expiresAt: 'expiresAt', + attempts: 'attempts', + consumedAt: 'consumedAt', + requestIp: 'requestIp', + createdAt: 'createdAt' +}; + exports.Prisma.SortOrder = { asc: 'asc', desc: 'desc' @@ -496,7 +531,8 @@ exports.CustomerKind = exports.$Enums.CustomerKind = { APP_SHOPKEEPER: 'APP_SHOPKEEPER', WALK_IN_SHOPKEEPER: 'WALK_IN_SHOPKEEPER', BANKER: 'BANKER', - WORKSHOP_SUPERVISOR: 'WORKSHOP_SUPERVISOR' + WORKSHOP_SUPERVISOR: 'WORKSHOP_SUPERVISOR', + RETAIL: 'RETAIL' }; exports.VerificationStep = exports.$Enums.VerificationStep = { @@ -589,6 +625,7 @@ exports.MessageAudience = exports.$Enums.MessageAudience = { exports.Prisma.ModelName = { City: 'City', AdminUser: 'AdminUser', + SalesExpertProfile: 'SalesExpertProfile', Customer: 'Customer', CustomerAddress: 'CustomerAddress', Branch: 'Branch', @@ -621,7 +658,8 @@ exports.Prisma.ModelName = { NotificationRecipient: 'NotificationRecipient', NewsletterSubscriber: 'NewsletterSubscriber', CustomerActivityLog: 'CustomerActivityLog', - UploadedFile: 'UploadedFile' + UploadedFile: 'UploadedFile', + CustomerOtp: 'CustomerOtp' }; /** * Create the Client @@ -634,7 +672,7 @@ const config = { "value": "prisma-client-js" }, "output": { - "value": "D:\\APP\\gold-bank\\node_modules\\.pnpm\\@prisma+client@5.22.0_prisma@5.22.0\\node_modules\\@prisma\\client", + "value": "E:\\gold bank\\gold-banking-monorepo\\node_modules\\.pnpm\\@prisma+client@5.22.0_prisma@5.22.0\\node_modules\\@prisma\\client", "fromEnvVar": null }, "config": { @@ -652,10 +690,11 @@ const config = { } ], "previewFeatures": [], - "sourceFilePath": "D:\\APP\\gold-bank\\apps\\api\\prisma\\schema.prisma" + "sourceFilePath": "E:\\gold bank\\gold-banking-monorepo\\apps\\api\\prisma\\schema.prisma" }, "relativeEnvPaths": { - "rootEnvPath": null + "rootEnvPath": null, + "schemaEnvPath": "../../../../../../apps/api/.env" }, "relativePath": "../../../../../../apps/api/prisma", "clientVersion": "5.22.0", @@ -673,13 +712,13 @@ 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; 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\n/// Legacy Customer.type — what kind of wholesale customer this is. Values and\n/// labels taken verbatim from bulkCustomer.vue's userTypes list.\nenum CustomerKind {\n SALES_AGENT // 1 کارشناس فروش\n APP_SHOPKEEPER // 2 مغازه دار اپلیکیشن\n WALK_IN_SHOPKEEPER // 3 مغازه دار حضوری\n BANKER // 4 بنکدار\n WORKSHOP_SUPERVISOR // 5 سرپرست کارگاه\n}\n\nenum VerificationStep {\n UNVERIFIED\n STEP_ONE_SUBMITTED\n STEP_TWO_SUBMITTED\n VERIFIED\n}\n\n/// Legacy kept retail and wholesale orders in two SEPARATE tables\n/// (orders_orders vs orders). The redesigned schema has one Order model, so the\n/// distinction lives here instead.\nenum OrderChannel {\n RETAIL // تک فروشی\n WHOLESALE // عمده فروشی\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 /// Legacy cities.percent / percent_aux — per-city wage surcharges the city\n /// screen edits through GET cities/get-percent/{id} and get-percent-aux/{id}.\n percent Decimal? @db.Decimal(6, 3)\n percentAux Decimal? @db.Decimal(6, 3)\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 /// Legacy `users.ut`. Granular ACL lives here (Permission rows hang off UserType);\n /// the coarse `role` enum above is retained for the business rules that already\n /// switch on it. Nullable so existing seeded admins remain valid.\n userTypeId Int?\n userType UserType? @relation(fields: [userTypeId], references: [id])\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 supervisedWorkshops Workshop[] @relation(\"WorkshopSupervisor\")\n branchAllocations BranchAllocation[] @relation(\"AllocationAuthor\")\n postedLedgerEntries LedgerEntry[]\n orderStatusEvents OrderStatusEvent[]\n preOrderStatusEvents PreOrderStatusEvent[]\n sentSmsMessages SmsMessage[] @relation(\"SmsAuthor\")\n sentNotifications Notification[] @relation(\"NotificationAuthor\")\n uploadedFiles UploadedFile[] @relation(\"FileUploader\")\n\n @@index([role])\n @@index([userTypeId])\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 /// Legacy Customer.type — distinguishes بنکدار from مغازه دار, which the\n /// BankerList / ShopkeeperList screens filter on.\n kind CustomerKind @default(APP_SHOPKEEPER)\n\n /// Legacy contact + shop columns, shown on the wholesale customer form and as\n /// (hidden-by-default) columns in its grid.\n phone String?\n address String?\n storeName String?\n pasazhName String?\n zone String?\n /// Legacy mcode — the operator-assigned customer code.\n memberCode String?\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 /// Legacy `image_id` — the profile photo (عکس پروفایل) on the sales-agent\n /// form. Legacy stored an FK to its Image table and read it back as a nested\n /// `image: { id, image }`, so this is a relation, not a bare URL string.\n avatarFileId String?\n avatarFile UploadedFile? @relation(\"CustomerAvatar\", fields: [avatarFileId], references: [id])\n\n /// Legacy had TWO independent switches on a customer:\n /// sts -> may they place orders (this field)\n /// active -> is the account enabled (isActive, below)\n /// The port previously drove both from isActive; they are separate again.\n canPlaceOrders Boolean @default(true)\n\n isActive Boolean @default(true)\n /// Legacy archive flag — the archived list filtered on this, not on isActive.\n isArchived Boolean @default(false)\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 comments Comment[]\n promotions Promotion[]\n notifications NotificationRecipient[]\n activityLogs CustomerActivityLog[]\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 /// Legacy Branch.address / phone — both are columns in the branch grid.\n address String?\n phone String?\n /// Legacy GET branch/active/{id} toggles this.\n isActive Boolean @default(true)\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n adminUsers AdminUser[]\n orders Order[]\n allocations BranchAllocation[]\n}\n\nmodel Workshop {\n id String @id @default(cuid())\n name String\n /// Legacy Workshop.address / phone — both are columns in the workshop grid.\n address String?\n phone String?\n /// The supervisor created alongside the workshop by the legacy Add form.\n supervisorId String?\n supervisor AdminUser? @relation(\"WorkshopSupervisor\", fields: [supervisorId], references: [id])\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 /// Legacy Category.type — which storefront the category belongs to:\n /// 1 سایت | 2 اپلیکشن موبایل. Drives the \"type_str\" grid column.\n type Int @default(1)\n /// Legacy Category.image / icon — the grid renders the image, the storefront the icon.\n imageUrl String?\n iconUrl String?\n /// Legacy meta tag + description, used for SEO on the site categories.\n metaTag String?\n description String?\n parentId String?\n parent Category? @relation(\"CategoryTree\", fields: [parentId], references: [id])\n children Category[] @relation(\"CategoryTree\")\n products Product[]\n promotions Promotion[]\n}\n\nmodel Brand {\n id String @id @default(cuid())\n title String\n products Product[]\n promotions Promotion[]\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 /// Legacy Product.product_code — the human-facing SKU shown in the grid.\n productCode String? @unique\n /// Legacy Product.show_type: 1 site, 2 mobile, 3 both.\n showType Int @default(3)\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n variants ProductVariant[]\n images ProductImage[]\n collections CollectionProduct[]\n comments Comment[]\n promotions Promotion[]\n allocationItems BranchAllocationItem[]\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 /// Legacy had 4 distinct upload slots (تصویر شاخص / تصویر شاخص موبایل /\n /// گالری تصاویر سایت / گالری تصاویر موبایل) — GALLERY is the default so\n /// existing rows and any generic \"just attach an image\" caller keep working.\n role ProductImageRole @default(GALLERY)\n}\n\nenum ProductImageRole {\n FEATURED\n FEATURED_MOBILE\n GALLERY\n GALLERY_MOBILE\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 /// Legacy ProductVariant.qty — on-hand stock. The cart refuses a variant with\n /// qty 0 unless it is flagged pre-order, so both fields are required for that\n /// guard to work (see apps/admin-panel/src/stores/cart.ts).\n qty Int @default(0)\n /// Legacy is_pre_order: orderable even at zero stock.\n isPreOrder Boolean @default(false)\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 /// Which sales channel placed this order — see OrderChannel.\n channel OrderChannel @default(RETAIL)\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 /// Instrument metadata from the legacy \"cash\" document form. Which fields are\n /// filled depends on sourceType: a melted-gold receipt carries lab/hallmark and\n /// the pre-normalization weight, a cheque carries the account and bank, and a\n /// transfer carries its destination.\n /// Legacy columns: lab, ang, account_name, bank_name, to, original_value, recive_date.\n labName String?\n hallmark String?\n accountName String?\n bankName String?\n transferTo String?\n /// Weight as handed over, BEFORE karat normalization (legacy original_value).\n originalValue Decimal? @db.Decimal(14, 4)\n /// Rial amount for cash/cheque documents (legacy price).\n priceRial Decimal? @db.Decimal(20, 2)\n receivedAt DateTime?\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\n// ─────────────────────────────────────────────────────────────────────────\n// Admin ACL — faithful port of the legacy granular permission model.\n//\n// Legacy (henza_server):\n// Schema::create('permission', function (Blueprint $table) {\n// $table->string('rid');\n// $table->integer('ut');\n// $table->primary(['rid' , 'ut']);\n// });\n//\n// Permissions attach to the USER TYPE (`ut`), never to an individual user, so\n// every admin of a given type shares one permission set. `ut` pointed at a row\n// in the legacy `config_static` lookup table; UserType is that table, narrowed\n// to the admin-user-type rows it was actually used for.\n//\n// A `rid` is a dotted permission id such as \"persons.list.client.omde\". The\n// frontend expands each stored rid into its ancestors at runtime via\n// checkParent() — see apps/admin-panel/src/stores/users.ts. Only the 58 leaf\n// rids the routers actually gate are seeded; ancestors are derived, not stored.\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel UserType {\n /// Legacy `permission.ut` / `users.ut` — an integer, kept as the PK so seeded\n /// rows keep their legacy identity rather than getting new cuids.\n id Int @id\n title String\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n permissions Permission[]\n adminUsers AdminUser[]\n\n @@map(\"user_type\")\n}\n\nmodel Permission {\n /// Dotted permission id, e.g. \"order.list\", \"persons.list.client.omde\".\n rid String\n userTypeId Int\n userType UserType @relation(fields: [userTypeId], references: [id], onDelete: Cascade)\n\n /// Legacy's composite primary key (rid, ut), preserved exactly.\n @@id([rid, userTypeId])\n @@index([userTypeId])\n @@map(\"permission\")\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Product comments / reviews — legacy `comment` table (App\\Models\\Comment),\n// moderated from views/customer/Comments/Comments.vue.\n//\n// Legacy fillable: product_id, comment, reply_to, sts, customer_id\n// Legacy Comment::$STS = [1 => 'در انتظار تایید', 2 => 'تایید شده', 3 => 'رد شده']\n// which is represented here as an enum rather than a magic integer.\n// ─────────────────────────────────────────────────────────────────────────\n\nenum CommentStatus {\n PENDING // 1 — در انتظار تایید\n APPROVED // 2 — تایید شده\n REJECTED // 3 — رد شده\n}\n\nmodel Comment {\n id String @id @default(cuid())\n productId String\n product Product @relation(fields: [productId], references: [id])\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n body String\n status CommentStatus @default(PENDING)\n\n /// Legacy `reply_to`: a staff reply threads under the comment it answers.\n replyToId String?\n replyTo Comment? @relation(\"CommentReplies\", fields: [replyToId], references: [id])\n replies Comment[] @relation(\"CommentReplies\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([productId])\n @@index([customerId])\n @@index([status])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Settings — replaces legacy `config_static`, a key/value table the admin\n// panel read and wrote through endpoints like /wallet/config and\n// /const/__list__ut. Values are stored as JSON so a setting can hold a scalar,\n// an object, or the dynamic option lists legacy kept under `__list__*` keys.\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel Setting {\n key String @id\n value Json\n updatedAt DateTime @updatedAt\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Promotions / discounts — legacy views/promotion/*, served by\n// PromotionController (`product/variant/promotion`). The redesigned schema had\n// no promotion concept at all; this models what the legacy forms actually\n// collect.\n// ─────────────────────────────────────────────────────────────────────────\n\n/// Legacy Promotion.type — what the discount is scoped to.\nenum PromotionScope {\n PRODUCT // 1 محصولات\n CATEGORY // 2 دسته بندی\n CUSTOMER // 3 مشتری\n BRAND // 4 برند\n}\n\n/// Legacy Promotion.use_only — how often a customer may redeem it.\nenum PromotionUsage {\n ONCE // 1 فقط یکبار\n UNTIL_END_DATE // 2 تا تاریخ پایان\n}\n\nmodel Promotion {\n id String @id @default(cuid())\n title String\n description String?\n scope PromotionScope @default(PRODUCT)\n usage PromotionUsage @default(ONCE)\n /// Percentage off, 0-100 (legacy discount_percent).\n discountPercent Decimal @db.Decimal(6, 3)\n isActive Boolean @default(true)\n /// Which storefront it shows on, mirroring Product.showType (legacy platform).\n platform Int @default(3)\n\n startsAt DateTime?\n endsAt DateTime?\n\n /// Exactly one of these is populated, per `scope`.\n productId String?\n product Product? @relation(fields: [productId], references: [id])\n categoryId String?\n category Category? @relation(fields: [categoryId], references: [id])\n customerId String?\n customer Customer? @relation(fields: [customerId], references: [id])\n brandId String?\n brand Brand? @relation(fields: [brandId], references: [id])\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([scope, isActive])\n}\n\n/// Legacy \"ignore\" table (views/offersIgnore) — specific products, categories,\n/// customers or brands excluded from the automatic discount engine.\n/// Legacy posts { type, item_id[] }: \"type\" reuses the promotion scope codes\n/// (1 محصولات | 2 دسته بندی | 3 مشتری | 4 برند) and \"itemId\" is the excluded row.\nmodel PromotionExclusion {\n id String @id @default(cuid())\n type Int\n itemId String\n createdAt DateTime @default(now())\n\n @@unique([type, itemId])\n @@index([type])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Branch stock allocation — legacy views/invoice/* (\"فاکتور انتقال\"), served by\n// BranchController as `branch/product`. Despite the screen being called an\n// invoice, it is not a sales document: it records which products head office\n// allocated to a branch, and who allocated them.\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel BranchAllocation {\n id String @id @default(cuid())\n branchId String\n branch Branch @relation(fields: [branchId], references: [id])\n\n /// Legacy `allocator_user` — the admin who filed the allocation.\n allocatorAdminUserId String?\n allocatorAdminUser AdminUser? @relation(\"AllocationAuthor\", fields: [allocatorAdminUserId], references: [id])\n\n note String?\n createdAt DateTime @default(now())\n\n items BranchAllocationItem[]\n\n @@index([branchId])\n}\n\nmodel BranchAllocationItem {\n id String @id @default(cuid())\n allocationId String\n allocation BranchAllocation @relation(fields: [allocationId], references: [id], onDelete: Cascade)\n productId String\n product Product @relation(fields: [productId], references: [id])\n /// Weight snapshot at allocation time, so later catalog edits do not rewrite history.\n weightGrams Decimal? @db.Decimal(14, 4)\n\n @@unique([allocationId, productId])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Messaging + audit — legacy views/sms, views/notify, views/newsletters and\n// views/statics. None of these had a model in the redesigned schema.\n// ─────────────────────────────────────────────────────────────────────────\n\n/// Who an SMS or notification was aimed at. Legacy's sms/create offered these\n/// audiences, each gated by its own rid (sms.create.all, .omde, .single, ...).\nenum MessageAudience {\n SINGLE_NUMBER // یک شماره\n MULTIPLE_NUMBERS // چند شماره\n ALL_CUSTOMERS // همه\n WHOLESALE_CUSTOMERS // عمده\n RETAIL_CUSTOMERS // تک\n SALES_AGENTS // کارشناسان فروش\n}\n\nmodel SmsMessage {\n id String @id @default(cuid())\n audience MessageAudience\n /// Comma-separated recipients for the SINGLE/MULTIPLE audiences.\n recipients String?\n body String\n /// How many numbers the send actually resolved to.\n sentCount Int @default(0)\n\n sentByAdminUserId String?\n sentByAdminUser AdminUser? @relation(\"SmsAuthor\", fields: [sentByAdminUserId], references: [id])\n\n createdAt DateTime @default(now())\n\n @@index([createdAt])\n}\n\nmodel Notification {\n id String @id @default(cuid())\n title String\n body String\n audience MessageAudience @default(ALL_CUSTOMERS)\n\n sentByAdminUserId String?\n sentByAdminUser AdminUser? @relation(\"NotificationAuthor\", fields: [sentByAdminUserId], references: [id])\n\n createdAt DateTime @default(now())\n\n recipients NotificationRecipient[]\n\n @@index([createdAt])\n}\n\n/// Per-customer delivery + read state — legacy `GET notify/read/{id}`.\nmodel NotificationRecipient {\n id String @id @default(cuid())\n notificationId String\n notification Notification @relation(fields: [notificationId], references: [id], onDelete: Cascade)\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n readAt DateTime?\n\n @@unique([notificationId, customerId])\n @@index([customerId, readAt])\n}\n\n/// Legacy views/newsletters — the mailing-list signups collected by the site.\nmodel NewsletterSubscriber {\n id String @id @default(cuid())\n email String?\n mobile String?\n createdAt DateTime @default(now())\n\n @@unique([email])\n @@unique([mobile])\n}\n\n/// Legacy views/statics — the customer activity log the \"آمار\" screen reads\n/// (`log/list/customer`, `log/ago/{customerId}`).\nmodel CustomerActivityLog {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n /// Free-form action key, e.g. 'login', 'order.placed', 'profile.updated'.\n action String\n detail String?\n createdAt DateTime @default(now())\n\n @@index([customerId, createdAt])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Uploads — replaces the legacy MyUpload / SingleUploadInput widgets, which\n// POSTed to a file endpoint and stored the returned URL on the owning record.\n// Ports of those forms currently take a URL by hand; this gives them a real\n// destination to upload to.\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel UploadedFile {\n id String @id @default(cuid())\n /// Public URL the stored file is served from.\n url String\n originalName String\n mimeType String\n sizeBytes Int\n /// Free-form tag for what the file belongs to, e.g. 'product', 'category',\n /// 'customer.nationalId'. Kept loose so a new form needs no schema change.\n purpose String?\n\n uploadedByAdminUserId String?\n uploadedByAdminUser AdminUser? @relation(\"FileUploader\", fields: [uploadedByAdminUserId], references: [id])\n\n createdAt DateTime @default(now())\n customerAvatars Customer[] @relation(\"CustomerAvatar\")\n\n @@index([purpose])\n}\n", - "inlineSchemaHash": "2a0cac26ecbad0f3dec2805682be8f2a8b95feeb6b57cd369f94c84ce9997c84", + "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\n/// Legacy Customer.type — what kind of wholesale customer this is. Values and\n/// labels taken verbatim from bulkCustomer.vue's userTypes list.\n///\n/// SALES_AGENT is a LEGACY value only: legacy stored a sales agent as a\n/// Customer row with type=1, but the redesigned system models a sales agent\n/// as an AdminUser (role SALES_EXPERT) instead — see admin-users.service.ts\n/// and admin-customers.service.ts's listReagents()/create(). No Customer row\n/// should be created with kind SALES_AGENT going forward; the value is kept\n/// only so legacyTypeToKind()/kindToLegacyType() stay total functions and any\n/// already-migrated data keeps a readable label.\n///\n/// RETAIL marks a \"تک\" (single/retail) customer — legacy kept these in an\n/// entirely separate table (SingleCustomer/`login`) that could never contain\n/// a wholesale/agent/shopkeeper/banker row. The redesigned schema folded both\n/// into one Customer model, so this value is what listSingle() now filters on\n/// to reproduce that same separation.\nenum CustomerKind {\n SALES_AGENT // legacy 1 کارشناس فروش — superseded by AdminRole.SALES_EXPERT, do not use for new rows\n APP_SHOPKEEPER // 2 مغازه دار اپلیکیشن\n WALK_IN_SHOPKEEPER // 3 مغازه دار حضوری\n BANKER // 4 بنکدار\n WORKSHOP_SUPERVISOR // 5 سرپرست کارگاه\n RETAIL // مشتری تک — legacy SingleCustomer\n}\n\nenum VerificationStep {\n UNVERIFIED\n STEP_ONE_SUBMITTED\n STEP_TWO_SUBMITTED\n VERIFIED\n}\n\n/// Legacy kept retail and wholesale orders in two SEPARATE tables\n/// (orders_orders vs orders). The redesigned schema has one Order model, so the\n/// distinction lives here instead.\nenum OrderChannel {\n RETAIL // تک فروشی\n WHOLESALE // عمده فروشی\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 /// Legacy cities.percent / percent_aux — per-city wage surcharges the city\n /// screen edits through GET cities/get-percent/{id} and get-percent-aux/{id}.\n percent Decimal? @db.Decimal(6, 3)\n percentAux Decimal? @db.Decimal(6, 3)\n customers Customer[]\n addresses CustomerAddress[]\n branches Branch[]\n preOrders PreOrder[]\n salesExpertProfiles SalesExpertProfile[]\n}\n\nmodel AdminUser {\n id String @id @default(cuid())\n\n /// ⚠️ Kept in sync BY A DATABASE TRIGGER — `admin_user_sync_full_name`, from\n /// migration 20260922180000. Prisma cannot see it, so a write here may store\n /// something other than what you passed:\n ///\n /// * write the parts -> fullName is rebuilt as \"first last\"\n /// * write fullName only -> the parts are re-derived from it\n /// * write BOTH in one update -> THE PARTS WIN; the submitted fullName is\n /// discarded\n ///\n /// Re-read after writing if you need the stored value. Same arrangement as\n /// Customer (D0 requirement 7 — the split applies to ALL admins, not only\n /// sales experts).\n fullName String\n firstName String?\n lastName String?\n\n mobile String @unique\n passwordHash String\n role AdminRole\n /// Legacy `users.ut`. Granular ACL lives here (Permission rows hang off UserType);\n /// the coarse `role` enum above is retained for the business rules that already\n /// switch on it. Nullable so existing seeded admins remain valid.\n userTypeId Int?\n userType UserType? @relation(fields: [userTypeId], references: [id])\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 /// Legacy stored a sales agent as a `customers` row with type=1, so\n /// ReagentCustomer.vue's shared form collected these alongside the\n /// wholesale-customer fields. A sales agent is now an AdminUser\n /// (role=SALES_EXPERT), so these live here instead — only ever set for that\n /// role; every other AdminUser leaves them null.\n avatarFileId String?\n avatarFile UploadedFile? @relation(\"AdminUserAvatar\", fields: [avatarFileId], references: [id])\n address String?\n storeName String? // legacy store_name\n pasazhName String? // legacy pasazh_name\n memberCode String? // legacy mcode\n\n referredCustomers Customer[] @relation(\"ReferralAgent\")\n supervisedWorkshops Workshop[] @relation(\"WorkshopSupervisor\")\n branchAllocations BranchAllocation[] @relation(\"AllocationAuthor\")\n postedLedgerEntries LedgerEntry[]\n orderStatusEvents OrderStatusEvent[]\n preOrderStatusEvents PreOrderStatusEvent[]\n sentSmsMessages SmsMessage[] @relation(\"SmsAuthor\")\n sentNotifications Notification[] @relation(\"NotificationAuthor\")\n uploadedFiles UploadedFile[] @relation(\"FileUploader\")\n\n /// Only ever set for role = SALES_EXPERT (D17).\n salesExpertProfile SalesExpertProfile?\n\n @@index([role])\n @@index([userTypeId])\n @@index([avatarFileId])\n @@index([firstName])\n @@index([lastName])\n}\n\n/// The sales-expert fields legacy kept on the `customers` row (type=1) and that\n/// were lost when a sales expert became an AdminUser (D17).\n///\n/// A separate table rather than columns on AdminUser: these are meaningful only\n/// for role = SALES_EXPERT and would be permanently null for every other admin.\n/// A row is created for every SALES_EXPERT by the migration, and upserted by the\n/// service whenever one is created or a user's role becomes SALES_EXPERT — but\n/// readers still left-join, so a missing profile degrades to nulls rather than\n/// dropping the agent from the grid.\n///\n/// `reagent_id` is deliberately NOT here — see D17: in the reference it was a\n/// side effect of the shared customers form, and filling it removed that expert\n/// from the selectable-agent list (queryReagent, AdminController.php:420).\nmodel SalesExpertProfile {\n id String @id @default(cuid())\n adminUserId String @unique\n adminUser AdminUser @relation(fields: [adminUserId], references: [id], onDelete: Cascade)\n\n /// Legacy customers.city_id — a filterable grid column\n /// (ReagentListModel.js:14) and the source of the commission autofill\n /// (ReagentCustomer.vue:215-228).\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n\n /// Legacy customers.gram_level — the «سطح اعتبار» column, filterable.\n /// Decimal(14,4) matches Customer.goldCreditLimitGrams exactly so the same\n /// credit limit has identical precision on both tables.\n gramLevel Decimal @default(0) @db.Decimal(14, 4)\n\n /// Legacy customers.phone (varchar 15) and ncode (varchar 20). The reference\n /// stores both and reads neither back; kept so the form matches it. Neither\n /// is unique and neither has a validation rule in the reference — inventing\n /// one would reject data the reference accepts.\n phone String? @db.VarChar(15)\n nationalCode String? @db.VarChar(20)\n\n /// The Customer row carrying this expert's OWN orders, debt and credit limit.\n ///\n /// The reference stored a sales agent AS a Customer (type=1), so an order the\n /// agent placed from the app was simply their own, against their own\n /// gram_level (ApiMobileController.php:534-535). Here the expert is an\n /// AdminUser (D17), which has no ledger and cannot be an Order's subject, so\n /// this one linked Customer plays that part.\n ///\n /// Distinct from the expert's REFERRED customers, which are ordinary Customer\n /// rows pointing at the AdminUser through Customer.referredByAgentId.\n ///\n /// Null until the expert first signs into the app, when it is created from\n /// this profile. See D21.\n tradingCustomerId String? @unique\n tradingCustomer Customer? @relation(\"SalesExpertTradingAccount\", fields: [tradingCustomerId], references: [id], onDelete: SetNull)\n\n createdAt DateTime @default(now())\n updatedAt DateTime @default(now()) @updatedAt\n\n @@index([cityId])\n}\n\nmodel Customer {\n id String @id @default(cuid())\n\n /// ⚠️ `fullName`, `firstName` and `lastName` are kept in sync BY A DATABASE\n /// TRIGGER — `customer_sync_full_name`, created in migration\n /// 20260922120000_customer_name_parts. Prisma does not know about it, so a\n /// write here is NOT the whole story:\n ///\n /// * write the parts -> fullName is rebuilt as \"first last\"\n /// * write fullName only -> the parts are re-derived from it\n /// * write BOTH in one update -> THE PARTS WIN; fullName is overwritten\n /// with \"first last\" and whatever you passed for fullName is discarded\n ///\n /// So a value you wrote can differ from the value that lands in the row.\n /// Re-read after writing if you need the stored value. Prefer writing the\n /// parts; `fullName` exists for the legacy readers that predate D0.\n ///\n /// Reference has these as two columns from the start\n /// (henza_server 2020_10_01_120430_customers_table.php:18-19 fname/lname);\n /// the port collapsed them, which is the D0 defect.\n fullName String\n firstName String?\n lastName String?\n\n mobile String @unique\n nationalCode String @unique\n passwordHash String\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n\n /// Legacy Customer.type — distinguishes بنکدار from مغازه دار, which the\n /// BankerList / ShopkeeperList screens filter on.\n kind CustomerKind @default(APP_SHOPKEEPER)\n\n /// Legacy contact + shop columns, shown on the wholesale customer form and as\n /// (hidden-by-default) columns in its grid.\n phone String?\n address String?\n storeName String?\n pasazhName String?\n zone String?\n /// Legacy mcode — the operator-assigned customer code.\n memberCode String?\n\n referredByAgentId String?\n referredByAgent AdminUser? @relation(\"ReferralAgent\", fields: [referredByAgentId], references: [id])\n\n /// Set only on the one Customer row that acts as a sales expert's own trading\n /// account (see SalesExpertProfile.tradingCustomerId). Empty for every\n /// ordinary customer.\n salesExpertTradingFor SalesExpertProfile? @relation(\"SalesExpertTradingAccount\")\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 /// Legacy `customers.percent` — the per-customer making-charge offset. It is\n /// ADDED to the variant's making charge when pricing an order line\n /// (henza_pwa src/Mixins/product/index.ts:69-75:\n /// getWage(v) = state.percent + v.percentage_pay)\n /// and it shifts the catalogue making-charge filter bounds\n /// (ApiMobileController.php:391-396,76-77). It is NOT part of the price\n /// formula itself — that extra percent is ConfigStatic['product_price_extra_percent'].\n ///\n /// NOT the wallet cashback rate (storefront ConfigStatic['percent'], D13) and\n /// NOT the wholesale per-line charge (OrderItem.wagePercentSnapshot).\n ///\n /// DB default 0 keeps every existing customer's price unchanged. The\n /// reference's application-layer default for NEW customers is 3\n /// (AdminController.php:338) and the field is required on create (:354).\n makingChargeOffsetPercent Decimal @default(0) @db.Decimal(6, 3)\n\n verificationStep VerificationStep @default(UNVERIFIED)\n nationalIdImageUrl String?\n businessLicenseUrl String?\n shopImageUrl String?\n\n /// Legacy `image_id` — the profile photo (عکس پروفایل) on the sales-agent\n /// form. Legacy stored an FK to its Image table and read it back as a nested\n /// `image: { id, image }`, so this is a relation, not a bare URL string.\n avatarFileId String?\n avatarFile UploadedFile? @relation(\"CustomerAvatar\", fields: [avatarFileId], references: [id])\n\n /// Legacy had TWO independent switches on a customer:\n /// sts -> may they place orders (this field)\n /// active -> is the account enabled (isActive, below)\n /// The port previously drove both from isActive; they are separate again.\n canPlaceOrders Boolean @default(true)\n\n isActive Boolean @default(true)\n /// Legacy archive flag — the archived list filtered on this, not on isActive.\n isArchived Boolean @default(false)\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 comments Comment[]\n promotions Promotion[]\n notifications NotificationRecipient[]\n activityLogs CustomerActivityLog[]\n otpChallenges CustomerOtp[]\n\n @@index([referredByAgentId])\n @@index([firstName])\n @@index([lastName])\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 /// Legacy Branch.address / phone — both are columns in the branch grid.\n address String?\n phone String?\n /// Legacy GET branch/active/{id} toggles this.\n isActive Boolean @default(true)\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n adminUsers AdminUser[]\n orders Order[]\n allocations BranchAllocation[]\n}\n\nmodel Workshop {\n id String @id @default(cuid())\n name String\n /// Legacy Workshop.address / phone — both are columns in the workshop grid.\n address String?\n phone String?\n /// The supervisor created alongside the workshop by the legacy Add form.\n supervisorId String?\n supervisor AdminUser? @relation(\"WorkshopSupervisor\", fields: [supervisorId], references: [id])\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 /// Legacy Category.type — which storefront the category belongs to:\n /// 1 سایت | 2 اپلیکشن موبایل. Drives the \"type_str\" grid column.\n type Int @default(1)\n /// Legacy Category.image / icon — the grid renders the image, the storefront the icon.\n imageUrl String?\n iconUrl String?\n /// Legacy meta tag + description, used for SEO on the site categories.\n metaTag String?\n description String?\n parentId String?\n parent Category? @relation(\"CategoryTree\", fields: [parentId], references: [id])\n children Category[] @relation(\"CategoryTree\")\n products Product[]\n promotions Promotion[]\n}\n\nmodel Brand {\n id String @id @default(cuid())\n title String\n products Product[]\n promotions Promotion[]\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 /// Legacy Product.product_code — the human-facing SKU shown in the grid.\n productCode String? @unique\n /// Legacy Product.show_type: 1 site, 2 mobile, 3 both.\n showType Int @default(3)\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n variants ProductVariant[]\n images ProductImage[]\n collections CollectionProduct[]\n comments Comment[]\n promotions Promotion[]\n allocationItems BranchAllocationItem[]\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 /// Legacy had 4 distinct upload slots (تصویر شاخص / تصویر شاخص موبایل /\n /// گالری تصاویر سایت / گالری تصاویر موبایل) — GALLERY is the default so\n /// existing rows and any generic \"just attach an image\" caller keep working.\n role ProductImageRole @default(GALLERY)\n\n /// Variants that display this image. A gallery image may back several\n /// variants (one photo, several weights), which is why this is a plain\n /// one-to-many and not a join table.\n variants ProductVariant[]\n}\n\nenum ProductImageRole {\n FEATURED\n FEATURED_MOBILE\n GALLERY\n GALLERY_MOBILE\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 /// Legacy ProductVariant.qty — on-hand stock. The cart refuses a variant with\n /// qty 0 unless it is flagged pre-order, so both fields are required for that\n /// guard to work (see apps/admin-panel/src/stores/cart.ts).\n qty Int @default(0)\n /// Legacy is_pre_order: orderable even at zero stock.\n isPreOrder Boolean @default(false)\n isActive Boolean @default(true)\n\n /// The gallery image shown for this variant, picked from its own product's\n /// images — never a re-uploaded copy. onDelete: SetNull because dropping an\n /// image must never cascade into the variant: OrderItem rows reference\n /// variants, so losing one would break order history.\n imageId String?\n image ProductImage? @relation(fields: [imageId], references: [id], onDelete: SetNull)\n\n orderItems OrderItem[]\n\n @@index([imageId])\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 /// Which sales channel placed this order — see OrderChannel.\n channel OrderChannel @default(RETAIL)\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 /// Instrument metadata from the legacy \"cash\" document form. Which fields are\n /// filled depends on sourceType: a melted-gold receipt carries lab/hallmark and\n /// the pre-normalization weight, a cheque carries the account and bank, and a\n /// transfer carries its destination.\n /// Legacy columns: lab, ang, account_name, bank_name, to, original_value, recive_date.\n labName String?\n hallmark String?\n accountName String?\n bankName String?\n transferTo String?\n /// Weight as handed over, BEFORE karat normalization (legacy original_value).\n originalValue Decimal? @db.Decimal(14, 4)\n /// Rial amount for cash/cheque documents (legacy price).\n priceRial Decimal? @db.Decimal(20, 2)\n receivedAt DateTime?\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\n// ─────────────────────────────────────────────────────────────────────────\n// Admin ACL — faithful port of the legacy granular permission model.\n//\n// Legacy (henza_server):\n// Schema::create('permission', function (Blueprint $table) {\n// $table->string('rid');\n// $table->integer('ut');\n// $table->primary(['rid' , 'ut']);\n// });\n//\n// Permissions attach to the USER TYPE (`ut`), never to an individual user, so\n// every admin of a given type shares one permission set. `ut` pointed at a row\n// in the legacy `config_static` lookup table; UserType is that table, narrowed\n// to the admin-user-type rows it was actually used for.\n//\n// A `rid` is a dotted permission id such as \"persons.list.client.omde\". The\n// frontend expands each stored rid into its ancestors at runtime via\n// checkParent() — see apps/admin-panel/src/stores/users.ts. Only the 58 leaf\n// rids the routers actually gate are seeded; ancestors are derived, not stored.\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel UserType {\n /// Legacy `permission.ut` / `users.ut` — an integer, kept as the PK so seeded\n /// rows keep their legacy identity rather than getting new cuids.\n id Int @id\n title String\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n permissions Permission[]\n adminUsers AdminUser[]\n\n @@map(\"user_type\")\n}\n\nmodel Permission {\n /// Dotted permission id, e.g. \"order.list\", \"persons.list.client.omde\".\n rid String\n userTypeId Int\n userType UserType @relation(fields: [userTypeId], references: [id], onDelete: Cascade)\n\n /// Legacy's composite primary key (rid, ut), preserved exactly.\n @@id([rid, userTypeId])\n @@index([userTypeId])\n @@map(\"permission\")\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Product comments / reviews — legacy `comment` table (App\\Models\\Comment),\n// moderated from views/customer/Comments/Comments.vue.\n//\n// Legacy fillable: product_id, comment, reply_to, sts, customer_id\n// Legacy Comment::$STS = [1 => 'در انتظار تایید', 2 => 'تایید شده', 3 => 'رد شده']\n// which is represented here as an enum rather than a magic integer.\n// ─────────────────────────────────────────────────────────────────────────\n\nenum CommentStatus {\n PENDING // 1 — در انتظار تایید\n APPROVED // 2 — تایید شده\n REJECTED // 3 — رد شده\n}\n\nmodel Comment {\n id String @id @default(cuid())\n productId String\n product Product @relation(fields: [productId], references: [id])\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n body String\n status CommentStatus @default(PENDING)\n\n /// Legacy `reply_to`: a staff reply threads under the comment it answers.\n replyToId String?\n replyTo Comment? @relation(\"CommentReplies\", fields: [replyToId], references: [id])\n replies Comment[] @relation(\"CommentReplies\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([productId])\n @@index([customerId])\n @@index([status])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Settings — replaces legacy `config_static`, a key/value table the admin\n// panel read and wrote through endpoints like /wallet/config and\n// /const/__list__ut. Values are stored as JSON so a setting can hold a scalar,\n// an object, or the dynamic option lists legacy kept under `__list__*` keys.\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel Setting {\n key String @id\n value Json\n updatedAt DateTime @updatedAt\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Promotions / discounts — legacy views/promotion/*, served by\n// PromotionController (`product/variant/promotion`). The redesigned schema had\n// no promotion concept at all; this models what the legacy forms actually\n// collect.\n// ─────────────────────────────────────────────────────────────────────────\n\n/// Legacy Promotion.type — what the discount is scoped to.\nenum PromotionScope {\n PRODUCT // 1 محصولات\n CATEGORY // 2 دسته بندی\n CUSTOMER // 3 مشتری\n BRAND // 4 برند\n}\n\n/// Legacy Promotion.use_only — how often a customer may redeem it.\nenum PromotionUsage {\n ONCE // 1 فقط یکبار\n UNTIL_END_DATE // 2 تا تاریخ پایان\n}\n\nmodel Promotion {\n id String @id @default(cuid())\n title String\n description String?\n scope PromotionScope @default(PRODUCT)\n usage PromotionUsage @default(ONCE)\n /// Percentage off, 0-100 (legacy discount_percent).\n discountPercent Decimal @db.Decimal(6, 3)\n isActive Boolean @default(true)\n /// Which storefront it shows on, mirroring Product.showType (legacy platform).\n platform Int @default(3)\n\n startsAt DateTime?\n endsAt DateTime?\n\n /// Exactly one of these is populated, per `scope`.\n productId String?\n product Product? @relation(fields: [productId], references: [id])\n categoryId String?\n category Category? @relation(fields: [categoryId], references: [id])\n customerId String?\n customer Customer? @relation(fields: [customerId], references: [id])\n brandId String?\n brand Brand? @relation(fields: [brandId], references: [id])\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([scope, isActive])\n}\n\n/// Legacy \"ignore\" table (views/offersIgnore) — specific products, categories,\n/// customers or brands excluded from the automatic discount engine.\n/// Legacy posts { type, item_id[] }: \"type\" reuses the promotion scope codes\n/// (1 محصولات | 2 دسته بندی | 3 مشتری | 4 برند) and \"itemId\" is the excluded row.\nmodel PromotionExclusion {\n id String @id @default(cuid())\n type Int\n itemId String\n createdAt DateTime @default(now())\n\n @@unique([type, itemId])\n @@index([type])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Branch stock allocation — legacy views/invoice/* (\"فاکتور انتقال\"), served by\n// BranchController as `branch/product`. Despite the screen being called an\n// invoice, it is not a sales document: it records which products head office\n// allocated to a branch, and who allocated them.\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel BranchAllocation {\n id String @id @default(cuid())\n branchId String\n branch Branch @relation(fields: [branchId], references: [id])\n\n /// Legacy `allocator_user` — the admin who filed the allocation.\n allocatorAdminUserId String?\n allocatorAdminUser AdminUser? @relation(\"AllocationAuthor\", fields: [allocatorAdminUserId], references: [id])\n\n note String?\n createdAt DateTime @default(now())\n\n items BranchAllocationItem[]\n\n @@index([branchId])\n}\n\nmodel BranchAllocationItem {\n id String @id @default(cuid())\n allocationId String\n allocation BranchAllocation @relation(fields: [allocationId], references: [id], onDelete: Cascade)\n productId String\n product Product @relation(fields: [productId], references: [id])\n /// Weight snapshot at allocation time, so later catalog edits do not rewrite history.\n weightGrams Decimal? @db.Decimal(14, 4)\n\n @@unique([allocationId, productId])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Messaging + audit — legacy views/sms, views/notify, views/newsletters and\n// views/statics. None of these had a model in the redesigned schema.\n// ─────────────────────────────────────────────────────────────────────────\n\n/// Who an SMS or notification was aimed at. Legacy's sms/create offered these\n/// audiences, each gated by its own rid (sms.create.all, .omde, .single, ...).\nenum MessageAudience {\n SINGLE_NUMBER // یک شماره\n MULTIPLE_NUMBERS // چند شماره\n ALL_CUSTOMERS // همه\n WHOLESALE_CUSTOMERS // عمده\n RETAIL_CUSTOMERS // تک\n SALES_AGENTS // کارشناسان فروش\n}\n\nmodel SmsMessage {\n id String @id @default(cuid())\n audience MessageAudience\n /// Comma-separated recipients for the SINGLE/MULTIPLE audiences.\n recipients String?\n body String\n /// How many numbers the send actually resolved to.\n sentCount Int @default(0)\n\n sentByAdminUserId String?\n sentByAdminUser AdminUser? @relation(\"SmsAuthor\", fields: [sentByAdminUserId], references: [id])\n\n createdAt DateTime @default(now())\n\n @@index([createdAt])\n}\n\nmodel Notification {\n id String @id @default(cuid())\n title String\n body String\n audience MessageAudience @default(ALL_CUSTOMERS)\n\n sentByAdminUserId String?\n sentByAdminUser AdminUser? @relation(\"NotificationAuthor\", fields: [sentByAdminUserId], references: [id])\n\n createdAt DateTime @default(now())\n\n recipients NotificationRecipient[]\n\n @@index([createdAt])\n}\n\n/// Per-customer delivery + read state — legacy `GET notify/read/{id}`.\nmodel NotificationRecipient {\n id String @id @default(cuid())\n notificationId String\n notification Notification @relation(fields: [notificationId], references: [id], onDelete: Cascade)\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n readAt DateTime?\n\n @@unique([notificationId, customerId])\n @@index([customerId, readAt])\n}\n\n/// Legacy views/newsletters — the mailing-list signups collected by the site.\nmodel NewsletterSubscriber {\n id String @id @default(cuid())\n email String?\n mobile String?\n createdAt DateTime @default(now())\n\n @@unique([email])\n @@unique([mobile])\n}\n\n/// Legacy views/statics — the customer activity log the \"آمار\" screen reads\n/// (`log/list/customer`, `log/ago/{customerId}`).\nmodel CustomerActivityLog {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n /// Free-form action key, e.g. 'login', 'order.placed', 'profile.updated'.\n action String\n detail String?\n createdAt DateTime @default(now())\n\n @@index([customerId, createdAt])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Uploads — replaces the legacy MyUpload / SingleUploadInput widgets, which\n// POSTed to a file endpoint and stored the returned URL on the owning record.\n// Ports of those forms currently take a URL by hand; this gives them a real\n// destination to upload to.\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel UploadedFile {\n id String @id @default(cuid())\n /// Public URL the stored file is served from.\n url String\n originalName String\n mimeType String\n sizeBytes Int\n /// Free-form tag for what the file belongs to, e.g. 'product', 'category',\n /// 'customer.nationalId'. Kept loose so a new form needs no schema change.\n purpose String?\n\n uploadedByAdminUserId String?\n uploadedByAdminUser AdminUser? @relation(\"FileUploader\", fields: [uploadedByAdminUserId], references: [id])\n\n createdAt DateTime @default(now())\n customerAvatars Customer[] @relation(\"CustomerAvatar\")\n adminUserAvatars AdminUser[] @relation(\"AdminUserAvatar\")\n\n @@index([purpose])\n}\n\n/// One OTP login challenge for a customer.\n///\n/// Ports henza_server's flow (ApiMobileController.php:39-78), where userAuth\n/// generated rand(1000,9999), stored it on the customer row as `send_password`\n/// and sent it by SMS, and userVerfiy compared the submitted code against it\n/// (mismatch -> HTTP 422).\n///\n/// Three deliberate improvements over the reference, approved by the user\n/// (docs/parity/DECISIONS.md D6 and T1):\n/// 1. the code is stored as a bcrypt hash, never in plain text — the\n/// reference kept it readable in a customer column;\n/// 2. it expires (`expiresAt`) — the reference's code stayed valid forever;\n/// 3. attempts are counted and the challenge locks out — the reference had\n/// no limit at all.\n/// The parity behaviour itself is the code comparison and the rejection.\nmodel CustomerOtp {\n id String @id @default(cuid())\n /// Normalised to English digits before storing, as the reference does with\n /// Utility::faTOen() on both send and verify.\n mobile String\n customerId String\n customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)\n\n /// bcrypt hash of the 4-digit code. Never the code itself.\n codeHash String\n expiresAt DateTime\n /// Failed verify attempts against THIS challenge.\n attempts Int @default(0)\n /// Set when the code is accepted, so one challenge cannot be reused.\n consumedAt DateTime?\n\n /// Kept for the per-IP send throttle.\n requestIp String?\n createdAt DateTime @default(now())\n\n @@index([mobile, createdAt])\n @@index([customerId])\n @@index([expiresAt])\n}\n\n/// Bespoke migration ledger left by the EARLIER, pre-Prisma port.\n///\n/// It predates Prisma Migrate in this repo and holds 12 historical rows\n/// (`0001_init.sql`, `0002_admin_schema.sql`, `seed:initial`, and the early\n/// `2026091…` migrations). It is the only record of how that port was applied,\n/// so it is **kept and never written to or deleted from** (DEAD-07).\n///\n/// It is declared here purely so `prisma migrate diff` stops proposing\n/// `DROP TABLE \"_goldbank_migrations\"` — an undeclared table reads as drift.\n/// `@@ignore` keeps it out of Prisma Client: nothing in the application should\n/// ever read or write it, and Prisma Migrate will not manage its shape either.\n///\n/// Prisma's own ledger is `_prisma_migrations`, which is separate and is never\n/// edited by hand (D14).\nmodel goldbank_migrations {\n name String @id\n appliedAt DateTime @default(now()) @map(\"applied_at\") @db.Timestamptz(6)\n\n @@map(\"_goldbank_migrations\")\n @@ignore\n}\n", + "inlineSchemaHash": "e290014d6a3917ba9e61ab09b945e2d1aca56498b54593681f437b770dcb227c", "copyEngine": true } config.dirname = '/' -config.runtimeDataModel = JSON.parse("{\"models\":{\"City\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"province\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"percent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy cities.percent / percent_aux — per-city wage surcharges the city\\\\nscreen edits through GET cities/get-percent/{id} and get-percent-aux/{id}.\"},{\"name\":\"percentAux\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customers\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CityToCustomer\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"addresses\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CustomerAddress\",\"relationName\":\"CityToCustomerAddress\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branches\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Branch\",\"relationName\":\"BranchToCity\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"CityToPreOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"AdminUser\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fullName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mobile\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"passwordHash\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"role\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminRole\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"userTypeId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `users.ut`. Granular ACL lives here (Permission rows hang off UserType);\\\\nthe coarse `role` enum above is retained for the business rules that already\\\\nswitch on it. Nullable so existing seeded admins remain valid.\"},{\"name\":\"userType\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UserType\",\"relationName\":\"AdminUserToUserType\",\"relationFromFields\":[\"userTypeId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branchId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branch\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Branch\",\"relationName\":\"AdminUserToBranch\",\"relationFromFields\":[\"branchId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"wagePercent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"referredCustomers\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"ReferralAgent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"supervisedWorkshops\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Workshop\",\"relationName\":\"WorkshopSupervisor\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branchAllocations\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocation\",\"relationName\":\"AllocationAuthor\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"postedLedgerEntries\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerEntry\",\"relationName\":\"AdminUserToLedgerEntry\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderStatusEvents\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderStatusEvent\",\"relationName\":\"AdminUserToOrderStatusEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrderStatusEvents\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrderStatusEvent\",\"relationName\":\"AdminUserToPreOrderStatusEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentSmsMessages\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"SmsMessage\",\"relationName\":\"SmsAuthor\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentNotifications\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Notification\",\"relationName\":\"NotificationAuthor\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"uploadedFiles\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UploadedFile\",\"relationName\":\"FileUploader\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Customer\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fullName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mobile\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"nationalCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"passwordHash\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"CityToCustomer\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"kind\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"CustomerKind\",\"default\":\"APP_SHOPKEEPER\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Customer.type — distinguishes بنکدار from مغازه دار, which the\\\\nBankerList / ShopkeeperList screens filter on.\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy contact + shop columns, shown on the wholesale customer form and as\\\\n(hidden-by-default) columns in its grid.\"},{\"name\":\"address\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"storeName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"pasazhName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"zone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"memberCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy mcode — the operator-assigned customer code.\"},{\"name\":\"referredByAgentId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"referredByAgent\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"ReferralAgent\",\"relationFromFields\":[\"referredByAgentId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"goldCreditLimitGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"goldDebtGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fiatDebtRial\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"verificationStep\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"VerificationStep\",\"default\":\"UNVERIFIED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"nationalIdImageUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"businessLicenseUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"shopImageUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"avatarFileId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `image_id` — the profile photo (عکس پروفایل) on the sales-agent\\\\nform. Legacy stored an FK to its Image table and read it back as a nested\\\\n`image: { id, image }`, so this is a relation, not a bare URL string.\"},{\"name\":\"avatarFile\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UploadedFile\",\"relationName\":\"CustomerAvatar\",\"relationFromFields\":[\"avatarFileId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"canPlaceOrders\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy had TWO independent switches on a customer:\\\\nsts -> may they place orders (this field)\\\\nactive -> is the account enabled (isActive, below)\\\\nThe port previously drove both from isActive; they are separate again.\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isArchived\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy archive flag — the archived list filtered on this, not on isActive.\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"addresses\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CustomerAddress\",\"relationName\":\"CustomerToCustomerAddress\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"CustomerToOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"CustomerToPreOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ledgerEntries\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerEntry\",\"relationName\":\"CustomerToLedgerEntry\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"walletBonuses\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"WalletBonus\",\"relationName\":\"CustomerToWalletBonus\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"comments\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Comment\",\"relationName\":\"CommentToCustomer\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"promotions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Promotion\",\"relationName\":\"CustomerToPromotion\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"notifications\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"NotificationRecipient\",\"relationName\":\"CustomerToNotificationRecipient\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"activityLogs\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CustomerActivityLog\",\"relationName\":\"CustomerToCustomerActivityLog\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"CustomerAddress\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToCustomerAddress\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"CityToCustomerAddress\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"addressLine\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isDefault\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Branch\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"address\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Branch.address / phone — both are columns in the branch grid.\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy GET branch/active/{id} toggles this.\"},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"BranchToCity\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"adminUsers\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToBranch\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"BranchToOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocations\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocation\",\"relationName\":\"BranchToBranchAllocation\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Workshop\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"address\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Workshop.address / phone — both are columns in the workshop grid.\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"supervisorId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"The supervisor created alongside the workshop by the legacy Add form.\"},{\"name\":\"supervisor\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"WorkshopSupervisor\",\"relationFromFields\":[\"supervisorId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"PreOrderToWorkshop\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Category\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"type\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":1,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Category.type — which storefront the category belongs to:\\\\n1 سایت | 2 اپلیکشن موبایل. Drives the \\\"type_str\\\" grid column.\"},{\"name\":\"imageUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Category.image / icon — the grid renders the image, the storefront the icon.\"},{\"name\":\"iconUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"metaTag\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy meta tag + description, used for SEO on the site categories.\"},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"parentId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"parent\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Category\",\"relationName\":\"CategoryTree\",\"relationFromFields\":[\"parentId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"children\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Category\",\"relationName\":\"CategoryTree\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"products\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"CategoryToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"promotions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Promotion\",\"relationName\":\"CategoryToPromotion\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Brand\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"products\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"BrandToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"promotions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Promotion\",\"relationName\":\"BrandToPromotion\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Collection\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"products\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CollectionProduct\",\"relationName\":\"CollectionToCollectionProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"Curated product groupings — replaces legacy \\\"Ciny/CinyProduct\\\" (a tagging\\\\nfeature, NOT a gold-price snapshot mechanism, despite the misleading legacy name).\"},\"CollectionProduct\":{\"dbName\":null,\"fields\":[{\"name\":\"collectionId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"collection\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Collection\",\"relationName\":\"CollectionToCollectionProduct\",\"relationFromFields\":[\"collectionId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"CollectionProductToProduct\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":{\"name\":null,\"fields\":[\"collectionId\",\"productId\"]},\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Product\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"categoryId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"category\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Category\",\"relationName\":\"CategoryToProduct\",\"relationFromFields\":[\"categoryId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"brandId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"brand\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Brand\",\"relationName\":\"BrandToProduct\",\"relationFromFields\":[\"brandId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Product.product_code — the human-facing SKU shown in the grid.\"},{\"name\":\"showType\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":3,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Product.show_type: 1 site, 2 mobile, 3 both.\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"variants\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ProductVariant\",\"relationName\":\"ProductToProductVariant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"images\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ProductImage\",\"relationName\":\"ProductToProductImage\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"collections\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CollectionProduct\",\"relationName\":\"CollectionProductToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"comments\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Comment\",\"relationName\":\"CommentToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"promotions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Promotion\",\"relationName\":\"ProductToPromotion\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocationItems\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocationItem\",\"relationName\":\"BranchAllocationItemToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"ProductImage\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"ProductToProductImage\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"url\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sortOrder\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"role\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"ProductImageRole\",\"default\":\"GALLERY\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy had 4 distinct upload slots (تصویر شاخص / تصویر شاخص موبایل /\\\\nگالری تصاویر سایت / گالری تصاویر موبایل) — GALLERY is the default so\\\\nexisting rows and any generic \\\"just attach an image\\\" caller keep working.\"}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"ProductVariant\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"ProductToProductVariant\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sku\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rashWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Base/raw weight before making charge (\\\"rash weight\\\").\"},{\"name\":\"defaultWagePercent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Default making-charge percentage applied on top of rash weight.\"},{\"name\":\"qty\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy ProductVariant.qty — on-hand stock. The cart refuses a variant with\\\\nqty 0 unless it is flagged pre-order, so both fields are required for that\\\\nguard to work (see apps/admin-panel/src/stores/cart.ts).\"},{\"name\":\"isPreOrder\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy is_pre_order: orderable even at zero stock.\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderItems\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderItem\",\"relationName\":\"OrderItemToProductVariant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Order\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderNumber\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToOrder\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branchId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branch\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Branch\",\"relationName\":\"BranchToOrder\",\"relationFromFields\":[\"branchId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"channel\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"OrderChannel\",\"default\":\"RETAIL\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Which sales channel placed this order — see OrderChannel.\"},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"OrderStatus\",\"default\":\"PLACED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"totalGoldWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Total gold weight incl. wage across all items (\\\"allGram\\\" / order.w).\"},{\"name\":\"wageWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Pure making-charge weight component (totalGoldWeightGrams minus raw weight sum).\"},{\"name\":\"flatRialCharge\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Flat Rial charge independent of gold price (e.g. stones) — legacy rial_input.\"},{\"name\":\"isSuspendedOverCreditLimit\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"deliveredAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"items\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderItem\",\"relationName\":\"OrderToOrderItem\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"statusEvents\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderStatusEvent\",\"relationName\":\"OrderToOrderStatusEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ledgerEntries\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerEntry\",\"relationName\":\"LedgerEntryToOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"convertedFromPreOrder\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"OrderToPreOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"OrderItem\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"order\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"OrderToOrderItem\",\"relationFromFields\":[\"orderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productVariantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productVariant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ProductVariant\",\"relationName\":\"OrderItemToProductVariant\",\"relationFromFields\":[\"productVariantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"quantity\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rashWeightGramsSnapshot\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"wagePercentSnapshot\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"discountPercentSnapshot\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"computedWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"computed: rash*(1 + (wagePercent-discount)/100)\"},{\"name\":\"flatRialCharge\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"OrderStatusEvent\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"order\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"OrderToOrderStatusEvent\",\"relationFromFields\":[\"orderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fromStatus\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderStatus\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"toStatus\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderStatus\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"note\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"actorAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"actorAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToOrderStatusEvent\",\"relationFromFields\":[\"actorAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"PreOrder\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToPreOrder\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"CityToPreOrder\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"workshopId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"workshop\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Workshop\",\"relationName\":\"PreOrderToWorkshop\",\"relationFromFields\":[\"workshopId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"PreOrderStatus\",\"default\":\"PLACED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"designImageUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"colorName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sizeSpec\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"stoneColor\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"minaColor\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"estimatedWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"estimatedPriceRial\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"convertedOrderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Once priced, an admin converts this into a real (debt-bearing) Order.\"},{\"name\":\"convertedOrder\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"OrderToPreOrder\",\"relationFromFields\":[\"convertedOrderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"statusEvents\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrderStatusEvent\",\"relationName\":\"PreOrderToPreOrderStatusEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"PreOrderStatusEvent\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrder\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"PreOrderToPreOrderStatusEvent\",\"relationFromFields\":[\"preOrderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"changes\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Json\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Field-level diff, e.g. {\\\"status\\\": {\\\"old\\\": \\\"PLACED\\\", \\\"new\\\": \\\"ADMIN_APPROVED\\\"}}\"},{\"name\":\"actorAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"actorAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToPreOrderStatusEvent\",\"relationFromFields\":[\"actorAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ipAddress\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"LedgerEntry\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToLedgerEntry\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"currency\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerCurrency\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"direction\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerDirection\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sourceType\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerSourceType\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"amount\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Always positive; direction determines the sign applied to the customer balance.\"},{\"name\":\"karatPerMille\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Purity of the physical instrument, for REPAYMENT_MELTED_GOLD (e.g. 750, 916).\"},{\"name\":\"pricePerGramRialAtEntry\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Gold price/gram at conversion time, for REPAYMENT_CASH.\"},{\"name\":\"orderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"order\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"LedgerEntryToOrder\",\"relationFromFields\":[\"orderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"confirmed\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Pending entries don't count toward the cached customer balance until confirmed.\"},{\"name\":\"confirmedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"postedByAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"postedByAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToLedgerEntry\",\"relationFromFields\":[\"postedByAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"labName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Instrument metadata from the legacy \\\"cash\\\" document form. Which fields are\\\\nfilled depends on sourceType: a melted-gold receipt carries lab/hallmark and\\\\nthe pre-normalization weight, a cheque carries the account and bank, and a\\\\ntransfer carries its destination.\\\\nLegacy columns: lab, ang, account_name, bank_name, to, original_value, recive_date.\"},{\"name\":\"hallmark\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"accountName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"bankName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"transferTo\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"originalValue\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Weight as handed over, BEFORE karat normalization (legacy original_value).\"},{\"name\":\"priceRial\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Rial amount for cash/cheque documents (legacy price).\"},{\"name\":\"receivedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"note\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"GoldPriceQuote\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"karatPerMille\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"pricePerGramRial\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"source\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"quotedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"Replaces legacy WebServiceGold / ConfigStatic.gold_price — timestamped price feed.\"},\"WalletBonus\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToWalletBonus\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"goldGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"grantedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"consumedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"Time-limited gold cashback bonus — replaces the legacy Rooyesh\\\\Wallet gold credit.\"},\"UserType\":{\"dbName\":\"user_type\",\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `permission.ut` / `users.ut` — an integer, kept as the PK so seeded\\\\nrows keep their legacy identity rather than getting new cuids.\"},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"permissions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Permission\",\"relationName\":\"PermissionToUserType\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"adminUsers\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToUserType\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Permission\":{\"dbName\":\"permission\",\"fields\":[{\"name\":\"rid\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Dotted permission id, e.g. \\\"order.list\\\", \\\"persons.list.client.omde\\\".\"},{\"name\":\"userTypeId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"userType\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UserType\",\"relationName\":\"PermissionToUserType\",\"relationFromFields\":[\"userTypeId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"Cascade\",\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":{\"name\":null,\"fields\":[\"rid\",\"userTypeId\"]},\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Comment\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"CommentToProduct\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CommentToCustomer\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"body\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"CommentStatus\",\"default\":\"PENDING\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"replyToId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `reply_to`: a staff reply threads under the comment it answers.\"},{\"name\":\"replyTo\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Comment\",\"relationName\":\"CommentReplies\",\"relationFromFields\":[\"replyToId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"replies\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Comment\",\"relationName\":\"CommentReplies\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Setting\":{\"dbName\":null,\"fields\":[{\"name\":\"key\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"value\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Json\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Promotion\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"scope\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"PromotionScope\",\"default\":\"PRODUCT\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"usage\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"PromotionUsage\",\"default\":\"ONCE\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"discountPercent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Percentage off, 0-100 (legacy discount_percent).\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"platform\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":3,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Which storefront it shows on, mirroring Product.showType (legacy platform).\"},{\"name\":\"startsAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"endsAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Exactly one of these is populated, per `scope`.\"},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"ProductToPromotion\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"categoryId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"category\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Category\",\"relationName\":\"CategoryToPromotion\",\"relationFromFields\":[\"categoryId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToPromotion\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"brandId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"brand\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Brand\",\"relationName\":\"BrandToPromotion\",\"relationFromFields\":[\"brandId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"PromotionExclusion\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"type\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"itemId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[[\"type\",\"itemId\"]],\"uniqueIndexes\":[{\"name\":null,\"fields\":[\"type\",\"itemId\"]}],\"isGenerated\":false,\"documentation\":\"Legacy \\\"ignore\\\" table (views/offersIgnore) — specific products, categories,\\\\ncustomers or brands excluded from the automatic discount engine.\\\\nLegacy posts { type, item_id[] }: \\\"type\\\" reuses the promotion scope codes\\\\n(1 محصولات | 2 دسته بندی | 3 مشتری | 4 برند) and \\\"itemId\\\" is the excluded row.\"},\"BranchAllocation\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branchId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branch\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Branch\",\"relationName\":\"BranchToBranchAllocation\",\"relationFromFields\":[\"branchId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocatorAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `allocator_user` — the admin who filed the allocation.\"},{\"name\":\"allocatorAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AllocationAuthor\",\"relationFromFields\":[\"allocatorAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"note\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"items\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocationItem\",\"relationName\":\"BranchAllocationToBranchAllocationItem\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"BranchAllocationItem\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocationId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocation\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocation\",\"relationName\":\"BranchAllocationToBranchAllocationItem\",\"relationFromFields\":[\"allocationId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"Cascade\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"BranchAllocationItemToProduct\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"weightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Weight snapshot at allocation time, so later catalog edits do not rewrite history.\"}],\"primaryKey\":null,\"uniqueFields\":[[\"allocationId\",\"productId\"]],\"uniqueIndexes\":[{\"name\":null,\"fields\":[\"allocationId\",\"productId\"]}],\"isGenerated\":false},\"SmsMessage\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"audience\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MessageAudience\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"recipients\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Comma-separated recipients for the SINGLE/MULTIPLE audiences.\"},{\"name\":\"body\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentCount\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"How many numbers the send actually resolved to.\"},{\"name\":\"sentByAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentByAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"SmsAuthor\",\"relationFromFields\":[\"sentByAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Notification\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"body\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"audience\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"MessageAudience\",\"default\":\"ALL_CUSTOMERS\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentByAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentByAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"NotificationAuthor\",\"relationFromFields\":[\"sentByAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"recipients\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"NotificationRecipient\",\"relationName\":\"NotificationToNotificationRecipient\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"NotificationRecipient\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"notificationId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"notification\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Notification\",\"relationName\":\"NotificationToNotificationRecipient\",\"relationFromFields\":[\"notificationId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"Cascade\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToNotificationRecipient\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"readAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[[\"notificationId\",\"customerId\"]],\"uniqueIndexes\":[{\"name\":null,\"fields\":[\"notificationId\",\"customerId\"]}],\"isGenerated\":false,\"documentation\":\"Per-customer delivery + read state — legacy `GET notify/read/{id}`.\"},\"NewsletterSubscriber\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"email\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mobile\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[[\"email\"],[\"mobile\"]],\"uniqueIndexes\":[{\"name\":null,\"fields\":[\"email\"]},{\"name\":null,\"fields\":[\"mobile\"]}],\"isGenerated\":false,\"documentation\":\"Legacy views/newsletters — the mailing-list signups collected by the site.\"},\"CustomerActivityLog\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToCustomerActivityLog\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"action\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Free-form action key, e.g. 'login', 'order.placed', 'profile.updated'.\"},{\"name\":\"detail\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"Legacy views/statics — the customer activity log the \\\"آمار\\\" screen reads\\\\n(`log/list/customer`, `log/ago/{customerId}`).\"},\"UploadedFile\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"url\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Public URL the stored file is served from.\"},{\"name\":\"originalName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mimeType\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sizeBytes\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"purpose\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Free-form tag for what the file belongs to, e.g. 'product', 'category',\\\\n'customer.nationalId'. Kept loose so a new form needs no schema change.\"},{\"name\":\"uploadedByAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"uploadedByAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"FileUploader\",\"relationFromFields\":[\"uploadedByAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerAvatars\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerAvatar\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false}},\"enums\":{\"AdminRole\":{\"values\":[{\"name\":\"SUPER_ADMIN\",\"dbName\":null},{\"name\":\"SYSTEM_APPROVER\",\"dbName\":null},{\"name\":\"SALES_EXPERT\",\"dbName\":null},{\"name\":\"WORKSHOP_SUPERVISOR\",\"dbName\":null},{\"name\":\"ACCOUNTANT\",\"dbName\":null}],\"dbName\":null},\"CustomerKind\":{\"values\":[{\"name\":\"SALES_AGENT\",\"dbName\":null},{\"name\":\"APP_SHOPKEEPER\",\"dbName\":null},{\"name\":\"WALK_IN_SHOPKEEPER\",\"dbName\":null},{\"name\":\"BANKER\",\"dbName\":null},{\"name\":\"WORKSHOP_SUPERVISOR\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Legacy Customer.type — what kind of wholesale customer this is. Values and\\\\nlabels taken verbatim from bulkCustomer.vue's userTypes list.\"},\"VerificationStep\":{\"values\":[{\"name\":\"UNVERIFIED\",\"dbName\":null},{\"name\":\"STEP_ONE_SUBMITTED\",\"dbName\":null},{\"name\":\"STEP_TWO_SUBMITTED\",\"dbName\":null},{\"name\":\"VERIFIED\",\"dbName\":null}],\"dbName\":null},\"OrderChannel\":{\"values\":[{\"name\":\"RETAIL\",\"dbName\":null},{\"name\":\"WHOLESALE\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Legacy kept retail and wholesale orders in two SEPARATE tables\\\\n(orders_orders vs orders). The redesigned schema has one Order model, so the\\\\ndistinction lives here instead.\"},\"OrderStatus\":{\"values\":[{\"name\":\"SUSPENDED\",\"dbName\":null},{\"name\":\"PLACED\",\"dbName\":null},{\"name\":\"ADMIN_APPROVED\",\"dbName\":null},{\"name\":\"SOURCING\",\"dbName\":null},{\"name\":\"PREPARING\",\"dbName\":null},{\"name\":\"DELIVERED_TO_AGENT\",\"dbName\":null},{\"name\":\"DELIVERED_TO_CUSTOMER\",\"dbName\":null},{\"name\":\"CANCELLED\",\"dbName\":null}],\"dbName\":null},\"PreOrderStatus\":{\"values\":[{\"name\":\"PLACED\",\"dbName\":null},{\"name\":\"ADMIN_APPROVED\",\"dbName\":null},{\"name\":\"WORKSHOP_APPROVED\",\"dbName\":null},{\"name\":\"READY_TO_SHIP\",\"dbName\":null},{\"name\":\"DELIVERED\",\"dbName\":null},{\"name\":\"CANCELLED\",\"dbName\":null}],\"dbName\":null},\"LedgerCurrency\":{\"values\":[{\"name\":\"GOLD_GRAM\",\"dbName\":null},{\"name\":\"RIAL\",\"dbName\":null}],\"dbName\":null},\"LedgerDirection\":{\"values\":[{\"name\":\"DEBIT\",\"dbName\":null},{\"name\":\"CREDIT\",\"dbName\":null}],\"dbName\":null},\"LedgerSourceType\":{\"values\":[{\"name\":\"ORDER_STOCK\",\"dbName\":null},{\"name\":\"ORDER_PRE_ORDER\",\"dbName\":null},{\"name\":\"REPAYMENT_MELTED_GOLD\",\"dbName\":null},{\"name\":\"REPAYMENT_TRANSFER\",\"dbName\":null},{\"name\":\"REPAYMENT_CHEQUE\",\"dbName\":null},{\"name\":\"REPAYMENT_CASH\",\"dbName\":null},{\"name\":\"WALLET_BONUS\",\"dbName\":null},{\"name\":\"ADJUSTMENT\",\"dbName\":null}],\"dbName\":null},\"ProductImageRole\":{\"values\":[{\"name\":\"FEATURED\",\"dbName\":null},{\"name\":\"FEATURED_MOBILE\",\"dbName\":null},{\"name\":\"GALLERY\",\"dbName\":null},{\"name\":\"GALLERY_MOBILE\",\"dbName\":null}],\"dbName\":null},\"CommentStatus\":{\"values\":[{\"name\":\"PENDING\",\"dbName\":null},{\"name\":\"APPROVED\",\"dbName\":null},{\"name\":\"REJECTED\",\"dbName\":null}],\"dbName\":null},\"PromotionScope\":{\"values\":[{\"name\":\"PRODUCT\",\"dbName\":null},{\"name\":\"CATEGORY\",\"dbName\":null},{\"name\":\"CUSTOMER\",\"dbName\":null},{\"name\":\"BRAND\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Legacy Promotion.type — what the discount is scoped to.\"},\"PromotionUsage\":{\"values\":[{\"name\":\"ONCE\",\"dbName\":null},{\"name\":\"UNTIL_END_DATE\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Legacy Promotion.use_only — how often a customer may redeem it.\"},\"MessageAudience\":{\"values\":[{\"name\":\"SINGLE_NUMBER\",\"dbName\":null},{\"name\":\"MULTIPLE_NUMBERS\",\"dbName\":null},{\"name\":\"ALL_CUSTOMERS\",\"dbName\":null},{\"name\":\"WHOLESALE_CUSTOMERS\",\"dbName\":null},{\"name\":\"RETAIL_CUSTOMERS\",\"dbName\":null},{\"name\":\"SALES_AGENTS\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Who an SMS or notification was aimed at. Legacy's sms/create offered these\\\\naudiences, each gated by its own rid (sms.create.all, .omde, .single, ...).\"}},\"types\":{}}") +config.runtimeDataModel = JSON.parse("{\"models\":{\"City\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"province\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"percent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy cities.percent / percent_aux — per-city wage surcharges the city\\\\nscreen edits through GET cities/get-percent/{id} and get-percent-aux/{id}.\"},{\"name\":\"percentAux\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customers\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CityToCustomer\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"addresses\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CustomerAddress\",\"relationName\":\"CityToCustomerAddress\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branches\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Branch\",\"relationName\":\"BranchToCity\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"CityToPreOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"salesExpertProfiles\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"SalesExpertProfile\",\"relationName\":\"CityToSalesExpertProfile\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"AdminUser\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fullName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"⚠️ Kept in sync BY A DATABASE TRIGGER — `admin_user_sync_full_name`, from\\\\nmigration 20260922180000. Prisma cannot see it, so a write here may store\\\\nsomething other than what you passed:\\\\n\\\\n* write the parts -> fullName is rebuilt as \\\"first last\\\"\\\\n* write fullName only -> the parts are re-derived from it\\\\n* write BOTH in one update -> THE PARTS WIN; the submitted fullName is\\\\ndiscarded\\\\n\\\\nRe-read after writing if you need the stored value. Same arrangement as\\\\nCustomer (D0 requirement 7 — the split applies to ALL admins, not only\\\\nsales experts).\"},{\"name\":\"firstName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"lastName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mobile\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"passwordHash\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"role\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminRole\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"userTypeId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `users.ut`. Granular ACL lives here (Permission rows hang off UserType);\\\\nthe coarse `role` enum above is retained for the business rules that already\\\\nswitch on it. Nullable so existing seeded admins remain valid.\"},{\"name\":\"userType\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UserType\",\"relationName\":\"AdminUserToUserType\",\"relationFromFields\":[\"userTypeId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branchId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branch\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Branch\",\"relationName\":\"AdminUserToBranch\",\"relationFromFields\":[\"branchId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"wagePercent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"avatarFileId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy stored a sales agent as a `customers` row with type=1, so\\\\nReagentCustomer.vue's shared form collected these alongside the\\\\nwholesale-customer fields. A sales agent is now an AdminUser\\\\n(role=SALES_EXPERT), so these live here instead — only ever set for that\\\\nrole; every other AdminUser leaves them null.\"},{\"name\":\"avatarFile\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UploadedFile\",\"relationName\":\"AdminUserAvatar\",\"relationFromFields\":[\"avatarFileId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"address\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"storeName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"pasazhName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"memberCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"referredCustomers\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"ReferralAgent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"supervisedWorkshops\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Workshop\",\"relationName\":\"WorkshopSupervisor\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branchAllocations\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocation\",\"relationName\":\"AllocationAuthor\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"postedLedgerEntries\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerEntry\",\"relationName\":\"AdminUserToLedgerEntry\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderStatusEvents\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderStatusEvent\",\"relationName\":\"AdminUserToOrderStatusEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrderStatusEvents\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrderStatusEvent\",\"relationName\":\"AdminUserToPreOrderStatusEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentSmsMessages\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"SmsMessage\",\"relationName\":\"SmsAuthor\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentNotifications\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Notification\",\"relationName\":\"NotificationAuthor\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"uploadedFiles\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UploadedFile\",\"relationName\":\"FileUploader\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"salesExpertProfile\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"SalesExpertProfile\",\"relationName\":\"AdminUserToSalesExpertProfile\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Only ever set for role = SALES_EXPERT (D17).\"}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"SalesExpertProfile\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"adminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"adminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToSalesExpertProfile\",\"relationFromFields\":[\"adminUserId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"Cascade\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy customers.city_id — a filterable grid column\\\\n(ReagentListModel.js:14) and the source of the commission autofill\\\\n(ReagentCustomer.vue:215-228).\"},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"CityToSalesExpertProfile\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"gramLevel\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy customers.gram_level — the «سطح اعتبار» column, filterable.\\\\nDecimal(14,4) matches Customer.goldCreditLimitGrams exactly so the same\\\\ncredit limit has identical precision on both tables.\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy customers.phone (varchar 15) and ncode (varchar 20). The reference\\\\nstores both and reads neither back; kept so the form matches it. Neither\\\\nis unique and neither has a validation rule in the reference — inventing\\\\none would reject data the reference accepts.\"},{\"name\":\"nationalCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tradingCustomerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"The Customer row carrying this expert's OWN orders, debt and credit limit.\\\\n\\\\nThe reference stored a sales agent AS a Customer (type=1), so an order the\\\\nagent placed from the app was simply their own, against their own\\\\ngram_level (ApiMobileController.php:534-535). Here the expert is an\\\\nAdminUser (D17), which has no ledger and cannot be an Order's subject, so\\\\nthis one linked Customer plays that part.\\\\n\\\\nDistinct from the expert's REFERRED customers, which are ordinary Customer\\\\nrows pointing at the AdminUser through Customer.referredByAgentId.\\\\n\\\\nNull until the expert first signs into the app, when it is created from\\\\nthis profile. See D21.\"},{\"name\":\"tradingCustomer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"SalesExpertTradingAccount\",\"relationFromFields\":[\"tradingCustomerId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"SetNull\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"The sales-expert fields legacy kept on the `customers` row (type=1) and that\\\\nwere lost when a sales expert became an AdminUser (D17).\\\\n\\\\nA separate table rather than columns on AdminUser: these are meaningful only\\\\nfor role = SALES_EXPERT and would be permanently null for every other admin.\\\\nA row is created for every SALES_EXPERT by the migration, and upserted by the\\\\nservice whenever one is created or a user's role becomes SALES_EXPERT — but\\\\nreaders still left-join, so a missing profile degrades to nulls rather than\\\\ndropping the agent from the grid.\\\\n\\\\n`reagent_id` is deliberately NOT here — see D17: in the reference it was a\\\\nside effect of the shared customers form, and filling it removed that expert\\\\nfrom the selectable-agent list (queryReagent, AdminController.php:420).\"},\"Customer\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fullName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"⚠️ `fullName`, `firstName` and `lastName` are kept in sync BY A DATABASE\\\\nTRIGGER — `customer_sync_full_name`, created in migration\\\\n20260922120000_customer_name_parts. Prisma does not know about it, so a\\\\nwrite here is NOT the whole story:\\\\n\\\\n* write the parts -> fullName is rebuilt as \\\"first last\\\"\\\\n* write fullName only -> the parts are re-derived from it\\\\n* write BOTH in one update -> THE PARTS WIN; fullName is overwritten\\\\nwith \\\"first last\\\" and whatever you passed for fullName is discarded\\\\n\\\\nSo a value you wrote can differ from the value that lands in the row.\\\\nRe-read after writing if you need the stored value. Prefer writing the\\\\nparts; `fullName` exists for the legacy readers that predate D0.\\\\n\\\\nReference has these as two columns from the start\\\\n(henza_server 2020_10_01_120430_customers_table.php:18-19 fname/lname);\\\\nthe port collapsed them, which is the D0 defect.\"},{\"name\":\"firstName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"lastName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mobile\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"nationalCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"passwordHash\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"CityToCustomer\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"kind\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"CustomerKind\",\"default\":\"APP_SHOPKEEPER\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Customer.type — distinguishes بنکدار from مغازه دار, which the\\\\nBankerList / ShopkeeperList screens filter on.\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy contact + shop columns, shown on the wholesale customer form and as\\\\n(hidden-by-default) columns in its grid.\"},{\"name\":\"address\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"storeName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"pasazhName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"zone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"memberCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy mcode — the operator-assigned customer code.\"},{\"name\":\"referredByAgentId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"referredByAgent\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"ReferralAgent\",\"relationFromFields\":[\"referredByAgentId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"salesExpertTradingFor\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"SalesExpertProfile\",\"relationName\":\"SalesExpertTradingAccount\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Set only on the one Customer row that acts as a sales expert's own trading\\\\naccount (see SalesExpertProfile.tradingCustomerId). Empty for every\\\\nordinary customer.\"},{\"name\":\"goldCreditLimitGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"goldDebtGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fiatDebtRial\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"makingChargeOffsetPercent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `customers.percent` — the per-customer making-charge offset. It is\\\\nADDED to the variant's making charge when pricing an order line\\\\n(henza_pwa src/Mixins/product/index.ts:69-75:\\\\ngetWage(v) = state.percent + v.percentage_pay)\\\\nand it shifts the catalogue making-charge filter bounds\\\\n(ApiMobileController.php:391-396,76-77). It is NOT part of the price\\\\nformula itself — that extra percent is ConfigStatic['product_price_extra_percent'].\\\\n\\\\nNOT the wallet cashback rate (storefront ConfigStatic['percent'], D13) and\\\\nNOT the wholesale per-line charge (OrderItem.wagePercentSnapshot).\\\\n\\\\nDB default 0 keeps every existing customer's price unchanged. The\\\\nreference's application-layer default for NEW customers is 3\\\\n(AdminController.php:338) and the field is required on create (:354).\"},{\"name\":\"verificationStep\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"VerificationStep\",\"default\":\"UNVERIFIED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"nationalIdImageUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"businessLicenseUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"shopImageUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"avatarFileId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `image_id` — the profile photo (عکس پروفایل) on the sales-agent\\\\nform. Legacy stored an FK to its Image table and read it back as a nested\\\\n`image: { id, image }`, so this is a relation, not a bare URL string.\"},{\"name\":\"avatarFile\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UploadedFile\",\"relationName\":\"CustomerAvatar\",\"relationFromFields\":[\"avatarFileId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"canPlaceOrders\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy had TWO independent switches on a customer:\\\\nsts -> may they place orders (this field)\\\\nactive -> is the account enabled (isActive, below)\\\\nThe port previously drove both from isActive; they are separate again.\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isArchived\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy archive flag — the archived list filtered on this, not on isActive.\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"addresses\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CustomerAddress\",\"relationName\":\"CustomerToCustomerAddress\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"CustomerToOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"CustomerToPreOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ledgerEntries\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerEntry\",\"relationName\":\"CustomerToLedgerEntry\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"walletBonuses\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"WalletBonus\",\"relationName\":\"CustomerToWalletBonus\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"comments\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Comment\",\"relationName\":\"CommentToCustomer\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"promotions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Promotion\",\"relationName\":\"CustomerToPromotion\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"notifications\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"NotificationRecipient\",\"relationName\":\"CustomerToNotificationRecipient\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"activityLogs\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CustomerActivityLog\",\"relationName\":\"CustomerToCustomerActivityLog\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"otpChallenges\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CustomerOtp\",\"relationName\":\"CustomerToCustomerOtp\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"CustomerAddress\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToCustomerAddress\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"CityToCustomerAddress\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"addressLine\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isDefault\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Branch\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"address\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Branch.address / phone — both are columns in the branch grid.\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy GET branch/active/{id} toggles this.\"},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"BranchToCity\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"adminUsers\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToBranch\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"BranchToOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocations\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocation\",\"relationName\":\"BranchToBranchAllocation\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Workshop\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"address\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Workshop.address / phone — both are columns in the workshop grid.\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"supervisorId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"The supervisor created alongside the workshop by the legacy Add form.\"},{\"name\":\"supervisor\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"WorkshopSupervisor\",\"relationFromFields\":[\"supervisorId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"PreOrderToWorkshop\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Category\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"type\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":1,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Category.type — which storefront the category belongs to:\\\\n1 سایت | 2 اپلیکشن موبایل. Drives the \\\"type_str\\\" grid column.\"},{\"name\":\"imageUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Category.image / icon — the grid renders the image, the storefront the icon.\"},{\"name\":\"iconUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"metaTag\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy meta tag + description, used for SEO on the site categories.\"},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"parentId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"parent\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Category\",\"relationName\":\"CategoryTree\",\"relationFromFields\":[\"parentId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"children\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Category\",\"relationName\":\"CategoryTree\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"products\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"CategoryToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"promotions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Promotion\",\"relationName\":\"CategoryToPromotion\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Brand\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"products\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"BrandToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"promotions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Promotion\",\"relationName\":\"BrandToPromotion\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Collection\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"products\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CollectionProduct\",\"relationName\":\"CollectionToCollectionProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"Curated product groupings — replaces legacy \\\"Ciny/CinyProduct\\\" (a tagging\\\\nfeature, NOT a gold-price snapshot mechanism, despite the misleading legacy name).\"},\"CollectionProduct\":{\"dbName\":null,\"fields\":[{\"name\":\"collectionId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"collection\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Collection\",\"relationName\":\"CollectionToCollectionProduct\",\"relationFromFields\":[\"collectionId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"CollectionProductToProduct\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":{\"name\":null,\"fields\":[\"collectionId\",\"productId\"]},\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Product\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"categoryId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"category\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Category\",\"relationName\":\"CategoryToProduct\",\"relationFromFields\":[\"categoryId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"brandId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"brand\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Brand\",\"relationName\":\"BrandToProduct\",\"relationFromFields\":[\"brandId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Product.product_code — the human-facing SKU shown in the grid.\"},{\"name\":\"showType\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":3,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Product.show_type: 1 site, 2 mobile, 3 both.\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"variants\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ProductVariant\",\"relationName\":\"ProductToProductVariant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"images\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ProductImage\",\"relationName\":\"ProductToProductImage\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"collections\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CollectionProduct\",\"relationName\":\"CollectionProductToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"comments\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Comment\",\"relationName\":\"CommentToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"promotions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Promotion\",\"relationName\":\"ProductToPromotion\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocationItems\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocationItem\",\"relationName\":\"BranchAllocationItemToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"ProductImage\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"ProductToProductImage\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"url\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sortOrder\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"role\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"ProductImageRole\",\"default\":\"GALLERY\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy had 4 distinct upload slots (تصویر شاخص / تصویر شاخص موبایل /\\\\nگالری تصاویر سایت / گالری تصاویر موبایل) — GALLERY is the default so\\\\nexisting rows and any generic \\\"just attach an image\\\" caller keep working.\"},{\"name\":\"variants\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ProductVariant\",\"relationName\":\"ProductImageToProductVariant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Variants that display this image. A gallery image may back several\\\\nvariants (one photo, several weights), which is why this is a plain\\\\none-to-many and not a join table.\"}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"ProductVariant\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"ProductToProductVariant\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sku\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rashWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Base/raw weight before making charge (\\\"rash weight\\\").\"},{\"name\":\"defaultWagePercent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Default making-charge percentage applied on top of rash weight.\"},{\"name\":\"qty\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy ProductVariant.qty — on-hand stock. The cart refuses a variant with\\\\nqty 0 unless it is flagged pre-order, so both fields are required for that\\\\nguard to work (see apps/admin-panel/src/stores/cart.ts).\"},{\"name\":\"isPreOrder\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy is_pre_order: orderable even at zero stock.\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"imageId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"The gallery image shown for this variant, picked from its own product's\\\\nimages — never a re-uploaded copy. onDelete: SetNull because dropping an\\\\nimage must never cascade into the variant: OrderItem rows reference\\\\nvariants, so losing one would break order history.\"},{\"name\":\"image\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ProductImage\",\"relationName\":\"ProductImageToProductVariant\",\"relationFromFields\":[\"imageId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"SetNull\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderItems\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderItem\",\"relationName\":\"OrderItemToProductVariant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Order\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderNumber\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToOrder\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branchId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branch\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Branch\",\"relationName\":\"BranchToOrder\",\"relationFromFields\":[\"branchId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"channel\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"OrderChannel\",\"default\":\"RETAIL\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Which sales channel placed this order — see OrderChannel.\"},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"OrderStatus\",\"default\":\"PLACED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"totalGoldWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Total gold weight incl. wage across all items (\\\"allGram\\\" / order.w).\"},{\"name\":\"wageWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Pure making-charge weight component (totalGoldWeightGrams minus raw weight sum).\"},{\"name\":\"flatRialCharge\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Flat Rial charge independent of gold price (e.g. stones) — legacy rial_input.\"},{\"name\":\"isSuspendedOverCreditLimit\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"deliveredAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"items\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderItem\",\"relationName\":\"OrderToOrderItem\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"statusEvents\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderStatusEvent\",\"relationName\":\"OrderToOrderStatusEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ledgerEntries\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerEntry\",\"relationName\":\"LedgerEntryToOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"convertedFromPreOrder\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"OrderToPreOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"OrderItem\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"order\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"OrderToOrderItem\",\"relationFromFields\":[\"orderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productVariantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productVariant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ProductVariant\",\"relationName\":\"OrderItemToProductVariant\",\"relationFromFields\":[\"productVariantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"quantity\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rashWeightGramsSnapshot\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"wagePercentSnapshot\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"discountPercentSnapshot\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"computedWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"computed: rash*(1 + (wagePercent-discount)/100)\"},{\"name\":\"flatRialCharge\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"OrderStatusEvent\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"order\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"OrderToOrderStatusEvent\",\"relationFromFields\":[\"orderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fromStatus\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderStatus\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"toStatus\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderStatus\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"note\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"actorAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"actorAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToOrderStatusEvent\",\"relationFromFields\":[\"actorAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"PreOrder\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToPreOrder\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"CityToPreOrder\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"workshopId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"workshop\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Workshop\",\"relationName\":\"PreOrderToWorkshop\",\"relationFromFields\":[\"workshopId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"PreOrderStatus\",\"default\":\"PLACED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"designImageUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"colorName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sizeSpec\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"stoneColor\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"minaColor\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"estimatedWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"estimatedPriceRial\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"convertedOrderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Once priced, an admin converts this into a real (debt-bearing) Order.\"},{\"name\":\"convertedOrder\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"OrderToPreOrder\",\"relationFromFields\":[\"convertedOrderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"statusEvents\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrderStatusEvent\",\"relationName\":\"PreOrderToPreOrderStatusEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"PreOrderStatusEvent\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrder\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"PreOrderToPreOrderStatusEvent\",\"relationFromFields\":[\"preOrderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"changes\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Json\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Field-level diff, e.g. {\\\"status\\\": {\\\"old\\\": \\\"PLACED\\\", \\\"new\\\": \\\"ADMIN_APPROVED\\\"}}\"},{\"name\":\"actorAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"actorAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToPreOrderStatusEvent\",\"relationFromFields\":[\"actorAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ipAddress\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"LedgerEntry\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToLedgerEntry\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"currency\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerCurrency\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"direction\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerDirection\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sourceType\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerSourceType\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"amount\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Always positive; direction determines the sign applied to the customer balance.\"},{\"name\":\"karatPerMille\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Purity of the physical instrument, for REPAYMENT_MELTED_GOLD (e.g. 750, 916).\"},{\"name\":\"pricePerGramRialAtEntry\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Gold price/gram at conversion time, for REPAYMENT_CASH.\"},{\"name\":\"orderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"order\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"LedgerEntryToOrder\",\"relationFromFields\":[\"orderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"confirmed\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Pending entries don't count toward the cached customer balance until confirmed.\"},{\"name\":\"confirmedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"postedByAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"postedByAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToLedgerEntry\",\"relationFromFields\":[\"postedByAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"labName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Instrument metadata from the legacy \\\"cash\\\" document form. Which fields are\\\\nfilled depends on sourceType: a melted-gold receipt carries lab/hallmark and\\\\nthe pre-normalization weight, a cheque carries the account and bank, and a\\\\ntransfer carries its destination.\\\\nLegacy columns: lab, ang, account_name, bank_name, to, original_value, recive_date.\"},{\"name\":\"hallmark\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"accountName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"bankName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"transferTo\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"originalValue\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Weight as handed over, BEFORE karat normalization (legacy original_value).\"},{\"name\":\"priceRial\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Rial amount for cash/cheque documents (legacy price).\"},{\"name\":\"receivedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"note\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"GoldPriceQuote\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"karatPerMille\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"pricePerGramRial\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"source\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"quotedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"Replaces legacy WebServiceGold / ConfigStatic.gold_price — timestamped price feed.\"},\"WalletBonus\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToWalletBonus\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"goldGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"grantedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"consumedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"Time-limited gold cashback bonus — replaces the legacy Rooyesh\\\\Wallet gold credit.\"},\"UserType\":{\"dbName\":\"user_type\",\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `permission.ut` / `users.ut` — an integer, kept as the PK so seeded\\\\nrows keep their legacy identity rather than getting new cuids.\"},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"permissions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Permission\",\"relationName\":\"PermissionToUserType\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"adminUsers\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToUserType\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Permission\":{\"dbName\":\"permission\",\"fields\":[{\"name\":\"rid\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Dotted permission id, e.g. \\\"order.list\\\", \\\"persons.list.client.omde\\\".\"},{\"name\":\"userTypeId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"userType\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UserType\",\"relationName\":\"PermissionToUserType\",\"relationFromFields\":[\"userTypeId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"Cascade\",\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":{\"name\":null,\"fields\":[\"rid\",\"userTypeId\"]},\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Comment\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"CommentToProduct\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CommentToCustomer\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"body\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"CommentStatus\",\"default\":\"PENDING\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"replyToId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `reply_to`: a staff reply threads under the comment it answers.\"},{\"name\":\"replyTo\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Comment\",\"relationName\":\"CommentReplies\",\"relationFromFields\":[\"replyToId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"replies\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Comment\",\"relationName\":\"CommentReplies\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Setting\":{\"dbName\":null,\"fields\":[{\"name\":\"key\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"value\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Json\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Promotion\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"scope\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"PromotionScope\",\"default\":\"PRODUCT\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"usage\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"PromotionUsage\",\"default\":\"ONCE\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"discountPercent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Percentage off, 0-100 (legacy discount_percent).\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"platform\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":3,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Which storefront it shows on, mirroring Product.showType (legacy platform).\"},{\"name\":\"startsAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"endsAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Exactly one of these is populated, per `scope`.\"},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"ProductToPromotion\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"categoryId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"category\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Category\",\"relationName\":\"CategoryToPromotion\",\"relationFromFields\":[\"categoryId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToPromotion\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"brandId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"brand\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Brand\",\"relationName\":\"BrandToPromotion\",\"relationFromFields\":[\"brandId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"PromotionExclusion\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"type\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"itemId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[[\"type\",\"itemId\"]],\"uniqueIndexes\":[{\"name\":null,\"fields\":[\"type\",\"itemId\"]}],\"isGenerated\":false,\"documentation\":\"Legacy \\\"ignore\\\" table (views/offersIgnore) — specific products, categories,\\\\ncustomers or brands excluded from the automatic discount engine.\\\\nLegacy posts { type, item_id[] }: \\\"type\\\" reuses the promotion scope codes\\\\n(1 محصولات | 2 دسته بندی | 3 مشتری | 4 برند) and \\\"itemId\\\" is the excluded row.\"},\"BranchAllocation\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branchId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branch\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Branch\",\"relationName\":\"BranchToBranchAllocation\",\"relationFromFields\":[\"branchId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocatorAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `allocator_user` — the admin who filed the allocation.\"},{\"name\":\"allocatorAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AllocationAuthor\",\"relationFromFields\":[\"allocatorAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"note\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"items\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocationItem\",\"relationName\":\"BranchAllocationToBranchAllocationItem\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"BranchAllocationItem\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocationId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocation\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocation\",\"relationName\":\"BranchAllocationToBranchAllocationItem\",\"relationFromFields\":[\"allocationId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"Cascade\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"BranchAllocationItemToProduct\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"weightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Weight snapshot at allocation time, so later catalog edits do not rewrite history.\"}],\"primaryKey\":null,\"uniqueFields\":[[\"allocationId\",\"productId\"]],\"uniqueIndexes\":[{\"name\":null,\"fields\":[\"allocationId\",\"productId\"]}],\"isGenerated\":false},\"SmsMessage\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"audience\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MessageAudience\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"recipients\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Comma-separated recipients for the SINGLE/MULTIPLE audiences.\"},{\"name\":\"body\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentCount\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"How many numbers the send actually resolved to.\"},{\"name\":\"sentByAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentByAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"SmsAuthor\",\"relationFromFields\":[\"sentByAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Notification\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"body\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"audience\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"MessageAudience\",\"default\":\"ALL_CUSTOMERS\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentByAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentByAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"NotificationAuthor\",\"relationFromFields\":[\"sentByAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"recipients\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"NotificationRecipient\",\"relationName\":\"NotificationToNotificationRecipient\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"NotificationRecipient\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"notificationId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"notification\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Notification\",\"relationName\":\"NotificationToNotificationRecipient\",\"relationFromFields\":[\"notificationId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"Cascade\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToNotificationRecipient\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"readAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[[\"notificationId\",\"customerId\"]],\"uniqueIndexes\":[{\"name\":null,\"fields\":[\"notificationId\",\"customerId\"]}],\"isGenerated\":false,\"documentation\":\"Per-customer delivery + read state — legacy `GET notify/read/{id}`.\"},\"NewsletterSubscriber\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"email\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mobile\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[[\"email\"],[\"mobile\"]],\"uniqueIndexes\":[{\"name\":null,\"fields\":[\"email\"]},{\"name\":null,\"fields\":[\"mobile\"]}],\"isGenerated\":false,\"documentation\":\"Legacy views/newsletters — the mailing-list signups collected by the site.\"},\"CustomerActivityLog\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToCustomerActivityLog\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"action\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Free-form action key, e.g. 'login', 'order.placed', 'profile.updated'.\"},{\"name\":\"detail\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"Legacy views/statics — the customer activity log the \\\"آمار\\\" screen reads\\\\n(`log/list/customer`, `log/ago/{customerId}`).\"},\"UploadedFile\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"url\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Public URL the stored file is served from.\"},{\"name\":\"originalName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mimeType\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sizeBytes\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"purpose\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Free-form tag for what the file belongs to, e.g. 'product', 'category',\\\\n'customer.nationalId'. Kept loose so a new form needs no schema change.\"},{\"name\":\"uploadedByAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"uploadedByAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"FileUploader\",\"relationFromFields\":[\"uploadedByAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerAvatars\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerAvatar\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"adminUserAvatars\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserAvatar\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"CustomerOtp\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mobile\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Normalised to English digits before storing, as the reference does with\\\\nUtility::faTOen() on both send and verify.\"},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToCustomerOtp\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"Cascade\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"codeHash\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"bcrypt hash of the 4-digit code. Never the code itself.\"},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"attempts\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Failed verify attempts against THIS challenge.\"},{\"name\":\"consumedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Set when the code is accepted, so one challenge cannot be reused.\"},{\"name\":\"requestIp\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Kept for the per-IP send throttle.\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"One OTP login challenge for a customer.\\\\n\\\\nPorts henza_server's flow (ApiMobileController.php:39-78), where userAuth\\\\ngenerated rand(1000,9999), stored it on the customer row as `send_password`\\\\nand sent it by SMS, and userVerfiy compared the submitted code against it\\\\n(mismatch -> HTTP 422).\\\\n\\\\nThree deliberate improvements over the reference, approved by the user\\\\n(docs/parity/DECISIONS.md D6 and T1):\\\\n1. the code is stored as a bcrypt hash, never in plain text — the\\\\nreference kept it readable in a customer column;\\\\n2. it expires (`expiresAt`) — the reference's code stayed valid forever;\\\\n3. attempts are counted and the challenge locks out — the reference had\\\\nno limit at all.\\\\nThe parity behaviour itself is the code comparison and the rejection.\"}},\"enums\":{\"AdminRole\":{\"values\":[{\"name\":\"SUPER_ADMIN\",\"dbName\":null},{\"name\":\"SYSTEM_APPROVER\",\"dbName\":null},{\"name\":\"SALES_EXPERT\",\"dbName\":null},{\"name\":\"WORKSHOP_SUPERVISOR\",\"dbName\":null},{\"name\":\"ACCOUNTANT\",\"dbName\":null}],\"dbName\":null},\"CustomerKind\":{\"values\":[{\"name\":\"SALES_AGENT\",\"dbName\":null},{\"name\":\"APP_SHOPKEEPER\",\"dbName\":null},{\"name\":\"WALK_IN_SHOPKEEPER\",\"dbName\":null},{\"name\":\"BANKER\",\"dbName\":null},{\"name\":\"WORKSHOP_SUPERVISOR\",\"dbName\":null},{\"name\":\"RETAIL\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Legacy Customer.type — what kind of wholesale customer this is. Values and\\\\nlabels taken verbatim from bulkCustomer.vue's userTypes list.\\\\n\\\\nSALES_AGENT is a LEGACY value only: legacy stored a sales agent as a\\\\nCustomer row with type=1, but the redesigned system models a sales agent\\\\nas an AdminUser (role SALES_EXPERT) instead — see admin-users.service.ts\\\\nand admin-customers.service.ts's listReagents()/create(). No Customer row\\\\nshould be created with kind SALES_AGENT going forward; the value is kept\\\\nonly so legacyTypeToKind()/kindToLegacyType() stay total functions and any\\\\nalready-migrated data keeps a readable label.\\\\n\\\\nRETAIL marks a \\\"تک\\\" (single/retail) customer — legacy kept these in an\\\\nentirely separate table (SingleCustomer/`login`) that could never contain\\\\na wholesale/agent/shopkeeper/banker row. The redesigned schema folded both\\\\ninto one Customer model, so this value is what listSingle() now filters on\\\\nto reproduce that same separation.\"},\"VerificationStep\":{\"values\":[{\"name\":\"UNVERIFIED\",\"dbName\":null},{\"name\":\"STEP_ONE_SUBMITTED\",\"dbName\":null},{\"name\":\"STEP_TWO_SUBMITTED\",\"dbName\":null},{\"name\":\"VERIFIED\",\"dbName\":null}],\"dbName\":null},\"OrderChannel\":{\"values\":[{\"name\":\"RETAIL\",\"dbName\":null},{\"name\":\"WHOLESALE\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Legacy kept retail and wholesale orders in two SEPARATE tables\\\\n(orders_orders vs orders). The redesigned schema has one Order model, so the\\\\ndistinction lives here instead.\"},\"OrderStatus\":{\"values\":[{\"name\":\"SUSPENDED\",\"dbName\":null},{\"name\":\"PLACED\",\"dbName\":null},{\"name\":\"ADMIN_APPROVED\",\"dbName\":null},{\"name\":\"SOURCING\",\"dbName\":null},{\"name\":\"PREPARING\",\"dbName\":null},{\"name\":\"DELIVERED_TO_AGENT\",\"dbName\":null},{\"name\":\"DELIVERED_TO_CUSTOMER\",\"dbName\":null},{\"name\":\"CANCELLED\",\"dbName\":null}],\"dbName\":null},\"PreOrderStatus\":{\"values\":[{\"name\":\"PLACED\",\"dbName\":null},{\"name\":\"ADMIN_APPROVED\",\"dbName\":null},{\"name\":\"WORKSHOP_APPROVED\",\"dbName\":null},{\"name\":\"READY_TO_SHIP\",\"dbName\":null},{\"name\":\"DELIVERED\",\"dbName\":null},{\"name\":\"CANCELLED\",\"dbName\":null}],\"dbName\":null},\"LedgerCurrency\":{\"values\":[{\"name\":\"GOLD_GRAM\",\"dbName\":null},{\"name\":\"RIAL\",\"dbName\":null}],\"dbName\":null},\"LedgerDirection\":{\"values\":[{\"name\":\"DEBIT\",\"dbName\":null},{\"name\":\"CREDIT\",\"dbName\":null}],\"dbName\":null},\"LedgerSourceType\":{\"values\":[{\"name\":\"ORDER_STOCK\",\"dbName\":null},{\"name\":\"ORDER_PRE_ORDER\",\"dbName\":null},{\"name\":\"REPAYMENT_MELTED_GOLD\",\"dbName\":null},{\"name\":\"REPAYMENT_TRANSFER\",\"dbName\":null},{\"name\":\"REPAYMENT_CHEQUE\",\"dbName\":null},{\"name\":\"REPAYMENT_CASH\",\"dbName\":null},{\"name\":\"WALLET_BONUS\",\"dbName\":null},{\"name\":\"ADJUSTMENT\",\"dbName\":null}],\"dbName\":null},\"ProductImageRole\":{\"values\":[{\"name\":\"FEATURED\",\"dbName\":null},{\"name\":\"FEATURED_MOBILE\",\"dbName\":null},{\"name\":\"GALLERY\",\"dbName\":null},{\"name\":\"GALLERY_MOBILE\",\"dbName\":null}],\"dbName\":null},\"CommentStatus\":{\"values\":[{\"name\":\"PENDING\",\"dbName\":null},{\"name\":\"APPROVED\",\"dbName\":null},{\"name\":\"REJECTED\",\"dbName\":null}],\"dbName\":null},\"PromotionScope\":{\"values\":[{\"name\":\"PRODUCT\",\"dbName\":null},{\"name\":\"CATEGORY\",\"dbName\":null},{\"name\":\"CUSTOMER\",\"dbName\":null},{\"name\":\"BRAND\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Legacy Promotion.type — what the discount is scoped to.\"},\"PromotionUsage\":{\"values\":[{\"name\":\"ONCE\",\"dbName\":null},{\"name\":\"UNTIL_END_DATE\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Legacy Promotion.use_only — how often a customer may redeem it.\"},\"MessageAudience\":{\"values\":[{\"name\":\"SINGLE_NUMBER\",\"dbName\":null},{\"name\":\"MULTIPLE_NUMBERS\",\"dbName\":null},{\"name\":\"ALL_CUSTOMERS\",\"dbName\":null},{\"name\":\"WHOLESALE_CUSTOMERS\",\"dbName\":null},{\"name\":\"RETAIL_CUSTOMERS\",\"dbName\":null},{\"name\":\"SALES_AGENTS\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Who an SMS or notification was aimed at. Legacy's sms/create offered these\\\\naudiences, each gated by its own rid (sms.create.all, .omde, .single, ...).\"}},\"types\":{}}") defineDmmfProperty(exports.Prisma, config.runtimeDataModel) config.engineWasm = undefined diff --git a/apps/api/release/vendor/.prisma/client/index-browser.js b/apps/api/release/vendor/.prisma/client/index-browser.js index b5f4160..3979a6f 100644 --- a/apps/api/release/vendor/.prisma/client/index-browser.js +++ b/apps/api/release/vendor/.prisma/client/index-browser.js @@ -133,6 +133,8 @@ exports.Prisma.CityScalarFieldEnum = { exports.Prisma.AdminUserScalarFieldEnum = { id: 'id', fullName: 'fullName', + firstName: 'firstName', + lastName: 'lastName', mobile: 'mobile', passwordHash: 'passwordHash', role: 'role', @@ -140,12 +142,31 @@ exports.Prisma.AdminUserScalarFieldEnum = { branchId: 'branchId', wagePercent: 'wagePercent', isActive: 'isActive', - createdAt: 'createdAt' + createdAt: 'createdAt', + avatarFileId: 'avatarFileId', + address: 'address', + storeName: 'storeName', + pasazhName: 'pasazhName', + memberCode: 'memberCode' +}; + +exports.Prisma.SalesExpertProfileScalarFieldEnum = { + id: 'id', + adminUserId: 'adminUserId', + cityId: 'cityId', + gramLevel: 'gramLevel', + phone: 'phone', + nationalCode: 'nationalCode', + tradingCustomerId: 'tradingCustomerId', + createdAt: 'createdAt', + updatedAt: 'updatedAt' }; exports.Prisma.CustomerScalarFieldEnum = { id: 'id', fullName: 'fullName', + firstName: 'firstName', + lastName: 'lastName', mobile: 'mobile', nationalCode: 'nationalCode', passwordHash: 'passwordHash', @@ -161,6 +182,7 @@ exports.Prisma.CustomerScalarFieldEnum = { goldCreditLimitGrams: 'goldCreditLimitGrams', goldDebtGrams: 'goldDebtGrams', fiatDebtRial: 'fiatDebtRial', + makingChargeOffsetPercent: 'makingChargeOffsetPercent', verificationStep: 'verificationStep', nationalIdImageUrl: 'nationalIdImageUrl', businessLicenseUrl: 'businessLicenseUrl', @@ -256,7 +278,8 @@ exports.Prisma.ProductVariantScalarFieldEnum = { defaultWagePercent: 'defaultWagePercent', qty: 'qty', isPreOrder: 'isPreOrder', - isActive: 'isActive' + isActive: 'isActive', + imageId: 'imageId' }; exports.Prisma.OrderScalarFieldEnum = { @@ -491,6 +514,18 @@ exports.Prisma.UploadedFileScalarFieldEnum = { createdAt: 'createdAt' }; +exports.Prisma.CustomerOtpScalarFieldEnum = { + id: 'id', + mobile: 'mobile', + customerId: 'customerId', + codeHash: 'codeHash', + expiresAt: 'expiresAt', + attempts: 'attempts', + consumedAt: 'consumedAt', + requestIp: 'requestIp', + createdAt: 'createdAt' +}; + exports.Prisma.SortOrder = { asc: 'asc', desc: 'desc' @@ -528,7 +563,8 @@ exports.CustomerKind = exports.$Enums.CustomerKind = { APP_SHOPKEEPER: 'APP_SHOPKEEPER', WALK_IN_SHOPKEEPER: 'WALK_IN_SHOPKEEPER', BANKER: 'BANKER', - WORKSHOP_SUPERVISOR: 'WORKSHOP_SUPERVISOR' + WORKSHOP_SUPERVISOR: 'WORKSHOP_SUPERVISOR', + RETAIL: 'RETAIL' }; exports.VerificationStep = exports.$Enums.VerificationStep = { @@ -621,6 +657,7 @@ exports.MessageAudience = exports.$Enums.MessageAudience = { exports.Prisma.ModelName = { City: 'City', AdminUser: 'AdminUser', + SalesExpertProfile: 'SalesExpertProfile', Customer: 'Customer', CustomerAddress: 'CustomerAddress', Branch: 'Branch', @@ -653,7 +690,8 @@ exports.Prisma.ModelName = { NotificationRecipient: 'NotificationRecipient', NewsletterSubscriber: 'NewsletterSubscriber', CustomerActivityLog: 'CustomerActivityLog', - UploadedFile: 'UploadedFile' + UploadedFile: 'UploadedFile', + CustomerOtp: 'CustomerOtp' }; /** diff --git a/apps/api/release/vendor/.prisma/client/index.d.ts b/apps/api/release/vendor/.prisma/client/index.d.ts index b0c7710..74d5c56 100644 --- a/apps/api/release/vendor/.prisma/client/index.d.ts +++ b/apps/api/release/vendor/.prisma/client/index.d.ts @@ -23,6 +23,23 @@ export type City = $Result.DefaultSelection * */ export type AdminUser = $Result.DefaultSelection +/** + * Model SalesExpertProfile + * The sales-expert fields legacy kept on the `customers` row (type=1) and that + * were lost when a sales expert became an AdminUser (D17). + * + * A separate table rather than columns on AdminUser: these are meaningful only + * for role = SALES_EXPERT and would be permanently null for every other admin. + * A row is created for every SALES_EXPERT by the migration, and upserted by the + * service whenever one is created or a user's role becomes SALES_EXPERT — but + * readers still left-join, so a missing profile degrades to nulls rather than + * dropping the agent from the grid. + * + * `reagent_id` is deliberately NOT here — see D17: in the reference it was a + * side effect of the shared customers form, and filling it removed that expert + * from the selectable-agent list (queryReagent, AdminController.php:420). + */ +export type SalesExpertProfile = $Result.DefaultSelection /** * Model Customer * @@ -193,6 +210,25 @@ export type CustomerActivityLog = $Result.DefaultSelection +/** + * Model CustomerOtp + * One OTP login challenge for a customer. + * + * Ports henza_server's flow (ApiMobileController.php:39-78), where userAuth + * generated rand(1000,9999), stored it on the customer row as `send_password` + * and sent it by SMS, and userVerfiy compared the submitted code against it + * (mismatch -> HTTP 422). + * + * Three deliberate improvements over the reference, approved by the user + * (docs/parity/DECISIONS.md D6 and T1): + * 1. the code is stored as a bcrypt hash, never in plain text — the + * reference kept it readable in a customer column; + * 2. it expires (`expiresAt`) — the reference's code stayed valid forever; + * 3. attempts are counted and the challenge locks out — the reference had + * no limit at all. + * The parity behaviour itself is the code comparison and the rejection. + */ +export type CustomerOtp = $Result.DefaultSelection /** * Enums @@ -214,7 +250,8 @@ export const CustomerKind: { APP_SHOPKEEPER: 'APP_SHOPKEEPER', WALK_IN_SHOPKEEPER: 'WALK_IN_SHOPKEEPER', BANKER: 'BANKER', - WORKSHOP_SUPERVISOR: 'WORKSHOP_SUPERVISOR' + WORKSHOP_SUPERVISOR: 'WORKSHOP_SUPERVISOR', + RETAIL: 'RETAIL' }; export type CustomerKind = (typeof CustomerKind)[keyof typeof CustomerKind] @@ -543,6 +580,16 @@ export class PrismaClient< */ get adminUser(): Prisma.AdminUserDelegate; + /** + * `prisma.salesExpertProfile`: Exposes CRUD operations for the **SalesExpertProfile** model. + * Example usage: + * ```ts + * // Fetch zero or more SalesExpertProfiles + * const salesExpertProfiles = await prisma.salesExpertProfile.findMany() + * ``` + */ + get salesExpertProfile(): Prisma.SalesExpertProfileDelegate; + /** * `prisma.customer`: Exposes CRUD operations for the **Customer** model. * Example usage: @@ -872,6 +919,16 @@ export class PrismaClient< * ``` */ get uploadedFile(): Prisma.UploadedFileDelegate; + + /** + * `prisma.customerOtp`: Exposes CRUD operations for the **CustomerOtp** model. + * Example usage: + * ```ts + * // Fetch zero or more CustomerOtps + * const customerOtps = await prisma.customerOtp.findMany() + * ``` + */ + get customerOtp(): Prisma.CustomerOtpDelegate; } export namespace Prisma { @@ -1315,6 +1372,7 @@ export namespace Prisma { export const ModelName: { City: 'City', AdminUser: 'AdminUser', + SalesExpertProfile: 'SalesExpertProfile', Customer: 'Customer', CustomerAddress: 'CustomerAddress', Branch: 'Branch', @@ -1347,7 +1405,8 @@ export namespace Prisma { NotificationRecipient: 'NotificationRecipient', NewsletterSubscriber: 'NewsletterSubscriber', CustomerActivityLog: 'CustomerActivityLog', - UploadedFile: 'UploadedFile' + UploadedFile: 'UploadedFile', + CustomerOtp: 'CustomerOtp' }; export type ModelName = (typeof ModelName)[keyof typeof ModelName] @@ -1363,7 +1422,7 @@ export namespace Prisma { export type TypeMap = { meta: { - modelProps: "city" | "adminUser" | "customer" | "customerAddress" | "branch" | "workshop" | "category" | "brand" | "collection" | "collectionProduct" | "product" | "productImage" | "productVariant" | "order" | "orderItem" | "orderStatusEvent" | "preOrder" | "preOrderStatusEvent" | "ledgerEntry" | "goldPriceQuote" | "walletBonus" | "userType" | "permission" | "comment" | "setting" | "promotion" | "promotionExclusion" | "branchAllocation" | "branchAllocationItem" | "smsMessage" | "notification" | "notificationRecipient" | "newsletterSubscriber" | "customerActivityLog" | "uploadedFile" + modelProps: "city" | "adminUser" | "salesExpertProfile" | "customer" | "customerAddress" | "branch" | "workshop" | "category" | "brand" | "collection" | "collectionProduct" | "product" | "productImage" | "productVariant" | "order" | "orderItem" | "orderStatusEvent" | "preOrder" | "preOrderStatusEvent" | "ledgerEntry" | "goldPriceQuote" | "walletBonus" | "userType" | "permission" | "comment" | "setting" | "promotion" | "promotionExclusion" | "branchAllocation" | "branchAllocationItem" | "smsMessage" | "notification" | "notificationRecipient" | "newsletterSubscriber" | "customerActivityLog" | "uploadedFile" | "customerOtp" txIsolationLevel: Prisma.TransactionIsolationLevel } model: { @@ -1507,6 +1566,76 @@ export namespace Prisma { } } } + SalesExpertProfile: { + payload: Prisma.$SalesExpertProfilePayload + fields: Prisma.SalesExpertProfileFieldRefs + operations: { + findUnique: { + args: Prisma.SalesExpertProfileFindUniqueArgs + result: $Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.SalesExpertProfileFindUniqueOrThrowArgs + result: $Utils.PayloadToResult + } + findFirst: { + args: Prisma.SalesExpertProfileFindFirstArgs + result: $Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.SalesExpertProfileFindFirstOrThrowArgs + result: $Utils.PayloadToResult + } + findMany: { + args: Prisma.SalesExpertProfileFindManyArgs + result: $Utils.PayloadToResult[] + } + create: { + args: Prisma.SalesExpertProfileCreateArgs + result: $Utils.PayloadToResult + } + createMany: { + args: Prisma.SalesExpertProfileCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.SalesExpertProfileCreateManyAndReturnArgs + result: $Utils.PayloadToResult[] + } + delete: { + args: Prisma.SalesExpertProfileDeleteArgs + result: $Utils.PayloadToResult + } + update: { + args: Prisma.SalesExpertProfileUpdateArgs + result: $Utils.PayloadToResult + } + deleteMany: { + args: Prisma.SalesExpertProfileDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.SalesExpertProfileUpdateManyArgs + result: BatchPayload + } + upsert: { + args: Prisma.SalesExpertProfileUpsertArgs + result: $Utils.PayloadToResult + } + aggregate: { + args: Prisma.SalesExpertProfileAggregateArgs + result: $Utils.Optional + } + groupBy: { + args: Prisma.SalesExpertProfileGroupByArgs + result: $Utils.Optional[] + } + count: { + args: Prisma.SalesExpertProfileCountArgs + result: $Utils.Optional | number + } + } + } Customer: { payload: Prisma.$CustomerPayload fields: Prisma.CustomerFieldRefs @@ -3817,6 +3946,76 @@ export namespace Prisma { } } } + CustomerOtp: { + payload: Prisma.$CustomerOtpPayload + fields: Prisma.CustomerOtpFieldRefs + operations: { + findUnique: { + args: Prisma.CustomerOtpFindUniqueArgs + result: $Utils.PayloadToResult | null + } + findUniqueOrThrow: { + args: Prisma.CustomerOtpFindUniqueOrThrowArgs + result: $Utils.PayloadToResult + } + findFirst: { + args: Prisma.CustomerOtpFindFirstArgs + result: $Utils.PayloadToResult | null + } + findFirstOrThrow: { + args: Prisma.CustomerOtpFindFirstOrThrowArgs + result: $Utils.PayloadToResult + } + findMany: { + args: Prisma.CustomerOtpFindManyArgs + result: $Utils.PayloadToResult[] + } + create: { + args: Prisma.CustomerOtpCreateArgs + result: $Utils.PayloadToResult + } + createMany: { + args: Prisma.CustomerOtpCreateManyArgs + result: BatchPayload + } + createManyAndReturn: { + args: Prisma.CustomerOtpCreateManyAndReturnArgs + result: $Utils.PayloadToResult[] + } + delete: { + args: Prisma.CustomerOtpDeleteArgs + result: $Utils.PayloadToResult + } + update: { + args: Prisma.CustomerOtpUpdateArgs + result: $Utils.PayloadToResult + } + deleteMany: { + args: Prisma.CustomerOtpDeleteManyArgs + result: BatchPayload + } + updateMany: { + args: Prisma.CustomerOtpUpdateManyArgs + result: BatchPayload + } + upsert: { + args: Prisma.CustomerOtpUpsertArgs + result: $Utils.PayloadToResult + } + aggregate: { + args: Prisma.CustomerOtpAggregateArgs + result: $Utils.Optional + } + groupBy: { + args: Prisma.CustomerOtpGroupByArgs + result: $Utils.Optional[] + } + count: { + args: Prisma.CustomerOtpCountArgs + result: $Utils.Optional | number + } + } + } } } & { other: { @@ -3982,6 +4181,7 @@ export namespace Prisma { addresses: number branches: number preOrders: number + salesExpertProfiles: number } export type CityCountOutputTypeSelect = { @@ -3989,6 +4189,7 @@ export namespace Prisma { addresses?: boolean | CityCountOutputTypeCountAddressesArgs branches?: boolean | CityCountOutputTypeCountBranchesArgs preOrders?: boolean | CityCountOutputTypeCountPreOrdersArgs + salesExpertProfiles?: boolean | CityCountOutputTypeCountSalesExpertProfilesArgs } // Custom InputTypes @@ -4030,6 +4231,13 @@ export namespace Prisma { where?: PreOrderWhereInput } + /** + * CityCountOutputType without action + */ + export type CityCountOutputTypeCountSalesExpertProfilesArgs = { + where?: SalesExpertProfileWhereInput + } + /** * Count Type AdminUserCountOutputType @@ -4148,6 +4356,7 @@ export namespace Prisma { promotions: number notifications: number activityLogs: number + otpChallenges: number } export type CustomerCountOutputTypeSelect = { @@ -4160,6 +4369,7 @@ export namespace Prisma { promotions?: boolean | CustomerCountOutputTypeCountPromotionsArgs notifications?: boolean | CustomerCountOutputTypeCountNotificationsArgs activityLogs?: boolean | CustomerCountOutputTypeCountActivityLogsArgs + otpChallenges?: boolean | CustomerCountOutputTypeCountOtpChallengesArgs } // Custom InputTypes @@ -4236,6 +4446,13 @@ export namespace Prisma { where?: CustomerActivityLogWhereInput } + /** + * CustomerCountOutputType without action + */ + export type CustomerCountOutputTypeCountOtpChallengesArgs = { + where?: CustomerOtpWhereInput + } + /** * Count Type BranchCountOutputType @@ -4513,6 +4730,37 @@ export namespace Prisma { } + /** + * Count Type ProductImageCountOutputType + */ + + export type ProductImageCountOutputType = { + variants: number + } + + export type ProductImageCountOutputTypeSelect = { + variants?: boolean | ProductImageCountOutputTypeCountVariantsArgs + } + + // Custom InputTypes + /** + * ProductImageCountOutputType without action + */ + export type ProductImageCountOutputTypeDefaultArgs = { + /** + * Select specific fields to fetch from the ProductImageCountOutputType + */ + select?: ProductImageCountOutputTypeSelect | null + } + + /** + * ProductImageCountOutputType without action + */ + export type ProductImageCountOutputTypeCountVariantsArgs = { + where?: ProductVariantWhereInput + } + + /** * Count Type ProductVariantCountOutputType */ @@ -4763,10 +5011,12 @@ export namespace Prisma { export type UploadedFileCountOutputType = { customerAvatars: number + adminUserAvatars: number } export type UploadedFileCountOutputTypeSelect = { customerAvatars?: boolean | UploadedFileCountOutputTypeCountCustomerAvatarsArgs + adminUserAvatars?: boolean | UploadedFileCountOutputTypeCountAdminUserAvatarsArgs } // Custom InputTypes @@ -4787,6 +5037,13 @@ export namespace Prisma { where?: CustomerWhereInput } + /** + * UploadedFileCountOutputType without action + */ + export type UploadedFileCountOutputTypeCountAdminUserAvatarsArgs = { + where?: AdminUserWhereInput + } + /** * Models @@ -4998,6 +5255,7 @@ export namespace Prisma { addresses?: boolean | City$addressesArgs branches?: boolean | City$branchesArgs preOrders?: boolean | City$preOrdersArgs + salesExpertProfiles?: boolean | City$salesExpertProfilesArgs _count?: boolean | CityCountOutputTypeDefaultArgs }, ExtArgs["result"]["city"]> @@ -5022,6 +5280,7 @@ export namespace Prisma { addresses?: boolean | City$addressesArgs branches?: boolean | City$branchesArgs preOrders?: boolean | City$preOrdersArgs + salesExpertProfiles?: boolean | City$salesExpertProfilesArgs _count?: boolean | CityCountOutputTypeDefaultArgs } export type CityIncludeCreateManyAndReturn = {} @@ -5033,6 +5292,7 @@ export namespace Prisma { addresses: Prisma.$CustomerAddressPayload[] branches: Prisma.$BranchPayload[] preOrders: Prisma.$PreOrderPayload[] + salesExpertProfiles: Prisma.$SalesExpertProfilePayload[] } scalars: $Extensions.GetPayloadResult<{ id: string @@ -5412,6 +5672,7 @@ export namespace Prisma { addresses = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> branches = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> preOrders = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> + salesExpertProfiles = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> /** * Attaches callbacks for the resolution and/or rejection of the Promise. * @param onfulfilled The callback to execute when the Promise is resolved. @@ -5839,6 +6100,26 @@ export namespace Prisma { distinct?: PreOrderScalarFieldEnum | PreOrderScalarFieldEnum[] } + /** + * City.salesExpertProfiles + */ + export type City$salesExpertProfilesArgs = { + /** + * Select specific fields to fetch from the SalesExpertProfile + */ + select?: SalesExpertProfileSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: SalesExpertProfileInclude | null + where?: SalesExpertProfileWhereInput + orderBy?: SalesExpertProfileOrderByWithRelationInput | SalesExpertProfileOrderByWithRelationInput[] + cursor?: SalesExpertProfileWhereUniqueInput + take?: number + skip?: number + distinct?: SalesExpertProfileScalarFieldEnum | SalesExpertProfileScalarFieldEnum[] + } + /** * City without action */ @@ -5879,6 +6160,8 @@ export namespace Prisma { export type AdminUserMinAggregateOutputType = { id: string | null fullName: string | null + firstName: string | null + lastName: string | null mobile: string | null passwordHash: string | null role: $Enums.AdminRole | null @@ -5887,11 +6170,18 @@ export namespace Prisma { wagePercent: Decimal | null isActive: boolean | null createdAt: Date | null + avatarFileId: string | null + address: string | null + storeName: string | null + pasazhName: string | null + memberCode: string | null } export type AdminUserMaxAggregateOutputType = { id: string | null fullName: string | null + firstName: string | null + lastName: string | null mobile: string | null passwordHash: string | null role: $Enums.AdminRole | null @@ -5900,11 +6190,18 @@ export namespace Prisma { wagePercent: Decimal | null isActive: boolean | null createdAt: Date | null + avatarFileId: string | null + address: string | null + storeName: string | null + pasazhName: string | null + memberCode: string | null } export type AdminUserCountAggregateOutputType = { id: number fullName: number + firstName: number + lastName: number mobile: number passwordHash: number role: number @@ -5913,6 +6210,11 @@ export namespace Prisma { wagePercent: number isActive: number createdAt: number + avatarFileId: number + address: number + storeName: number + pasazhName: number + memberCode: number _all: number } @@ -5930,6 +6232,8 @@ export namespace Prisma { export type AdminUserMinAggregateInputType = { id?: true fullName?: true + firstName?: true + lastName?: true mobile?: true passwordHash?: true role?: true @@ -5938,11 +6242,18 @@ export namespace Prisma { wagePercent?: true isActive?: true createdAt?: true + avatarFileId?: true + address?: true + storeName?: true + pasazhName?: true + memberCode?: true } export type AdminUserMaxAggregateInputType = { id?: true fullName?: true + firstName?: true + lastName?: true mobile?: true passwordHash?: true role?: true @@ -5951,11 +6262,18 @@ export namespace Prisma { wagePercent?: true isActive?: true createdAt?: true + avatarFileId?: true + address?: true + storeName?: true + pasazhName?: true + memberCode?: true } export type AdminUserCountAggregateInputType = { id?: true fullName?: true + firstName?: true + lastName?: true mobile?: true passwordHash?: true role?: true @@ -5964,6 +6282,11 @@ export namespace Prisma { wagePercent?: true isActive?: true createdAt?: true + avatarFileId?: true + address?: true + storeName?: true + pasazhName?: true + memberCode?: true _all?: true } @@ -6056,6 +6379,8 @@ export namespace Prisma { export type AdminUserGroupByOutputType = { id: string fullName: string + firstName: string | null + lastName: string | null mobile: string passwordHash: string role: $Enums.AdminRole @@ -6064,6 +6389,11 @@ export namespace Prisma { wagePercent: Decimal | null isActive: boolean createdAt: Date + avatarFileId: string | null + address: string | null + storeName: string | null + pasazhName: string | null + memberCode: string | null _count: AdminUserCountAggregateOutputType | null _avg: AdminUserAvgAggregateOutputType | null _sum: AdminUserSumAggregateOutputType | null @@ -6088,6 +6418,8 @@ export namespace Prisma { export type AdminUserSelect = $Extensions.GetSelect<{ id?: boolean fullName?: boolean + firstName?: boolean + lastName?: boolean mobile?: boolean passwordHash?: boolean role?: boolean @@ -6096,8 +6428,14 @@ export namespace Prisma { wagePercent?: boolean isActive?: boolean createdAt?: boolean + avatarFileId?: boolean + address?: boolean + storeName?: boolean + pasazhName?: boolean + memberCode?: boolean userType?: boolean | AdminUser$userTypeArgs branch?: boolean | AdminUser$branchArgs + avatarFile?: boolean | AdminUser$avatarFileArgs referredCustomers?: boolean | AdminUser$referredCustomersArgs supervisedWorkshops?: boolean | AdminUser$supervisedWorkshopsArgs branchAllocations?: boolean | AdminUser$branchAllocationsArgs @@ -6107,12 +6445,15 @@ export namespace Prisma { sentSmsMessages?: boolean | AdminUser$sentSmsMessagesArgs sentNotifications?: boolean | AdminUser$sentNotificationsArgs uploadedFiles?: boolean | AdminUser$uploadedFilesArgs + salesExpertProfile?: boolean | AdminUser$salesExpertProfileArgs _count?: boolean | AdminUserCountOutputTypeDefaultArgs }, ExtArgs["result"]["adminUser"]> export type AdminUserSelectCreateManyAndReturn = $Extensions.GetSelect<{ id?: boolean fullName?: boolean + firstName?: boolean + lastName?: boolean mobile?: boolean passwordHash?: boolean role?: boolean @@ -6121,13 +6462,21 @@ export namespace Prisma { wagePercent?: boolean isActive?: boolean createdAt?: boolean + avatarFileId?: boolean + address?: boolean + storeName?: boolean + pasazhName?: boolean + memberCode?: boolean userType?: boolean | AdminUser$userTypeArgs branch?: boolean | AdminUser$branchArgs + avatarFile?: boolean | AdminUser$avatarFileArgs }, ExtArgs["result"]["adminUser"]> export type AdminUserSelectScalar = { id?: boolean fullName?: boolean + firstName?: boolean + lastName?: boolean mobile?: boolean passwordHash?: boolean role?: boolean @@ -6136,11 +6485,17 @@ export namespace Prisma { wagePercent?: boolean isActive?: boolean createdAt?: boolean + avatarFileId?: boolean + address?: boolean + storeName?: boolean + pasazhName?: boolean + memberCode?: boolean } export type AdminUserInclude = { userType?: boolean | AdminUser$userTypeArgs branch?: boolean | AdminUser$branchArgs + avatarFile?: boolean | AdminUser$avatarFileArgs referredCustomers?: boolean | AdminUser$referredCustomersArgs supervisedWorkshops?: boolean | AdminUser$supervisedWorkshopsArgs branchAllocations?: boolean | AdminUser$branchAllocationsArgs @@ -6150,11 +6505,13 @@ export namespace Prisma { sentSmsMessages?: boolean | AdminUser$sentSmsMessagesArgs sentNotifications?: boolean | AdminUser$sentNotificationsArgs uploadedFiles?: boolean | AdminUser$uploadedFilesArgs + salesExpertProfile?: boolean | AdminUser$salesExpertProfileArgs _count?: boolean | AdminUserCountOutputTypeDefaultArgs } export type AdminUserIncludeCreateManyAndReturn = { userType?: boolean | AdminUser$userTypeArgs branch?: boolean | AdminUser$branchArgs + avatarFile?: boolean | AdminUser$avatarFileArgs } export type $AdminUserPayload = { @@ -6162,6 +6519,7 @@ export namespace Prisma { objects: { userType: Prisma.$UserTypePayload | null branch: Prisma.$BranchPayload | null + avatarFile: Prisma.$UploadedFilePayload | null referredCustomers: Prisma.$CustomerPayload[] supervisedWorkshops: Prisma.$WorkshopPayload[] branchAllocations: Prisma.$BranchAllocationPayload[] @@ -6171,10 +6529,30 @@ export namespace Prisma { sentSmsMessages: Prisma.$SmsMessagePayload[] sentNotifications: Prisma.$NotificationPayload[] uploadedFiles: Prisma.$UploadedFilePayload[] + /** + * Only ever set for role = SALES_EXPERT (D17). + */ + salesExpertProfile: Prisma.$SalesExpertProfilePayload | null } scalars: $Extensions.GetPayloadResult<{ id: string + /** + * ⚠️ Kept in sync BY A DATABASE TRIGGER — `admin_user_sync_full_name`, from + * migration 20260922180000. Prisma cannot see it, so a write here may store + * something other than what you passed: + * + * * write the parts -> fullName is rebuilt as "first last" + * * write fullName only -> the parts are re-derived from it + * * write BOTH in one update -> THE PARTS WIN; the submitted fullName is + * discarded + * + * Re-read after writing if you need the stored value. Same arrangement as + * Customer (D0 requirement 7 — the split applies to ALL admins, not only + * sales experts). + */ fullName: string + firstName: string | null + lastName: string | null mobile: string passwordHash: string role: $Enums.AdminRole @@ -6188,6 +6566,18 @@ export namespace Prisma { wagePercent: Prisma.Decimal | null isActive: boolean createdAt: Date + /** + * Legacy stored a sales agent as a `customers` row with type=1, so + * ReagentCustomer.vue's shared form collected these alongside the + * wholesale-customer fields. A sales agent is now an AdminUser + * (role=SALES_EXPERT), so these live here instead — only ever set for that + * role; every other AdminUser leaves them null. + */ + avatarFileId: string | null + address: string | null + storeName: string | null + pasazhName: string | null + memberCode: string | null }, ExtArgs["result"]["adminUser"]> composites: {} } @@ -6554,6 +6944,7 @@ export namespace Prisma { readonly [Symbol.toStringTag]: "PrismaPromise" userType = {}>(args?: Subset>): Prisma__UserTypeClient<$Result.GetResult, T, "findUniqueOrThrow"> | null, null, ExtArgs> branch = {}>(args?: Subset>): Prisma__BranchClient<$Result.GetResult, T, "findUniqueOrThrow"> | null, null, ExtArgs> + avatarFile = {}>(args?: Subset>): Prisma__UploadedFileClient<$Result.GetResult, T, "findUniqueOrThrow"> | null, null, ExtArgs> referredCustomers = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> supervisedWorkshops = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> branchAllocations = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> @@ -6563,6 +6954,7 @@ export namespace Prisma { sentSmsMessages = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> sentNotifications = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> uploadedFiles = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> + salesExpertProfile = {}>(args?: Subset>): Prisma__SalesExpertProfileClient<$Result.GetResult, T, "findUniqueOrThrow"> | null, null, ExtArgs> /** * Attaches callbacks for the resolution and/or rejection of the Promise. * @param onfulfilled The callback to execute when the Promise is resolved. @@ -6594,6 +6986,8 @@ export namespace Prisma { interface AdminUserFieldRefs { readonly id: FieldRef<"AdminUser", 'String'> readonly fullName: FieldRef<"AdminUser", 'String'> + readonly firstName: FieldRef<"AdminUser", 'String'> + readonly lastName: FieldRef<"AdminUser", 'String'> readonly mobile: FieldRef<"AdminUser", 'String'> readonly passwordHash: FieldRef<"AdminUser", 'String'> readonly role: FieldRef<"AdminUser", 'AdminRole'> @@ -6602,6 +6996,11 @@ export namespace Prisma { readonly wagePercent: FieldRef<"AdminUser", 'Decimal'> readonly isActive: FieldRef<"AdminUser", 'Boolean'> readonly createdAt: FieldRef<"AdminUser", 'DateTime'> + readonly avatarFileId: FieldRef<"AdminUser", 'String'> + readonly address: FieldRef<"AdminUser", 'String'> + readonly storeName: FieldRef<"AdminUser", 'String'> + readonly pasazhName: FieldRef<"AdminUser", 'String'> + readonly memberCode: FieldRef<"AdminUser", 'String'> } @@ -6949,6 +7348,21 @@ export namespace Prisma { where?: BranchWhereInput } + /** + * AdminUser.avatarFile + */ + export type AdminUser$avatarFileArgs = { + /** + * Select specific fields to fetch from the UploadedFile + */ + select?: UploadedFileSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: UploadedFileInclude | null + where?: UploadedFileWhereInput + } + /** * AdminUser.referredCustomers */ @@ -7129,6 +7543,21 @@ export namespace Prisma { distinct?: UploadedFileScalarFieldEnum | UploadedFileScalarFieldEnum[] } + /** + * AdminUser.salesExpertProfile + */ + export type AdminUser$salesExpertProfileArgs = { + /** + * Select specific fields to fetch from the SalesExpertProfile + */ + select?: SalesExpertProfileSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: SalesExpertProfileInclude | null + where?: SalesExpertProfileWhereInput + } + /** * AdminUser without action */ @@ -7144,6 +7573,1094 @@ export namespace Prisma { } + /** + * Model SalesExpertProfile + */ + + export type AggregateSalesExpertProfile = { + _count: SalesExpertProfileCountAggregateOutputType | null + _avg: SalesExpertProfileAvgAggregateOutputType | null + _sum: SalesExpertProfileSumAggregateOutputType | null + _min: SalesExpertProfileMinAggregateOutputType | null + _max: SalesExpertProfileMaxAggregateOutputType | null + } + + export type SalesExpertProfileAvgAggregateOutputType = { + gramLevel: Decimal | null + } + + export type SalesExpertProfileSumAggregateOutputType = { + gramLevel: Decimal | null + } + + export type SalesExpertProfileMinAggregateOutputType = { + id: string | null + adminUserId: string | null + cityId: string | null + gramLevel: Decimal | null + phone: string | null + nationalCode: string | null + tradingCustomerId: string | null + createdAt: Date | null + updatedAt: Date | null + } + + export type SalesExpertProfileMaxAggregateOutputType = { + id: string | null + adminUserId: string | null + cityId: string | null + gramLevel: Decimal | null + phone: string | null + nationalCode: string | null + tradingCustomerId: string | null + createdAt: Date | null + updatedAt: Date | null + } + + export type SalesExpertProfileCountAggregateOutputType = { + id: number + adminUserId: number + cityId: number + gramLevel: number + phone: number + nationalCode: number + tradingCustomerId: number + createdAt: number + updatedAt: number + _all: number + } + + + export type SalesExpertProfileAvgAggregateInputType = { + gramLevel?: true + } + + export type SalesExpertProfileSumAggregateInputType = { + gramLevel?: true + } + + export type SalesExpertProfileMinAggregateInputType = { + id?: true + adminUserId?: true + cityId?: true + gramLevel?: true + phone?: true + nationalCode?: true + tradingCustomerId?: true + createdAt?: true + updatedAt?: true + } + + export type SalesExpertProfileMaxAggregateInputType = { + id?: true + adminUserId?: true + cityId?: true + gramLevel?: true + phone?: true + nationalCode?: true + tradingCustomerId?: true + createdAt?: true + updatedAt?: true + } + + export type SalesExpertProfileCountAggregateInputType = { + id?: true + adminUserId?: true + cityId?: true + gramLevel?: true + phone?: true + nationalCode?: true + tradingCustomerId?: true + createdAt?: true + updatedAt?: true + _all?: true + } + + export type SalesExpertProfileAggregateArgs = { + /** + * Filter which SalesExpertProfile to aggregate. + */ + where?: SalesExpertProfileWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of SalesExpertProfiles to fetch. + */ + orderBy?: SalesExpertProfileOrderByWithRelationInput | SalesExpertProfileOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the start position + */ + cursor?: SalesExpertProfileWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` SalesExpertProfiles from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` SalesExpertProfiles. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Count returned SalesExpertProfiles + **/ + _count?: true | SalesExpertProfileCountAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to average + **/ + _avg?: SalesExpertProfileAvgAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to sum + **/ + _sum?: SalesExpertProfileSumAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the minimum value + **/ + _min?: SalesExpertProfileMinAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the maximum value + **/ + _max?: SalesExpertProfileMaxAggregateInputType + } + + export type GetSalesExpertProfileAggregateType = { + [P in keyof T & keyof AggregateSalesExpertProfile]: P extends '_count' | 'count' + ? T[P] extends true + ? number + : GetScalarType + : GetScalarType + } + + + + + export type SalesExpertProfileGroupByArgs = { + where?: SalesExpertProfileWhereInput + orderBy?: SalesExpertProfileOrderByWithAggregationInput | SalesExpertProfileOrderByWithAggregationInput[] + by: SalesExpertProfileScalarFieldEnum[] | SalesExpertProfileScalarFieldEnum + having?: SalesExpertProfileScalarWhereWithAggregatesInput + take?: number + skip?: number + _count?: SalesExpertProfileCountAggregateInputType | true + _avg?: SalesExpertProfileAvgAggregateInputType + _sum?: SalesExpertProfileSumAggregateInputType + _min?: SalesExpertProfileMinAggregateInputType + _max?: SalesExpertProfileMaxAggregateInputType + } + + export type SalesExpertProfileGroupByOutputType = { + id: string + adminUserId: string + cityId: string | null + gramLevel: Decimal + phone: string | null + nationalCode: string | null + tradingCustomerId: string | null + createdAt: Date + updatedAt: Date + _count: SalesExpertProfileCountAggregateOutputType | null + _avg: SalesExpertProfileAvgAggregateOutputType | null + _sum: SalesExpertProfileSumAggregateOutputType | null + _min: SalesExpertProfileMinAggregateOutputType | null + _max: SalesExpertProfileMaxAggregateOutputType | null + } + + type GetSalesExpertProfileGroupByPayload = Prisma.PrismaPromise< + Array< + PickEnumerable & + { + [P in ((keyof T) & (keyof SalesExpertProfileGroupByOutputType))]: P extends '_count' + ? T[P] extends boolean + ? number + : GetScalarType + : GetScalarType + } + > + > + + + export type SalesExpertProfileSelect = $Extensions.GetSelect<{ + id?: boolean + adminUserId?: boolean + cityId?: boolean + gramLevel?: boolean + phone?: boolean + nationalCode?: boolean + tradingCustomerId?: boolean + createdAt?: boolean + updatedAt?: boolean + adminUser?: boolean | AdminUserDefaultArgs + city?: boolean | SalesExpertProfile$cityArgs + tradingCustomer?: boolean | SalesExpertProfile$tradingCustomerArgs + }, ExtArgs["result"]["salesExpertProfile"]> + + export type SalesExpertProfileSelectCreateManyAndReturn = $Extensions.GetSelect<{ + id?: boolean + adminUserId?: boolean + cityId?: boolean + gramLevel?: boolean + phone?: boolean + nationalCode?: boolean + tradingCustomerId?: boolean + createdAt?: boolean + updatedAt?: boolean + adminUser?: boolean | AdminUserDefaultArgs + city?: boolean | SalesExpertProfile$cityArgs + tradingCustomer?: boolean | SalesExpertProfile$tradingCustomerArgs + }, ExtArgs["result"]["salesExpertProfile"]> + + export type SalesExpertProfileSelectScalar = { + id?: boolean + adminUserId?: boolean + cityId?: boolean + gramLevel?: boolean + phone?: boolean + nationalCode?: boolean + tradingCustomerId?: boolean + createdAt?: boolean + updatedAt?: boolean + } + + export type SalesExpertProfileInclude = { + adminUser?: boolean | AdminUserDefaultArgs + city?: boolean | SalesExpertProfile$cityArgs + tradingCustomer?: boolean | SalesExpertProfile$tradingCustomerArgs + } + export type SalesExpertProfileIncludeCreateManyAndReturn = { + adminUser?: boolean | AdminUserDefaultArgs + city?: boolean | SalesExpertProfile$cityArgs + tradingCustomer?: boolean | SalesExpertProfile$tradingCustomerArgs + } + + export type $SalesExpertProfilePayload = { + name: "SalesExpertProfile" + objects: { + adminUser: Prisma.$AdminUserPayload + city: Prisma.$CityPayload | null + tradingCustomer: Prisma.$CustomerPayload | null + } + scalars: $Extensions.GetPayloadResult<{ + id: string + adminUserId: string + /** + * Legacy customers.city_id — a filterable grid column + * (ReagentListModel.js:14) and the source of the commission autofill + * (ReagentCustomer.vue:215-228). + */ + cityId: string | null + /** + * Legacy customers.gram_level — the «سطح اعتبار» column, filterable. + * Decimal(14,4) matches Customer.goldCreditLimitGrams exactly so the same + * credit limit has identical precision on both tables. + */ + gramLevel: Prisma.Decimal + /** + * Legacy customers.phone (varchar 15) and ncode (varchar 20). The reference + * stores both and reads neither back; kept so the form matches it. Neither + * is unique and neither has a validation rule in the reference — inventing + * one would reject data the reference accepts. + */ + phone: string | null + nationalCode: string | null + /** + * The Customer row carrying this expert's OWN orders, debt and credit limit. + * + * The reference stored a sales agent AS a Customer (type=1), so an order the + * agent placed from the app was simply their own, against their own + * gram_level (ApiMobileController.php:534-535). Here the expert is an + * AdminUser (D17), which has no ledger and cannot be an Order's subject, so + * this one linked Customer plays that part. + * + * Distinct from the expert's REFERRED customers, which are ordinary Customer + * rows pointing at the AdminUser through Customer.referredByAgentId. + * + * Null until the expert first signs into the app, when it is created from + * this profile. See D21. + */ + tradingCustomerId: string | null + createdAt: Date + updatedAt: Date + }, ExtArgs["result"]["salesExpertProfile"]> + composites: {} + } + + type SalesExpertProfileGetPayload = $Result.GetResult + + type SalesExpertProfileCountArgs = + Omit & { + select?: SalesExpertProfileCountAggregateInputType | true + } + + export interface SalesExpertProfileDelegate { + [K: symbol]: { types: Prisma.TypeMap['model']['SalesExpertProfile'], meta: { name: 'SalesExpertProfile' } } + /** + * Find zero or one SalesExpertProfile that matches the filter. + * @param {SalesExpertProfileFindUniqueArgs} args - Arguments to find a SalesExpertProfile + * @example + * // Get one SalesExpertProfile + * const salesExpertProfile = await prisma.salesExpertProfile.findUnique({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUnique(args: SelectSubset>): Prisma__SalesExpertProfileClient<$Result.GetResult, T, "findUnique"> | null, null, ExtArgs> + + /** + * Find one SalesExpertProfile that matches the filter or throw an error with `error.code='P2025'` + * if no matches were found. + * @param {SalesExpertProfileFindUniqueOrThrowArgs} args - Arguments to find a SalesExpertProfile + * @example + * // Get one SalesExpertProfile + * const salesExpertProfile = await prisma.salesExpertProfile.findUniqueOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUniqueOrThrow(args: SelectSubset>): Prisma__SalesExpertProfileClient<$Result.GetResult, T, "findUniqueOrThrow">, never, ExtArgs> + + /** + * Find the first SalesExpertProfile that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {SalesExpertProfileFindFirstArgs} args - Arguments to find a SalesExpertProfile + * @example + * // Get one SalesExpertProfile + * const salesExpertProfile = await prisma.salesExpertProfile.findFirst({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirst(args?: SelectSubset>): Prisma__SalesExpertProfileClient<$Result.GetResult, T, "findFirst"> | null, null, ExtArgs> + + /** + * Find the first SalesExpertProfile that matches the filter or + * throw `PrismaKnownClientError` with `P2025` code if no matches were found. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {SalesExpertProfileFindFirstOrThrowArgs} args - Arguments to find a SalesExpertProfile + * @example + * // Get one SalesExpertProfile + * const salesExpertProfile = await prisma.salesExpertProfile.findFirstOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirstOrThrow(args?: SelectSubset>): Prisma__SalesExpertProfileClient<$Result.GetResult, T, "findFirstOrThrow">, never, ExtArgs> + + /** + * Find zero or more SalesExpertProfiles that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {SalesExpertProfileFindManyArgs} args - Arguments to filter and select certain fields only. + * @example + * // Get all SalesExpertProfiles + * const salesExpertProfiles = await prisma.salesExpertProfile.findMany() + * + * // Get first 10 SalesExpertProfiles + * const salesExpertProfiles = await prisma.salesExpertProfile.findMany({ take: 10 }) + * + * // Only select the `id` + * const salesExpertProfileWithIdOnly = await prisma.salesExpertProfile.findMany({ select: { id: true } }) + * + */ + findMany(args?: SelectSubset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany">> + + /** + * Create a SalesExpertProfile. + * @param {SalesExpertProfileCreateArgs} args - Arguments to create a SalesExpertProfile. + * @example + * // Create one SalesExpertProfile + * const SalesExpertProfile = await prisma.salesExpertProfile.create({ + * data: { + * // ... data to create a SalesExpertProfile + * } + * }) + * + */ + create(args: SelectSubset>): Prisma__SalesExpertProfileClient<$Result.GetResult, T, "create">, never, ExtArgs> + + /** + * Create many SalesExpertProfiles. + * @param {SalesExpertProfileCreateManyArgs} args - Arguments to create many SalesExpertProfiles. + * @example + * // Create many SalesExpertProfiles + * const salesExpertProfile = await prisma.salesExpertProfile.createMany({ + * data: [ + * // ... provide data here + * ] + * }) + * + */ + createMany(args?: SelectSubset>): Prisma.PrismaPromise + + /** + * Create many SalesExpertProfiles and returns the data saved in the database. + * @param {SalesExpertProfileCreateManyAndReturnArgs} args - Arguments to create many SalesExpertProfiles. + * @example + * // Create many SalesExpertProfiles + * const salesExpertProfile = await prisma.salesExpertProfile.createManyAndReturn({ + * data: [ + * // ... provide data here + * ] + * }) + * + * // Create many SalesExpertProfiles and only return the `id` + * const salesExpertProfileWithIdOnly = await prisma.salesExpertProfile.createManyAndReturn({ + * select: { id: true }, + * data: [ + * // ... provide data here + * ] + * }) + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * + */ + createManyAndReturn(args?: SelectSubset>): Prisma.PrismaPromise<$Result.GetResult, T, "createManyAndReturn">> + + /** + * Delete a SalesExpertProfile. + * @param {SalesExpertProfileDeleteArgs} args - Arguments to delete one SalesExpertProfile. + * @example + * // Delete one SalesExpertProfile + * const SalesExpertProfile = await prisma.salesExpertProfile.delete({ + * where: { + * // ... filter to delete one SalesExpertProfile + * } + * }) + * + */ + delete(args: SelectSubset>): Prisma__SalesExpertProfileClient<$Result.GetResult, T, "delete">, never, ExtArgs> + + /** + * Update one SalesExpertProfile. + * @param {SalesExpertProfileUpdateArgs} args - Arguments to update one SalesExpertProfile. + * @example + * // Update one SalesExpertProfile + * const salesExpertProfile = await prisma.salesExpertProfile.update({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + update(args: SelectSubset>): Prisma__SalesExpertProfileClient<$Result.GetResult, T, "update">, never, ExtArgs> + + /** + * Delete zero or more SalesExpertProfiles. + * @param {SalesExpertProfileDeleteManyArgs} args - Arguments to filter SalesExpertProfiles to delete. + * @example + * // Delete a few SalesExpertProfiles + * const { count } = await prisma.salesExpertProfile.deleteMany({ + * where: { + * // ... provide filter here + * } + * }) + * + */ + deleteMany(args?: SelectSubset>): Prisma.PrismaPromise + + /** + * Update zero or more SalesExpertProfiles. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {SalesExpertProfileUpdateManyArgs} args - Arguments to update one or more rows. + * @example + * // Update many SalesExpertProfiles + * const salesExpertProfile = await prisma.salesExpertProfile.updateMany({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + updateMany(args: SelectSubset>): Prisma.PrismaPromise + + /** + * Create or update one SalesExpertProfile. + * @param {SalesExpertProfileUpsertArgs} args - Arguments to update or create a SalesExpertProfile. + * @example + * // Update or create a SalesExpertProfile + * const salesExpertProfile = await prisma.salesExpertProfile.upsert({ + * create: { + * // ... data to create a SalesExpertProfile + * }, + * update: { + * // ... in case it already exists, update + * }, + * where: { + * // ... the filter for the SalesExpertProfile we want to update + * } + * }) + */ + upsert(args: SelectSubset>): Prisma__SalesExpertProfileClient<$Result.GetResult, T, "upsert">, never, ExtArgs> + + + /** + * Count the number of SalesExpertProfiles. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {SalesExpertProfileCountArgs} args - Arguments to filter SalesExpertProfiles to count. + * @example + * // Count the number of SalesExpertProfiles + * const count = await prisma.salesExpertProfile.count({ + * where: { + * // ... the filter for the SalesExpertProfiles we want to count + * } + * }) + **/ + count( + args?: Subset, + ): Prisma.PrismaPromise< + T extends $Utils.Record<'select', any> + ? T['select'] extends true + ? number + : GetScalarType + : number + > + + /** + * Allows you to perform aggregations operations on a SalesExpertProfile. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {SalesExpertProfileAggregateArgs} args - Select which aggregations you would like to apply and on what fields. + * @example + * // Ordered by age ascending + * // Where email contains prisma.io + * // Limited to the 10 users + * const aggregations = await prisma.user.aggregate({ + * _avg: { + * age: true, + * }, + * where: { + * email: { + * contains: "prisma.io", + * }, + * }, + * orderBy: { + * age: "asc", + * }, + * take: 10, + * }) + **/ + aggregate(args: Subset): Prisma.PrismaPromise> + + /** + * Group by SalesExpertProfile. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {SalesExpertProfileGroupByArgs} args - Group by arguments. + * @example + * // Group by city, order by createdAt, get count + * const result = await prisma.user.groupBy({ + * by: ['city', 'createdAt'], + * orderBy: { + * createdAt: true + * }, + * _count: { + * _all: true + * }, + * }) + * + **/ + groupBy< + T extends SalesExpertProfileGroupByArgs, + HasSelectOrTake extends Or< + Extends<'skip', Keys>, + Extends<'take', Keys> + >, + OrderByArg extends True extends HasSelectOrTake + ? { orderBy: SalesExpertProfileGroupByArgs['orderBy'] } + : { orderBy?: SalesExpertProfileGroupByArgs['orderBy'] }, + OrderFields extends ExcludeUnderscoreKeys>>, + ByFields extends MaybeTupleToUnion, + ByValid extends Has, + HavingFields extends GetHavingFields, + HavingValid extends Has, + ByEmpty extends T['by'] extends never[] ? True : False, + InputErrors extends ByEmpty extends True + ? `Error: "by" must not be empty.` + : HavingValid extends False + ? { + [P in HavingFields]: P extends ByFields + ? never + : P extends string + ? `Error: Field "${P}" used in "having" needs to be provided in "by".` + : [ + Error, + 'Field ', + P, + ` in "having" needs to be provided in "by"`, + ] + }[HavingFields] + : 'take' extends Keys + ? 'orderBy' extends Keys + ? ByValid extends True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "take", you also need to provide "orderBy"' + : 'skip' extends Keys + ? 'orderBy' extends Keys + ? ByValid extends True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "skip", you also need to provide "orderBy"' + : ByValid extends True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + >(args: SubsetIntersection & InputErrors): {} extends InputErrors ? GetSalesExpertProfileGroupByPayload : Prisma.PrismaPromise + /** + * Fields of the SalesExpertProfile model + */ + readonly fields: SalesExpertProfileFieldRefs; + } + + /** + * The delegate class that acts as a "Promise-like" for SalesExpertProfile. + * Why is this prefixed with `Prisma__`? + * Because we want to prevent naming conflicts as mentioned in + * https://github.com/prisma/prisma-client-js/issues/707 + */ + export interface Prisma__SalesExpertProfileClient extends Prisma.PrismaPromise { + readonly [Symbol.toStringTag]: "PrismaPromise" + adminUser = {}>(args?: Subset>): Prisma__AdminUserClient<$Result.GetResult, T, "findUniqueOrThrow"> | Null, Null, ExtArgs> + city = {}>(args?: Subset>): Prisma__CityClient<$Result.GetResult, T, "findUniqueOrThrow"> | null, null, ExtArgs> + tradingCustomer = {}>(args?: Subset>): Prisma__CustomerClient<$Result.GetResult, T, "findUniqueOrThrow"> | null, null, ExtArgs> + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): $Utils.JsPromise + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): $Utils.JsPromise + /** + * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The + * resolved value cannot be modified from the callback. + * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). + * @returns A Promise for the completion of the callback. + */ + finally(onfinally?: (() => void) | undefined | null): $Utils.JsPromise + } + + + + + /** + * Fields of the SalesExpertProfile model + */ + interface SalesExpertProfileFieldRefs { + readonly id: FieldRef<"SalesExpertProfile", 'String'> + readonly adminUserId: FieldRef<"SalesExpertProfile", 'String'> + readonly cityId: FieldRef<"SalesExpertProfile", 'String'> + readonly gramLevel: FieldRef<"SalesExpertProfile", 'Decimal'> + readonly phone: FieldRef<"SalesExpertProfile", 'String'> + readonly nationalCode: FieldRef<"SalesExpertProfile", 'String'> + readonly tradingCustomerId: FieldRef<"SalesExpertProfile", 'String'> + readonly createdAt: FieldRef<"SalesExpertProfile", 'DateTime'> + readonly updatedAt: FieldRef<"SalesExpertProfile", 'DateTime'> + } + + + // Custom InputTypes + /** + * SalesExpertProfile findUnique + */ + export type SalesExpertProfileFindUniqueArgs = { + /** + * Select specific fields to fetch from the SalesExpertProfile + */ + select?: SalesExpertProfileSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: SalesExpertProfileInclude | null + /** + * Filter, which SalesExpertProfile to fetch. + */ + where: SalesExpertProfileWhereUniqueInput + } + + /** + * SalesExpertProfile findUniqueOrThrow + */ + export type SalesExpertProfileFindUniqueOrThrowArgs = { + /** + * Select specific fields to fetch from the SalesExpertProfile + */ + select?: SalesExpertProfileSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: SalesExpertProfileInclude | null + /** + * Filter, which SalesExpertProfile to fetch. + */ + where: SalesExpertProfileWhereUniqueInput + } + + /** + * SalesExpertProfile findFirst + */ + export type SalesExpertProfileFindFirstArgs = { + /** + * Select specific fields to fetch from the SalesExpertProfile + */ + select?: SalesExpertProfileSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: SalesExpertProfileInclude | null + /** + * Filter, which SalesExpertProfile to fetch. + */ + where?: SalesExpertProfileWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of SalesExpertProfiles to fetch. + */ + orderBy?: SalesExpertProfileOrderByWithRelationInput | SalesExpertProfileOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for SalesExpertProfiles. + */ + cursor?: SalesExpertProfileWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` SalesExpertProfiles from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` SalesExpertProfiles. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of SalesExpertProfiles. + */ + distinct?: SalesExpertProfileScalarFieldEnum | SalesExpertProfileScalarFieldEnum[] + } + + /** + * SalesExpertProfile findFirstOrThrow + */ + export type SalesExpertProfileFindFirstOrThrowArgs = { + /** + * Select specific fields to fetch from the SalesExpertProfile + */ + select?: SalesExpertProfileSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: SalesExpertProfileInclude | null + /** + * Filter, which SalesExpertProfile to fetch. + */ + where?: SalesExpertProfileWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of SalesExpertProfiles to fetch. + */ + orderBy?: SalesExpertProfileOrderByWithRelationInput | SalesExpertProfileOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for SalesExpertProfiles. + */ + cursor?: SalesExpertProfileWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` SalesExpertProfiles from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` SalesExpertProfiles. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of SalesExpertProfiles. + */ + distinct?: SalesExpertProfileScalarFieldEnum | SalesExpertProfileScalarFieldEnum[] + } + + /** + * SalesExpertProfile findMany + */ + export type SalesExpertProfileFindManyArgs = { + /** + * Select specific fields to fetch from the SalesExpertProfile + */ + select?: SalesExpertProfileSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: SalesExpertProfileInclude | null + /** + * Filter, which SalesExpertProfiles to fetch. + */ + where?: SalesExpertProfileWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of SalesExpertProfiles to fetch. + */ + orderBy?: SalesExpertProfileOrderByWithRelationInput | SalesExpertProfileOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for listing SalesExpertProfiles. + */ + cursor?: SalesExpertProfileWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` SalesExpertProfiles from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` SalesExpertProfiles. + */ + skip?: number + distinct?: SalesExpertProfileScalarFieldEnum | SalesExpertProfileScalarFieldEnum[] + } + + /** + * SalesExpertProfile create + */ + export type SalesExpertProfileCreateArgs = { + /** + * Select specific fields to fetch from the SalesExpertProfile + */ + select?: SalesExpertProfileSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: SalesExpertProfileInclude | null + /** + * The data needed to create a SalesExpertProfile. + */ + data: XOR + } + + /** + * SalesExpertProfile createMany + */ + export type SalesExpertProfileCreateManyArgs = { + /** + * The data used to create many SalesExpertProfiles. + */ + data: SalesExpertProfileCreateManyInput | SalesExpertProfileCreateManyInput[] + skipDuplicates?: boolean + } + + /** + * SalesExpertProfile createManyAndReturn + */ + export type SalesExpertProfileCreateManyAndReturnArgs = { + /** + * Select specific fields to fetch from the SalesExpertProfile + */ + select?: SalesExpertProfileSelectCreateManyAndReturn | null + /** + * The data used to create many SalesExpertProfiles. + */ + data: SalesExpertProfileCreateManyInput | SalesExpertProfileCreateManyInput[] + skipDuplicates?: boolean + /** + * Choose, which related nodes to fetch as well + */ + include?: SalesExpertProfileIncludeCreateManyAndReturn | null + } + + /** + * SalesExpertProfile update + */ + export type SalesExpertProfileUpdateArgs = { + /** + * Select specific fields to fetch from the SalesExpertProfile + */ + select?: SalesExpertProfileSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: SalesExpertProfileInclude | null + /** + * The data needed to update a SalesExpertProfile. + */ + data: XOR + /** + * Choose, which SalesExpertProfile to update. + */ + where: SalesExpertProfileWhereUniqueInput + } + + /** + * SalesExpertProfile updateMany + */ + export type SalesExpertProfileUpdateManyArgs = { + /** + * The data used to update SalesExpertProfiles. + */ + data: XOR + /** + * Filter which SalesExpertProfiles to update + */ + where?: SalesExpertProfileWhereInput + } + + /** + * SalesExpertProfile upsert + */ + export type SalesExpertProfileUpsertArgs = { + /** + * Select specific fields to fetch from the SalesExpertProfile + */ + select?: SalesExpertProfileSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: SalesExpertProfileInclude | null + /** + * The filter to search for the SalesExpertProfile to update in case it exists. + */ + where: SalesExpertProfileWhereUniqueInput + /** + * In case the SalesExpertProfile found by the `where` argument doesn't exist, create a new SalesExpertProfile with this data. + */ + create: XOR + /** + * In case the SalesExpertProfile was found with the provided `where` argument, update it with this data. + */ + update: XOR + } + + /** + * SalesExpertProfile delete + */ + export type SalesExpertProfileDeleteArgs = { + /** + * Select specific fields to fetch from the SalesExpertProfile + */ + select?: SalesExpertProfileSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: SalesExpertProfileInclude | null + /** + * Filter which SalesExpertProfile to delete. + */ + where: SalesExpertProfileWhereUniqueInput + } + + /** + * SalesExpertProfile deleteMany + */ + export type SalesExpertProfileDeleteManyArgs = { + /** + * Filter which SalesExpertProfiles to delete + */ + where?: SalesExpertProfileWhereInput + } + + /** + * SalesExpertProfile.city + */ + export type SalesExpertProfile$cityArgs = { + /** + * Select specific fields to fetch from the City + */ + select?: CitySelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: CityInclude | null + where?: CityWhereInput + } + + /** + * SalesExpertProfile.tradingCustomer + */ + export type SalesExpertProfile$tradingCustomerArgs = { + /** + * Select specific fields to fetch from the Customer + */ + select?: CustomerSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: CustomerInclude | null + where?: CustomerWhereInput + } + + /** + * SalesExpertProfile without action + */ + export type SalesExpertProfileDefaultArgs = { + /** + * Select specific fields to fetch from the SalesExpertProfile + */ + select?: SalesExpertProfileSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: SalesExpertProfileInclude | null + } + + /** * Model Customer */ @@ -7160,17 +8677,21 @@ export namespace Prisma { goldCreditLimitGrams: Decimal | null goldDebtGrams: Decimal | null fiatDebtRial: Decimal | null + makingChargeOffsetPercent: Decimal | null } export type CustomerSumAggregateOutputType = { goldCreditLimitGrams: Decimal | null goldDebtGrams: Decimal | null fiatDebtRial: Decimal | null + makingChargeOffsetPercent: Decimal | null } export type CustomerMinAggregateOutputType = { id: string | null fullName: string | null + firstName: string | null + lastName: string | null mobile: string | null nationalCode: string | null passwordHash: string | null @@ -7186,6 +8707,7 @@ export namespace Prisma { goldCreditLimitGrams: Decimal | null goldDebtGrams: Decimal | null fiatDebtRial: Decimal | null + makingChargeOffsetPercent: Decimal | null verificationStep: $Enums.VerificationStep | null nationalIdImageUrl: string | null businessLicenseUrl: string | null @@ -7201,6 +8723,8 @@ export namespace Prisma { export type CustomerMaxAggregateOutputType = { id: string | null fullName: string | null + firstName: string | null + lastName: string | null mobile: string | null nationalCode: string | null passwordHash: string | null @@ -7216,6 +8740,7 @@ export namespace Prisma { goldCreditLimitGrams: Decimal | null goldDebtGrams: Decimal | null fiatDebtRial: Decimal | null + makingChargeOffsetPercent: Decimal | null verificationStep: $Enums.VerificationStep | null nationalIdImageUrl: string | null businessLicenseUrl: string | null @@ -7231,6 +8756,8 @@ export namespace Prisma { export type CustomerCountAggregateOutputType = { id: number fullName: number + firstName: number + lastName: number mobile: number nationalCode: number passwordHash: number @@ -7246,6 +8773,7 @@ export namespace Prisma { goldCreditLimitGrams: number goldDebtGrams: number fiatDebtRial: number + makingChargeOffsetPercent: number verificationStep: number nationalIdImageUrl: number businessLicenseUrl: number @@ -7264,17 +8792,21 @@ export namespace Prisma { goldCreditLimitGrams?: true goldDebtGrams?: true fiatDebtRial?: true + makingChargeOffsetPercent?: true } export type CustomerSumAggregateInputType = { goldCreditLimitGrams?: true goldDebtGrams?: true fiatDebtRial?: true + makingChargeOffsetPercent?: true } export type CustomerMinAggregateInputType = { id?: true fullName?: true + firstName?: true + lastName?: true mobile?: true nationalCode?: true passwordHash?: true @@ -7290,6 +8822,7 @@ export namespace Prisma { goldCreditLimitGrams?: true goldDebtGrams?: true fiatDebtRial?: true + makingChargeOffsetPercent?: true verificationStep?: true nationalIdImageUrl?: true businessLicenseUrl?: true @@ -7305,6 +8838,8 @@ export namespace Prisma { export type CustomerMaxAggregateInputType = { id?: true fullName?: true + firstName?: true + lastName?: true mobile?: true nationalCode?: true passwordHash?: true @@ -7320,6 +8855,7 @@ export namespace Prisma { goldCreditLimitGrams?: true goldDebtGrams?: true fiatDebtRial?: true + makingChargeOffsetPercent?: true verificationStep?: true nationalIdImageUrl?: true businessLicenseUrl?: true @@ -7335,6 +8871,8 @@ export namespace Prisma { export type CustomerCountAggregateInputType = { id?: true fullName?: true + firstName?: true + lastName?: true mobile?: true nationalCode?: true passwordHash?: true @@ -7350,6 +8888,7 @@ export namespace Prisma { goldCreditLimitGrams?: true goldDebtGrams?: true fiatDebtRial?: true + makingChargeOffsetPercent?: true verificationStep?: true nationalIdImageUrl?: true businessLicenseUrl?: true @@ -7452,6 +8991,8 @@ export namespace Prisma { export type CustomerGroupByOutputType = { id: string fullName: string + firstName: string | null + lastName: string | null mobile: string nationalCode: string passwordHash: string @@ -7467,6 +9008,7 @@ export namespace Prisma { goldCreditLimitGrams: Decimal goldDebtGrams: Decimal fiatDebtRial: Decimal + makingChargeOffsetPercent: Decimal verificationStep: $Enums.VerificationStep nationalIdImageUrl: string | null businessLicenseUrl: string | null @@ -7501,6 +9043,8 @@ export namespace Prisma { export type CustomerSelect = $Extensions.GetSelect<{ id?: boolean fullName?: boolean + firstName?: boolean + lastName?: boolean mobile?: boolean nationalCode?: boolean passwordHash?: boolean @@ -7516,6 +9060,7 @@ export namespace Prisma { goldCreditLimitGrams?: boolean goldDebtGrams?: boolean fiatDebtRial?: boolean + makingChargeOffsetPercent?: boolean verificationStep?: boolean nationalIdImageUrl?: boolean businessLicenseUrl?: boolean @@ -7528,6 +9073,7 @@ export namespace Prisma { updatedAt?: boolean city?: boolean | Customer$cityArgs referredByAgent?: boolean | Customer$referredByAgentArgs + salesExpertTradingFor?: boolean | Customer$salesExpertTradingForArgs avatarFile?: boolean | Customer$avatarFileArgs addresses?: boolean | Customer$addressesArgs orders?: boolean | Customer$ordersArgs @@ -7538,12 +9084,15 @@ export namespace Prisma { promotions?: boolean | Customer$promotionsArgs notifications?: boolean | Customer$notificationsArgs activityLogs?: boolean | Customer$activityLogsArgs + otpChallenges?: boolean | Customer$otpChallengesArgs _count?: boolean | CustomerCountOutputTypeDefaultArgs }, ExtArgs["result"]["customer"]> export type CustomerSelectCreateManyAndReturn = $Extensions.GetSelect<{ id?: boolean fullName?: boolean + firstName?: boolean + lastName?: boolean mobile?: boolean nationalCode?: boolean passwordHash?: boolean @@ -7559,6 +9108,7 @@ export namespace Prisma { goldCreditLimitGrams?: boolean goldDebtGrams?: boolean fiatDebtRial?: boolean + makingChargeOffsetPercent?: boolean verificationStep?: boolean nationalIdImageUrl?: boolean businessLicenseUrl?: boolean @@ -7577,6 +9127,8 @@ export namespace Prisma { export type CustomerSelectScalar = { id?: boolean fullName?: boolean + firstName?: boolean + lastName?: boolean mobile?: boolean nationalCode?: boolean passwordHash?: boolean @@ -7592,6 +9144,7 @@ export namespace Prisma { goldCreditLimitGrams?: boolean goldDebtGrams?: boolean fiatDebtRial?: boolean + makingChargeOffsetPercent?: boolean verificationStep?: boolean nationalIdImageUrl?: boolean businessLicenseUrl?: boolean @@ -7607,6 +9160,7 @@ export namespace Prisma { export type CustomerInclude = { city?: boolean | Customer$cityArgs referredByAgent?: boolean | Customer$referredByAgentArgs + salesExpertTradingFor?: boolean | Customer$salesExpertTradingForArgs avatarFile?: boolean | Customer$avatarFileArgs addresses?: boolean | Customer$addressesArgs orders?: boolean | Customer$ordersArgs @@ -7617,6 +9171,7 @@ export namespace Prisma { promotions?: boolean | Customer$promotionsArgs notifications?: boolean | Customer$notificationsArgs activityLogs?: boolean | Customer$activityLogsArgs + otpChallenges?: boolean | Customer$otpChallengesArgs _count?: boolean | CustomerCountOutputTypeDefaultArgs } export type CustomerIncludeCreateManyAndReturn = { @@ -7630,6 +9185,12 @@ export namespace Prisma { objects: { city: Prisma.$CityPayload | null referredByAgent: Prisma.$AdminUserPayload | null + /** + * Set only on the one Customer row that acts as a sales expert's own trading + * account (see SalesExpertProfile.tradingCustomerId). Empty for every + * ordinary customer. + */ + salesExpertTradingFor: Prisma.$SalesExpertProfilePayload | null avatarFile: Prisma.$UploadedFilePayload | null addresses: Prisma.$CustomerAddressPayload[] orders: Prisma.$OrderPayload[] @@ -7640,10 +9201,32 @@ export namespace Prisma { promotions: Prisma.$PromotionPayload[] notifications: Prisma.$NotificationRecipientPayload[] activityLogs: Prisma.$CustomerActivityLogPayload[] + otpChallenges: Prisma.$CustomerOtpPayload[] } scalars: $Extensions.GetPayloadResult<{ id: string + /** + * ⚠️ `fullName`, `firstName` and `lastName` are kept in sync BY A DATABASE + * TRIGGER — `customer_sync_full_name`, created in migration + * 20260922120000_customer_name_parts. Prisma does not know about it, so a + * write here is NOT the whole story: + * + * * write the parts -> fullName is rebuilt as "first last" + * * write fullName only -> the parts are re-derived from it + * * write BOTH in one update -> THE PARTS WIN; fullName is overwritten + * with "first last" and whatever you passed for fullName is discarded + * + * So a value you wrote can differ from the value that lands in the row. + * Re-read after writing if you need the stored value. Prefer writing the + * parts; `fullName` exists for the legacy readers that predate D0. + * + * Reference has these as two columns from the start + * (henza_server 2020_10_01_120430_customers_table.php:18-19 fname/lname); + * the port collapsed them, which is the D0 defect. + */ fullName: string + firstName: string | null + lastName: string | null mobile: string nationalCode: string passwordHash: string @@ -7670,6 +9253,23 @@ export namespace Prisma { goldCreditLimitGrams: Prisma.Decimal goldDebtGrams: Prisma.Decimal fiatDebtRial: Prisma.Decimal + /** + * Legacy `customers.percent` — the per-customer making-charge offset. It is + * ADDED to the variant's making charge when pricing an order line + * (henza_pwa src/Mixins/product/index.ts:69-75: + * getWage(v) = state.percent + v.percentage_pay) + * and it shifts the catalogue making-charge filter bounds + * (ApiMobileController.php:391-396,76-77). It is NOT part of the price + * formula itself — that extra percent is ConfigStatic['product_price_extra_percent']. + * + * NOT the wallet cashback rate (storefront ConfigStatic['percent'], D13) and + * NOT the wholesale per-line charge (OrderItem.wagePercentSnapshot). + * + * DB default 0 keeps every existing customer's price unchanged. The + * reference's application-layer default for NEW customers is 3 + * (AdminController.php:338) and the field is required on create (:354). + */ + makingChargeOffsetPercent: Prisma.Decimal verificationStep: $Enums.VerificationStep nationalIdImageUrl: string | null businessLicenseUrl: string | null @@ -8060,6 +9660,7 @@ export namespace Prisma { readonly [Symbol.toStringTag]: "PrismaPromise" city = {}>(args?: Subset>): Prisma__CityClient<$Result.GetResult, T, "findUniqueOrThrow"> | null, null, ExtArgs> referredByAgent = {}>(args?: Subset>): Prisma__AdminUserClient<$Result.GetResult, T, "findUniqueOrThrow"> | null, null, ExtArgs> + salesExpertTradingFor = {}>(args?: Subset>): Prisma__SalesExpertProfileClient<$Result.GetResult, T, "findUniqueOrThrow"> | null, null, ExtArgs> avatarFile = {}>(args?: Subset>): Prisma__UploadedFileClient<$Result.GetResult, T, "findUniqueOrThrow"> | null, null, ExtArgs> addresses = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> orders = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> @@ -8070,6 +9671,7 @@ export namespace Prisma { promotions = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> notifications = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> activityLogs = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> + otpChallenges = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> /** * Attaches callbacks for the resolution and/or rejection of the Promise. * @param onfulfilled The callback to execute when the Promise is resolved. @@ -8101,6 +9703,8 @@ export namespace Prisma { interface CustomerFieldRefs { readonly id: FieldRef<"Customer", 'String'> readonly fullName: FieldRef<"Customer", 'String'> + readonly firstName: FieldRef<"Customer", 'String'> + readonly lastName: FieldRef<"Customer", 'String'> readonly mobile: FieldRef<"Customer", 'String'> readonly nationalCode: FieldRef<"Customer", 'String'> readonly passwordHash: FieldRef<"Customer", 'String'> @@ -8116,6 +9720,7 @@ export namespace Prisma { readonly goldCreditLimitGrams: FieldRef<"Customer", 'Decimal'> readonly goldDebtGrams: FieldRef<"Customer", 'Decimal'> readonly fiatDebtRial: FieldRef<"Customer", 'Decimal'> + readonly makingChargeOffsetPercent: FieldRef<"Customer", 'Decimal'> readonly verificationStep: FieldRef<"Customer", 'VerificationStep'> readonly nationalIdImageUrl: FieldRef<"Customer", 'String'> readonly businessLicenseUrl: FieldRef<"Customer", 'String'> @@ -8473,6 +10078,21 @@ export namespace Prisma { where?: AdminUserWhereInput } + /** + * Customer.salesExpertTradingFor + */ + export type Customer$salesExpertTradingForArgs = { + /** + * Select specific fields to fetch from the SalesExpertProfile + */ + select?: SalesExpertProfileSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: SalesExpertProfileInclude | null + where?: SalesExpertProfileWhereInput + } + /** * Customer.avatarFile */ @@ -8668,6 +10288,26 @@ export namespace Prisma { distinct?: CustomerActivityLogScalarFieldEnum | CustomerActivityLogScalarFieldEnum[] } + /** + * Customer.otpChallenges + */ + export type Customer$otpChallengesArgs = { + /** + * Select specific fields to fetch from the CustomerOtp + */ + select?: CustomerOtpSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: CustomerOtpInclude | null + where?: CustomerOtpWhereInput + orderBy?: CustomerOtpOrderByWithRelationInput | CustomerOtpOrderByWithRelationInput[] + cursor?: CustomerOtpWhereUniqueInput + take?: number + skip?: number + distinct?: CustomerOtpScalarFieldEnum | CustomerOtpScalarFieldEnum[] + } + /** * Customer without action */ @@ -16948,6 +18588,8 @@ export namespace Prisma { sortOrder?: boolean role?: boolean product?: boolean | ProductDefaultArgs + variants?: boolean | ProductImage$variantsArgs + _count?: boolean | ProductImageCountOutputTypeDefaultArgs }, ExtArgs["result"]["productImage"]> export type ProductImageSelectCreateManyAndReturn = $Extensions.GetSelect<{ @@ -16969,6 +18611,8 @@ export namespace Prisma { export type ProductImageInclude = { product?: boolean | ProductDefaultArgs + variants?: boolean | ProductImage$variantsArgs + _count?: boolean | ProductImageCountOutputTypeDefaultArgs } export type ProductImageIncludeCreateManyAndReturn = { product?: boolean | ProductDefaultArgs @@ -16978,6 +18622,12 @@ export namespace Prisma { name: "ProductImage" objects: { product: Prisma.$ProductPayload + /** + * Variants that display this image. A gallery image may back several + * variants (one photo, several weights), which is why this is a plain + * one-to-many and not a join table. + */ + variants: Prisma.$ProductVariantPayload[] } scalars: $Extensions.GetPayloadResult<{ id: string @@ -17355,6 +19005,7 @@ export namespace Prisma { export interface Prisma__ProductImageClient extends Prisma.PrismaPromise { readonly [Symbol.toStringTag]: "PrismaPromise" product = {}>(args?: Subset>): Prisma__ProductClient<$Result.GetResult, T, "findUniqueOrThrow"> | Null, Null, ExtArgs> + variants = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> /** * Attaches callbacks for the resolution and/or rejection of the Promise. * @param onfulfilled The callback to execute when the Promise is resolved. @@ -17706,6 +19357,26 @@ export namespace Prisma { where?: ProductImageWhereInput } + /** + * ProductImage.variants + */ + export type ProductImage$variantsArgs = { + /** + * Select specific fields to fetch from the ProductVariant + */ + select?: ProductVariantSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: ProductVariantInclude | null + where?: ProductVariantWhereInput + orderBy?: ProductVariantOrderByWithRelationInput | ProductVariantOrderByWithRelationInput[] + cursor?: ProductVariantWhereUniqueInput + take?: number + skip?: number + distinct?: ProductVariantScalarFieldEnum | ProductVariantScalarFieldEnum[] + } + /** * ProductImage without action */ @@ -17755,6 +19426,7 @@ export namespace Prisma { qty: number | null isPreOrder: boolean | null isActive: boolean | null + imageId: string | null } export type ProductVariantMaxAggregateOutputType = { @@ -17767,6 +19439,7 @@ export namespace Prisma { qty: number | null isPreOrder: boolean | null isActive: boolean | null + imageId: string | null } export type ProductVariantCountAggregateOutputType = { @@ -17779,6 +19452,7 @@ export namespace Prisma { qty: number isPreOrder: number isActive: number + imageId: number _all: number } @@ -17805,6 +19479,7 @@ export namespace Prisma { qty?: true isPreOrder?: true isActive?: true + imageId?: true } export type ProductVariantMaxAggregateInputType = { @@ -17817,6 +19492,7 @@ export namespace Prisma { qty?: true isPreOrder?: true isActive?: true + imageId?: true } export type ProductVariantCountAggregateInputType = { @@ -17829,6 +19505,7 @@ export namespace Prisma { qty?: true isPreOrder?: true isActive?: true + imageId?: true _all?: true } @@ -17928,6 +19605,7 @@ export namespace Prisma { qty: number isPreOrder: boolean isActive: boolean + imageId: string | null _count: ProductVariantCountAggregateOutputType | null _avg: ProductVariantAvgAggregateOutputType | null _sum: ProductVariantSumAggregateOutputType | null @@ -17959,7 +19637,9 @@ export namespace Prisma { qty?: boolean isPreOrder?: boolean isActive?: boolean + imageId?: boolean product?: boolean | ProductDefaultArgs + image?: boolean | ProductVariant$imageArgs orderItems?: boolean | ProductVariant$orderItemsArgs _count?: boolean | ProductVariantCountOutputTypeDefaultArgs }, ExtArgs["result"]["productVariant"]> @@ -17974,7 +19654,9 @@ export namespace Prisma { qty?: boolean isPreOrder?: boolean isActive?: boolean + imageId?: boolean product?: boolean | ProductDefaultArgs + image?: boolean | ProductVariant$imageArgs }, ExtArgs["result"]["productVariant"]> export type ProductVariantSelectScalar = { @@ -17987,21 +19669,25 @@ export namespace Prisma { qty?: boolean isPreOrder?: boolean isActive?: boolean + imageId?: boolean } export type ProductVariantInclude = { product?: boolean | ProductDefaultArgs + image?: boolean | ProductVariant$imageArgs orderItems?: boolean | ProductVariant$orderItemsArgs _count?: boolean | ProductVariantCountOutputTypeDefaultArgs } export type ProductVariantIncludeCreateManyAndReturn = { product?: boolean | ProductDefaultArgs + image?: boolean | ProductVariant$imageArgs } export type $ProductVariantPayload = { name: "ProductVariant" objects: { product: Prisma.$ProductPayload + image: Prisma.$ProductImagePayload | null orderItems: Prisma.$OrderItemPayload[] } scalars: $Extensions.GetPayloadResult<{ @@ -18028,6 +19714,13 @@ export namespace Prisma { */ isPreOrder: boolean isActive: boolean + /** + * The gallery image shown for this variant, picked from its own product's + * images — never a re-uploaded copy. onDelete: SetNull because dropping an + * image must never cascade into the variant: OrderItem rows reference + * variants, so losing one would break order history. + */ + imageId: string | null }, ExtArgs["result"]["productVariant"]> composites: {} } @@ -18393,6 +20086,7 @@ export namespace Prisma { export interface Prisma__ProductVariantClient extends Prisma.PrismaPromise { readonly [Symbol.toStringTag]: "PrismaPromise" product = {}>(args?: Subset>): Prisma__ProductClient<$Result.GetResult, T, "findUniqueOrThrow"> | Null, Null, ExtArgs> + image = {}>(args?: Subset>): Prisma__ProductImageClient<$Result.GetResult, T, "findUniqueOrThrow"> | null, null, ExtArgs> orderItems = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> /** * Attaches callbacks for the resolution and/or rejection of the Promise. @@ -18432,6 +20126,7 @@ export namespace Prisma { readonly qty: FieldRef<"ProductVariant", 'Int'> readonly isPreOrder: FieldRef<"ProductVariant", 'Boolean'> readonly isActive: FieldRef<"ProductVariant", 'Boolean'> + readonly imageId: FieldRef<"ProductVariant", 'String'> } @@ -18749,6 +20444,21 @@ export namespace Prisma { where?: ProductVariantWhereInput } + /** + * ProductVariant.image + */ + export type ProductVariant$imageArgs = { + /** + * Select specific fields to fetch from the ProductImage + */ + select?: ProductImageSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: ProductImageInclude | null + where?: ProductImageWhereInput + } + /** * ProductVariant.orderItems */ @@ -40131,6 +41841,7 @@ export namespace Prisma { createdAt?: boolean uploadedByAdminUser?: boolean | UploadedFile$uploadedByAdminUserArgs customerAvatars?: boolean | UploadedFile$customerAvatarsArgs + adminUserAvatars?: boolean | UploadedFile$adminUserAvatarsArgs _count?: boolean | UploadedFileCountOutputTypeDefaultArgs }, ExtArgs["result"]["uploadedFile"]> @@ -40160,6 +41871,7 @@ export namespace Prisma { export type UploadedFileInclude = { uploadedByAdminUser?: boolean | UploadedFile$uploadedByAdminUserArgs customerAvatars?: boolean | UploadedFile$customerAvatarsArgs + adminUserAvatars?: boolean | UploadedFile$adminUserAvatarsArgs _count?: boolean | UploadedFileCountOutputTypeDefaultArgs } export type UploadedFileIncludeCreateManyAndReturn = { @@ -40171,6 +41883,7 @@ export namespace Prisma { objects: { uploadedByAdminUser: Prisma.$AdminUserPayload | null customerAvatars: Prisma.$CustomerPayload[] + adminUserAvatars: Prisma.$AdminUserPayload[] } scalars: $Extensions.GetPayloadResult<{ id: string @@ -40554,6 +42267,7 @@ export namespace Prisma { readonly [Symbol.toStringTag]: "PrismaPromise" uploadedByAdminUser = {}>(args?: Subset>): Prisma__AdminUserClient<$Result.GetResult, T, "findUniqueOrThrow"> | null, null, ExtArgs> customerAvatars = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> + adminUserAvatars = {}>(args?: Subset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany"> | Null> /** * Attaches callbacks for the resolution and/or rejection of the Promise. * @param onfulfilled The callback to execute when the Promise is resolved. @@ -40943,6 +42657,26 @@ export namespace Prisma { distinct?: CustomerScalarFieldEnum | CustomerScalarFieldEnum[] } + /** + * UploadedFile.adminUserAvatars + */ + export type UploadedFile$adminUserAvatarsArgs = { + /** + * Select specific fields to fetch from the AdminUser + */ + select?: AdminUserSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: AdminUserInclude | null + where?: AdminUserWhereInput + orderBy?: AdminUserOrderByWithRelationInput | AdminUserOrderByWithRelationInput[] + cursor?: AdminUserWhereUniqueInput + take?: number + skip?: number + distinct?: AdminUserScalarFieldEnum | AdminUserScalarFieldEnum[] + } + /** * UploadedFile without action */ @@ -40958,6 +42692,1037 @@ export namespace Prisma { } + /** + * Model CustomerOtp + */ + + export type AggregateCustomerOtp = { + _count: CustomerOtpCountAggregateOutputType | null + _avg: CustomerOtpAvgAggregateOutputType | null + _sum: CustomerOtpSumAggregateOutputType | null + _min: CustomerOtpMinAggregateOutputType | null + _max: CustomerOtpMaxAggregateOutputType | null + } + + export type CustomerOtpAvgAggregateOutputType = { + attempts: number | null + } + + export type CustomerOtpSumAggregateOutputType = { + attempts: number | null + } + + export type CustomerOtpMinAggregateOutputType = { + id: string | null + mobile: string | null + customerId: string | null + codeHash: string | null + expiresAt: Date | null + attempts: number | null + consumedAt: Date | null + requestIp: string | null + createdAt: Date | null + } + + export type CustomerOtpMaxAggregateOutputType = { + id: string | null + mobile: string | null + customerId: string | null + codeHash: string | null + expiresAt: Date | null + attempts: number | null + consumedAt: Date | null + requestIp: string | null + createdAt: Date | null + } + + export type CustomerOtpCountAggregateOutputType = { + id: number + mobile: number + customerId: number + codeHash: number + expiresAt: number + attempts: number + consumedAt: number + requestIp: number + createdAt: number + _all: number + } + + + export type CustomerOtpAvgAggregateInputType = { + attempts?: true + } + + export type CustomerOtpSumAggregateInputType = { + attempts?: true + } + + export type CustomerOtpMinAggregateInputType = { + id?: true + mobile?: true + customerId?: true + codeHash?: true + expiresAt?: true + attempts?: true + consumedAt?: true + requestIp?: true + createdAt?: true + } + + export type CustomerOtpMaxAggregateInputType = { + id?: true + mobile?: true + customerId?: true + codeHash?: true + expiresAt?: true + attempts?: true + consumedAt?: true + requestIp?: true + createdAt?: true + } + + export type CustomerOtpCountAggregateInputType = { + id?: true + mobile?: true + customerId?: true + codeHash?: true + expiresAt?: true + attempts?: true + consumedAt?: true + requestIp?: true + createdAt?: true + _all?: true + } + + export type CustomerOtpAggregateArgs = { + /** + * Filter which CustomerOtp to aggregate. + */ + where?: CustomerOtpWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CustomerOtps to fetch. + */ + orderBy?: CustomerOtpOrderByWithRelationInput | CustomerOtpOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the start position + */ + cursor?: CustomerOtpWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CustomerOtps from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CustomerOtps. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Count returned CustomerOtps + **/ + _count?: true | CustomerOtpCountAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to average + **/ + _avg?: CustomerOtpAvgAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to sum + **/ + _sum?: CustomerOtpSumAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the minimum value + **/ + _min?: CustomerOtpMinAggregateInputType + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs} + * + * Select which fields to find the maximum value + **/ + _max?: CustomerOtpMaxAggregateInputType + } + + export type GetCustomerOtpAggregateType = { + [P in keyof T & keyof AggregateCustomerOtp]: P extends '_count' | 'count' + ? T[P] extends true + ? number + : GetScalarType + : GetScalarType + } + + + + + export type CustomerOtpGroupByArgs = { + where?: CustomerOtpWhereInput + orderBy?: CustomerOtpOrderByWithAggregationInput | CustomerOtpOrderByWithAggregationInput[] + by: CustomerOtpScalarFieldEnum[] | CustomerOtpScalarFieldEnum + having?: CustomerOtpScalarWhereWithAggregatesInput + take?: number + skip?: number + _count?: CustomerOtpCountAggregateInputType | true + _avg?: CustomerOtpAvgAggregateInputType + _sum?: CustomerOtpSumAggregateInputType + _min?: CustomerOtpMinAggregateInputType + _max?: CustomerOtpMaxAggregateInputType + } + + export type CustomerOtpGroupByOutputType = { + id: string + mobile: string + customerId: string + codeHash: string + expiresAt: Date + attempts: number + consumedAt: Date | null + requestIp: string | null + createdAt: Date + _count: CustomerOtpCountAggregateOutputType | null + _avg: CustomerOtpAvgAggregateOutputType | null + _sum: CustomerOtpSumAggregateOutputType | null + _min: CustomerOtpMinAggregateOutputType | null + _max: CustomerOtpMaxAggregateOutputType | null + } + + type GetCustomerOtpGroupByPayload = Prisma.PrismaPromise< + Array< + PickEnumerable & + { + [P in ((keyof T) & (keyof CustomerOtpGroupByOutputType))]: P extends '_count' + ? T[P] extends boolean + ? number + : GetScalarType + : GetScalarType + } + > + > + + + export type CustomerOtpSelect = $Extensions.GetSelect<{ + id?: boolean + mobile?: boolean + customerId?: boolean + codeHash?: boolean + expiresAt?: boolean + attempts?: boolean + consumedAt?: boolean + requestIp?: boolean + createdAt?: boolean + customer?: boolean | CustomerDefaultArgs + }, ExtArgs["result"]["customerOtp"]> + + export type CustomerOtpSelectCreateManyAndReturn = $Extensions.GetSelect<{ + id?: boolean + mobile?: boolean + customerId?: boolean + codeHash?: boolean + expiresAt?: boolean + attempts?: boolean + consumedAt?: boolean + requestIp?: boolean + createdAt?: boolean + customer?: boolean | CustomerDefaultArgs + }, ExtArgs["result"]["customerOtp"]> + + export type CustomerOtpSelectScalar = { + id?: boolean + mobile?: boolean + customerId?: boolean + codeHash?: boolean + expiresAt?: boolean + attempts?: boolean + consumedAt?: boolean + requestIp?: boolean + createdAt?: boolean + } + + export type CustomerOtpInclude = { + customer?: boolean | CustomerDefaultArgs + } + export type CustomerOtpIncludeCreateManyAndReturn = { + customer?: boolean | CustomerDefaultArgs + } + + export type $CustomerOtpPayload = { + name: "CustomerOtp" + objects: { + customer: Prisma.$CustomerPayload + } + scalars: $Extensions.GetPayloadResult<{ + id: string + /** + * Normalised to English digits before storing, as the reference does with + * Utility::faTOen() on both send and verify. + */ + mobile: string + customerId: string + /** + * bcrypt hash of the 4-digit code. Never the code itself. + */ + codeHash: string + expiresAt: Date + /** + * Failed verify attempts against THIS challenge. + */ + attempts: number + /** + * Set when the code is accepted, so one challenge cannot be reused. + */ + consumedAt: Date | null + /** + * Kept for the per-IP send throttle. + */ + requestIp: string | null + createdAt: Date + }, ExtArgs["result"]["customerOtp"]> + composites: {} + } + + type CustomerOtpGetPayload = $Result.GetResult + + type CustomerOtpCountArgs = + Omit & { + select?: CustomerOtpCountAggregateInputType | true + } + + export interface CustomerOtpDelegate { + [K: symbol]: { types: Prisma.TypeMap['model']['CustomerOtp'], meta: { name: 'CustomerOtp' } } + /** + * Find zero or one CustomerOtp that matches the filter. + * @param {CustomerOtpFindUniqueArgs} args - Arguments to find a CustomerOtp + * @example + * // Get one CustomerOtp + * const customerOtp = await prisma.customerOtp.findUnique({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUnique(args: SelectSubset>): Prisma__CustomerOtpClient<$Result.GetResult, T, "findUnique"> | null, null, ExtArgs> + + /** + * Find one CustomerOtp that matches the filter or throw an error with `error.code='P2025'` + * if no matches were found. + * @param {CustomerOtpFindUniqueOrThrowArgs} args - Arguments to find a CustomerOtp + * @example + * // Get one CustomerOtp + * const customerOtp = await prisma.customerOtp.findUniqueOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findUniqueOrThrow(args: SelectSubset>): Prisma__CustomerOtpClient<$Result.GetResult, T, "findUniqueOrThrow">, never, ExtArgs> + + /** + * Find the first CustomerOtp that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CustomerOtpFindFirstArgs} args - Arguments to find a CustomerOtp + * @example + * // Get one CustomerOtp + * const customerOtp = await prisma.customerOtp.findFirst({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirst(args?: SelectSubset>): Prisma__CustomerOtpClient<$Result.GetResult, T, "findFirst"> | null, null, ExtArgs> + + /** + * Find the first CustomerOtp that matches the filter or + * throw `PrismaKnownClientError` with `P2025` code if no matches were found. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CustomerOtpFindFirstOrThrowArgs} args - Arguments to find a CustomerOtp + * @example + * // Get one CustomerOtp + * const customerOtp = await prisma.customerOtp.findFirstOrThrow({ + * where: { + * // ... provide filter here + * } + * }) + */ + findFirstOrThrow(args?: SelectSubset>): Prisma__CustomerOtpClient<$Result.GetResult, T, "findFirstOrThrow">, never, ExtArgs> + + /** + * Find zero or more CustomerOtps that matches the filter. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CustomerOtpFindManyArgs} args - Arguments to filter and select certain fields only. + * @example + * // Get all CustomerOtps + * const customerOtps = await prisma.customerOtp.findMany() + * + * // Get first 10 CustomerOtps + * const customerOtps = await prisma.customerOtp.findMany({ take: 10 }) + * + * // Only select the `id` + * const customerOtpWithIdOnly = await prisma.customerOtp.findMany({ select: { id: true } }) + * + */ + findMany(args?: SelectSubset>): Prisma.PrismaPromise<$Result.GetResult, T, "findMany">> + + /** + * Create a CustomerOtp. + * @param {CustomerOtpCreateArgs} args - Arguments to create a CustomerOtp. + * @example + * // Create one CustomerOtp + * const CustomerOtp = await prisma.customerOtp.create({ + * data: { + * // ... data to create a CustomerOtp + * } + * }) + * + */ + create(args: SelectSubset>): Prisma__CustomerOtpClient<$Result.GetResult, T, "create">, never, ExtArgs> + + /** + * Create many CustomerOtps. + * @param {CustomerOtpCreateManyArgs} args - Arguments to create many CustomerOtps. + * @example + * // Create many CustomerOtps + * const customerOtp = await prisma.customerOtp.createMany({ + * data: [ + * // ... provide data here + * ] + * }) + * + */ + createMany(args?: SelectSubset>): Prisma.PrismaPromise + + /** + * Create many CustomerOtps and returns the data saved in the database. + * @param {CustomerOtpCreateManyAndReturnArgs} args - Arguments to create many CustomerOtps. + * @example + * // Create many CustomerOtps + * const customerOtp = await prisma.customerOtp.createManyAndReturn({ + * data: [ + * // ... provide data here + * ] + * }) + * + * // Create many CustomerOtps and only return the `id` + * const customerOtpWithIdOnly = await prisma.customerOtp.createManyAndReturn({ + * select: { id: true }, + * data: [ + * // ... provide data here + * ] + * }) + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * + */ + createManyAndReturn(args?: SelectSubset>): Prisma.PrismaPromise<$Result.GetResult, T, "createManyAndReturn">> + + /** + * Delete a CustomerOtp. + * @param {CustomerOtpDeleteArgs} args - Arguments to delete one CustomerOtp. + * @example + * // Delete one CustomerOtp + * const CustomerOtp = await prisma.customerOtp.delete({ + * where: { + * // ... filter to delete one CustomerOtp + * } + * }) + * + */ + delete(args: SelectSubset>): Prisma__CustomerOtpClient<$Result.GetResult, T, "delete">, never, ExtArgs> + + /** + * Update one CustomerOtp. + * @param {CustomerOtpUpdateArgs} args - Arguments to update one CustomerOtp. + * @example + * // Update one CustomerOtp + * const customerOtp = await prisma.customerOtp.update({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + update(args: SelectSubset>): Prisma__CustomerOtpClient<$Result.GetResult, T, "update">, never, ExtArgs> + + /** + * Delete zero or more CustomerOtps. + * @param {CustomerOtpDeleteManyArgs} args - Arguments to filter CustomerOtps to delete. + * @example + * // Delete a few CustomerOtps + * const { count } = await prisma.customerOtp.deleteMany({ + * where: { + * // ... provide filter here + * } + * }) + * + */ + deleteMany(args?: SelectSubset>): Prisma.PrismaPromise + + /** + * Update zero or more CustomerOtps. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CustomerOtpUpdateManyArgs} args - Arguments to update one or more rows. + * @example + * // Update many CustomerOtps + * const customerOtp = await prisma.customerOtp.updateMany({ + * where: { + * // ... provide filter here + * }, + * data: { + * // ... provide data here + * } + * }) + * + */ + updateMany(args: SelectSubset>): Prisma.PrismaPromise + + /** + * Create or update one CustomerOtp. + * @param {CustomerOtpUpsertArgs} args - Arguments to update or create a CustomerOtp. + * @example + * // Update or create a CustomerOtp + * const customerOtp = await prisma.customerOtp.upsert({ + * create: { + * // ... data to create a CustomerOtp + * }, + * update: { + * // ... in case it already exists, update + * }, + * where: { + * // ... the filter for the CustomerOtp we want to update + * } + * }) + */ + upsert(args: SelectSubset>): Prisma__CustomerOtpClient<$Result.GetResult, T, "upsert">, never, ExtArgs> + + + /** + * Count the number of CustomerOtps. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CustomerOtpCountArgs} args - Arguments to filter CustomerOtps to count. + * @example + * // Count the number of CustomerOtps + * const count = await prisma.customerOtp.count({ + * where: { + * // ... the filter for the CustomerOtps we want to count + * } + * }) + **/ + count( + args?: Subset, + ): Prisma.PrismaPromise< + T extends $Utils.Record<'select', any> + ? T['select'] extends true + ? number + : GetScalarType + : number + > + + /** + * Allows you to perform aggregations operations on a CustomerOtp. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CustomerOtpAggregateArgs} args - Select which aggregations you would like to apply and on what fields. + * @example + * // Ordered by age ascending + * // Where email contains prisma.io + * // Limited to the 10 users + * const aggregations = await prisma.user.aggregate({ + * _avg: { + * age: true, + * }, + * where: { + * email: { + * contains: "prisma.io", + * }, + * }, + * orderBy: { + * age: "asc", + * }, + * take: 10, + * }) + **/ + aggregate(args: Subset): Prisma.PrismaPromise> + + /** + * Group by CustomerOtp. + * Note, that providing `undefined` is treated as the value not being there. + * Read more here: https://pris.ly/d/null-undefined + * @param {CustomerOtpGroupByArgs} args - Group by arguments. + * @example + * // Group by city, order by createdAt, get count + * const result = await prisma.user.groupBy({ + * by: ['city', 'createdAt'], + * orderBy: { + * createdAt: true + * }, + * _count: { + * _all: true + * }, + * }) + * + **/ + groupBy< + T extends CustomerOtpGroupByArgs, + HasSelectOrTake extends Or< + Extends<'skip', Keys>, + Extends<'take', Keys> + >, + OrderByArg extends True extends HasSelectOrTake + ? { orderBy: CustomerOtpGroupByArgs['orderBy'] } + : { orderBy?: CustomerOtpGroupByArgs['orderBy'] }, + OrderFields extends ExcludeUnderscoreKeys>>, + ByFields extends MaybeTupleToUnion, + ByValid extends Has, + HavingFields extends GetHavingFields, + HavingValid extends Has, + ByEmpty extends T['by'] extends never[] ? True : False, + InputErrors extends ByEmpty extends True + ? `Error: "by" must not be empty.` + : HavingValid extends False + ? { + [P in HavingFields]: P extends ByFields + ? never + : P extends string + ? `Error: Field "${P}" used in "having" needs to be provided in "by".` + : [ + Error, + 'Field ', + P, + ` in "having" needs to be provided in "by"`, + ] + }[HavingFields] + : 'take' extends Keys + ? 'orderBy' extends Keys + ? ByValid extends True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "take", you also need to provide "orderBy"' + : 'skip' extends Keys + ? 'orderBy' extends Keys + ? ByValid extends True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + : 'Error: If you provide "skip", you also need to provide "orderBy"' + : ByValid extends True + ? {} + : { + [P in OrderFields]: P extends ByFields + ? never + : `Error: Field "${P}" in "orderBy" needs to be provided in "by"` + }[OrderFields] + >(args: SubsetIntersection & InputErrors): {} extends InputErrors ? GetCustomerOtpGroupByPayload : Prisma.PrismaPromise + /** + * Fields of the CustomerOtp model + */ + readonly fields: CustomerOtpFieldRefs; + } + + /** + * The delegate class that acts as a "Promise-like" for CustomerOtp. + * Why is this prefixed with `Prisma__`? + * Because we want to prevent naming conflicts as mentioned in + * https://github.com/prisma/prisma-client-js/issues/707 + */ + export interface Prisma__CustomerOtpClient extends Prisma.PrismaPromise { + readonly [Symbol.toStringTag]: "PrismaPromise" + customer = {}>(args?: Subset>): Prisma__CustomerClient<$Result.GetResult, T, "findUniqueOrThrow"> | Null, Null, ExtArgs> + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): $Utils.JsPromise + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): $Utils.JsPromise + /** + * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The + * resolved value cannot be modified from the callback. + * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). + * @returns A Promise for the completion of the callback. + */ + finally(onfinally?: (() => void) | undefined | null): $Utils.JsPromise + } + + + + + /** + * Fields of the CustomerOtp model + */ + interface CustomerOtpFieldRefs { + readonly id: FieldRef<"CustomerOtp", 'String'> + readonly mobile: FieldRef<"CustomerOtp", 'String'> + readonly customerId: FieldRef<"CustomerOtp", 'String'> + readonly codeHash: FieldRef<"CustomerOtp", 'String'> + readonly expiresAt: FieldRef<"CustomerOtp", 'DateTime'> + readonly attempts: FieldRef<"CustomerOtp", 'Int'> + readonly consumedAt: FieldRef<"CustomerOtp", 'DateTime'> + readonly requestIp: FieldRef<"CustomerOtp", 'String'> + readonly createdAt: FieldRef<"CustomerOtp", 'DateTime'> + } + + + // Custom InputTypes + /** + * CustomerOtp findUnique + */ + export type CustomerOtpFindUniqueArgs = { + /** + * Select specific fields to fetch from the CustomerOtp + */ + select?: CustomerOtpSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: CustomerOtpInclude | null + /** + * Filter, which CustomerOtp to fetch. + */ + where: CustomerOtpWhereUniqueInput + } + + /** + * CustomerOtp findUniqueOrThrow + */ + export type CustomerOtpFindUniqueOrThrowArgs = { + /** + * Select specific fields to fetch from the CustomerOtp + */ + select?: CustomerOtpSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: CustomerOtpInclude | null + /** + * Filter, which CustomerOtp to fetch. + */ + where: CustomerOtpWhereUniqueInput + } + + /** + * CustomerOtp findFirst + */ + export type CustomerOtpFindFirstArgs = { + /** + * Select specific fields to fetch from the CustomerOtp + */ + select?: CustomerOtpSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: CustomerOtpInclude | null + /** + * Filter, which CustomerOtp to fetch. + */ + where?: CustomerOtpWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CustomerOtps to fetch. + */ + orderBy?: CustomerOtpOrderByWithRelationInput | CustomerOtpOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for CustomerOtps. + */ + cursor?: CustomerOtpWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CustomerOtps from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CustomerOtps. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of CustomerOtps. + */ + distinct?: CustomerOtpScalarFieldEnum | CustomerOtpScalarFieldEnum[] + } + + /** + * CustomerOtp findFirstOrThrow + */ + export type CustomerOtpFindFirstOrThrowArgs = { + /** + * Select specific fields to fetch from the CustomerOtp + */ + select?: CustomerOtpSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: CustomerOtpInclude | null + /** + * Filter, which CustomerOtp to fetch. + */ + where?: CustomerOtpWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CustomerOtps to fetch. + */ + orderBy?: CustomerOtpOrderByWithRelationInput | CustomerOtpOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for searching for CustomerOtps. + */ + cursor?: CustomerOtpWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CustomerOtps from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CustomerOtps. + */ + skip?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/distinct Distinct Docs} + * + * Filter by unique combinations of CustomerOtps. + */ + distinct?: CustomerOtpScalarFieldEnum | CustomerOtpScalarFieldEnum[] + } + + /** + * CustomerOtp findMany + */ + export type CustomerOtpFindManyArgs = { + /** + * Select specific fields to fetch from the CustomerOtp + */ + select?: CustomerOtpSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: CustomerOtpInclude | null + /** + * Filter, which CustomerOtps to fetch. + */ + where?: CustomerOtpWhereInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/sorting Sorting Docs} + * + * Determine the order of CustomerOtps to fetch. + */ + orderBy?: CustomerOtpOrderByWithRelationInput | CustomerOtpOrderByWithRelationInput[] + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination#cursor-based-pagination Cursor Docs} + * + * Sets the position for listing CustomerOtps. + */ + cursor?: CustomerOtpWhereUniqueInput + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Take `±n` CustomerOtps from the position of the cursor. + */ + take?: number + /** + * {@link https://www.prisma.io/docs/concepts/components/prisma-client/pagination Pagination Docs} + * + * Skip the first `n` CustomerOtps. + */ + skip?: number + distinct?: CustomerOtpScalarFieldEnum | CustomerOtpScalarFieldEnum[] + } + + /** + * CustomerOtp create + */ + export type CustomerOtpCreateArgs = { + /** + * Select specific fields to fetch from the CustomerOtp + */ + select?: CustomerOtpSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: CustomerOtpInclude | null + /** + * The data needed to create a CustomerOtp. + */ + data: XOR + } + + /** + * CustomerOtp createMany + */ + export type CustomerOtpCreateManyArgs = { + /** + * The data used to create many CustomerOtps. + */ + data: CustomerOtpCreateManyInput | CustomerOtpCreateManyInput[] + skipDuplicates?: boolean + } + + /** + * CustomerOtp createManyAndReturn + */ + export type CustomerOtpCreateManyAndReturnArgs = { + /** + * Select specific fields to fetch from the CustomerOtp + */ + select?: CustomerOtpSelectCreateManyAndReturn | null + /** + * The data used to create many CustomerOtps. + */ + data: CustomerOtpCreateManyInput | CustomerOtpCreateManyInput[] + skipDuplicates?: boolean + /** + * Choose, which related nodes to fetch as well + */ + include?: CustomerOtpIncludeCreateManyAndReturn | null + } + + /** + * CustomerOtp update + */ + export type CustomerOtpUpdateArgs = { + /** + * Select specific fields to fetch from the CustomerOtp + */ + select?: CustomerOtpSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: CustomerOtpInclude | null + /** + * The data needed to update a CustomerOtp. + */ + data: XOR + /** + * Choose, which CustomerOtp to update. + */ + where: CustomerOtpWhereUniqueInput + } + + /** + * CustomerOtp updateMany + */ + export type CustomerOtpUpdateManyArgs = { + /** + * The data used to update CustomerOtps. + */ + data: XOR + /** + * Filter which CustomerOtps to update + */ + where?: CustomerOtpWhereInput + } + + /** + * CustomerOtp upsert + */ + export type CustomerOtpUpsertArgs = { + /** + * Select specific fields to fetch from the CustomerOtp + */ + select?: CustomerOtpSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: CustomerOtpInclude | null + /** + * The filter to search for the CustomerOtp to update in case it exists. + */ + where: CustomerOtpWhereUniqueInput + /** + * In case the CustomerOtp found by the `where` argument doesn't exist, create a new CustomerOtp with this data. + */ + create: XOR + /** + * In case the CustomerOtp was found with the provided `where` argument, update it with this data. + */ + update: XOR + } + + /** + * CustomerOtp delete + */ + export type CustomerOtpDeleteArgs = { + /** + * Select specific fields to fetch from the CustomerOtp + */ + select?: CustomerOtpSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: CustomerOtpInclude | null + /** + * Filter which CustomerOtp to delete. + */ + where: CustomerOtpWhereUniqueInput + } + + /** + * CustomerOtp deleteMany + */ + export type CustomerOtpDeleteManyArgs = { + /** + * Filter which CustomerOtps to delete + */ + where?: CustomerOtpWhereInput + } + + /** + * CustomerOtp without action + */ + export type CustomerOtpDefaultArgs = { + /** + * Select specific fields to fetch from the CustomerOtp + */ + select?: CustomerOtpSelect | null + /** + * Choose, which related nodes to fetch as well + */ + include?: CustomerOtpInclude | null + } + + /** * Enums */ @@ -40986,6 +43751,8 @@ export namespace Prisma { export const AdminUserScalarFieldEnum: { id: 'id', fullName: 'fullName', + firstName: 'firstName', + lastName: 'lastName', mobile: 'mobile', passwordHash: 'passwordHash', role: 'role', @@ -40993,15 +43760,37 @@ export namespace Prisma { branchId: 'branchId', wagePercent: 'wagePercent', isActive: 'isActive', - createdAt: 'createdAt' + createdAt: 'createdAt', + avatarFileId: 'avatarFileId', + address: 'address', + storeName: 'storeName', + pasazhName: 'pasazhName', + memberCode: 'memberCode' }; export type AdminUserScalarFieldEnum = (typeof AdminUserScalarFieldEnum)[keyof typeof AdminUserScalarFieldEnum] + export const SalesExpertProfileScalarFieldEnum: { + id: 'id', + adminUserId: 'adminUserId', + cityId: 'cityId', + gramLevel: 'gramLevel', + phone: 'phone', + nationalCode: 'nationalCode', + tradingCustomerId: 'tradingCustomerId', + createdAt: 'createdAt', + updatedAt: 'updatedAt' + }; + + export type SalesExpertProfileScalarFieldEnum = (typeof SalesExpertProfileScalarFieldEnum)[keyof typeof SalesExpertProfileScalarFieldEnum] + + export const CustomerScalarFieldEnum: { id: 'id', fullName: 'fullName', + firstName: 'firstName', + lastName: 'lastName', mobile: 'mobile', nationalCode: 'nationalCode', passwordHash: 'passwordHash', @@ -41017,6 +43806,7 @@ export namespace Prisma { goldCreditLimitGrams: 'goldCreditLimitGrams', goldDebtGrams: 'goldDebtGrams', fiatDebtRial: 'fiatDebtRial', + makingChargeOffsetPercent: 'makingChargeOffsetPercent', verificationStep: 'verificationStep', nationalIdImageUrl: 'nationalIdImageUrl', businessLicenseUrl: 'businessLicenseUrl', @@ -41142,7 +43932,8 @@ export namespace Prisma { defaultWagePercent: 'defaultWagePercent', qty: 'qty', isPreOrder: 'isPreOrder', - isActive: 'isActive' + isActive: 'isActive', + imageId: 'imageId' }; export type ProductVariantScalarFieldEnum = (typeof ProductVariantScalarFieldEnum)[keyof typeof ProductVariantScalarFieldEnum] @@ -41446,6 +44237,21 @@ export namespace Prisma { export type UploadedFileScalarFieldEnum = (typeof UploadedFileScalarFieldEnum)[keyof typeof UploadedFileScalarFieldEnum] + export const CustomerOtpScalarFieldEnum: { + id: 'id', + mobile: 'mobile', + customerId: 'customerId', + codeHash: 'codeHash', + expiresAt: 'expiresAt', + attempts: 'attempts', + consumedAt: 'consumedAt', + requestIp: 'requestIp', + createdAt: 'createdAt' + }; + + export type CustomerOtpScalarFieldEnum = (typeof CustomerOtpScalarFieldEnum)[keyof typeof CustomerOtpScalarFieldEnum] + + export const SortOrder: { asc: 'asc', desc: 'desc' @@ -41787,6 +44593,7 @@ export namespace Prisma { addresses?: CustomerAddressListRelationFilter branches?: BranchListRelationFilter preOrders?: PreOrderListRelationFilter + salesExpertProfiles?: SalesExpertProfileListRelationFilter } export type CityOrderByWithRelationInput = { @@ -41799,6 +44606,7 @@ export namespace Prisma { addresses?: CustomerAddressOrderByRelationAggregateInput branches?: BranchOrderByRelationAggregateInput preOrders?: PreOrderOrderByRelationAggregateInput + salesExpertProfiles?: SalesExpertProfileOrderByRelationAggregateInput } export type CityWhereUniqueInput = Prisma.AtLeast<{ @@ -41814,6 +44622,7 @@ export namespace Prisma { addresses?: CustomerAddressListRelationFilter branches?: BranchListRelationFilter preOrders?: PreOrderListRelationFilter + salesExpertProfiles?: SalesExpertProfileListRelationFilter }, "id"> export type CityOrderByWithAggregationInput = { @@ -41846,6 +44655,8 @@ export namespace Prisma { NOT?: AdminUserWhereInput | AdminUserWhereInput[] id?: StringFilter<"AdminUser"> | string fullName?: StringFilter<"AdminUser"> | string + firstName?: StringNullableFilter<"AdminUser"> | string | null + lastName?: StringNullableFilter<"AdminUser"> | string | null mobile?: StringFilter<"AdminUser"> | string passwordHash?: StringFilter<"AdminUser"> | string role?: EnumAdminRoleFilter<"AdminUser"> | $Enums.AdminRole @@ -41854,8 +44665,14 @@ export namespace Prisma { wagePercent?: DecimalNullableFilter<"AdminUser"> | Decimal | DecimalJsLike | number | string | null isActive?: BoolFilter<"AdminUser"> | boolean createdAt?: DateTimeFilter<"AdminUser"> | Date | string + avatarFileId?: StringNullableFilter<"AdminUser"> | string | null + address?: StringNullableFilter<"AdminUser"> | string | null + storeName?: StringNullableFilter<"AdminUser"> | string | null + pasazhName?: StringNullableFilter<"AdminUser"> | string | null + memberCode?: StringNullableFilter<"AdminUser"> | string | null userType?: XOR | null branch?: XOR | null + avatarFile?: XOR | null referredCustomers?: CustomerListRelationFilter supervisedWorkshops?: WorkshopListRelationFilter branchAllocations?: BranchAllocationListRelationFilter @@ -41865,11 +44682,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageListRelationFilter sentNotifications?: NotificationListRelationFilter uploadedFiles?: UploadedFileListRelationFilter + salesExpertProfile?: XOR | null } export type AdminUserOrderByWithRelationInput = { id?: SortOrder fullName?: SortOrder + firstName?: SortOrderInput | SortOrder + lastName?: SortOrderInput | SortOrder mobile?: SortOrder passwordHash?: SortOrder role?: SortOrder @@ -41878,8 +44698,14 @@ export namespace Prisma { wagePercent?: SortOrderInput | SortOrder isActive?: SortOrder createdAt?: SortOrder + avatarFileId?: SortOrderInput | SortOrder + address?: SortOrderInput | SortOrder + storeName?: SortOrderInput | SortOrder + pasazhName?: SortOrderInput | SortOrder + memberCode?: SortOrderInput | SortOrder userType?: UserTypeOrderByWithRelationInput branch?: BranchOrderByWithRelationInput + avatarFile?: UploadedFileOrderByWithRelationInput referredCustomers?: CustomerOrderByRelationAggregateInput supervisedWorkshops?: WorkshopOrderByRelationAggregateInput branchAllocations?: BranchAllocationOrderByRelationAggregateInput @@ -41889,6 +44715,7 @@ export namespace Prisma { sentSmsMessages?: SmsMessageOrderByRelationAggregateInput sentNotifications?: NotificationOrderByRelationAggregateInput uploadedFiles?: UploadedFileOrderByRelationAggregateInput + salesExpertProfile?: SalesExpertProfileOrderByWithRelationInput } export type AdminUserWhereUniqueInput = Prisma.AtLeast<{ @@ -41898,6 +44725,8 @@ export namespace Prisma { OR?: AdminUserWhereInput[] NOT?: AdminUserWhereInput | AdminUserWhereInput[] fullName?: StringFilter<"AdminUser"> | string + firstName?: StringNullableFilter<"AdminUser"> | string | null + lastName?: StringNullableFilter<"AdminUser"> | string | null passwordHash?: StringFilter<"AdminUser"> | string role?: EnumAdminRoleFilter<"AdminUser"> | $Enums.AdminRole userTypeId?: IntNullableFilter<"AdminUser"> | number | null @@ -41905,8 +44734,14 @@ export namespace Prisma { wagePercent?: DecimalNullableFilter<"AdminUser"> | Decimal | DecimalJsLike | number | string | null isActive?: BoolFilter<"AdminUser"> | boolean createdAt?: DateTimeFilter<"AdminUser"> | Date | string + avatarFileId?: StringNullableFilter<"AdminUser"> | string | null + address?: StringNullableFilter<"AdminUser"> | string | null + storeName?: StringNullableFilter<"AdminUser"> | string | null + pasazhName?: StringNullableFilter<"AdminUser"> | string | null + memberCode?: StringNullableFilter<"AdminUser"> | string | null userType?: XOR | null branch?: XOR | null + avatarFile?: XOR | null referredCustomers?: CustomerListRelationFilter supervisedWorkshops?: WorkshopListRelationFilter branchAllocations?: BranchAllocationListRelationFilter @@ -41916,11 +44751,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageListRelationFilter sentNotifications?: NotificationListRelationFilter uploadedFiles?: UploadedFileListRelationFilter + salesExpertProfile?: XOR | null }, "id" | "mobile"> export type AdminUserOrderByWithAggregationInput = { id?: SortOrder fullName?: SortOrder + firstName?: SortOrderInput | SortOrder + lastName?: SortOrderInput | SortOrder mobile?: SortOrder passwordHash?: SortOrder role?: SortOrder @@ -41929,6 +44767,11 @@ export namespace Prisma { wagePercent?: SortOrderInput | SortOrder isActive?: SortOrder createdAt?: SortOrder + avatarFileId?: SortOrderInput | SortOrder + address?: SortOrderInput | SortOrder + storeName?: SortOrderInput | SortOrder + pasazhName?: SortOrderInput | SortOrder + memberCode?: SortOrderInput | SortOrder _count?: AdminUserCountOrderByAggregateInput _avg?: AdminUserAvgOrderByAggregateInput _max?: AdminUserMaxOrderByAggregateInput @@ -41942,6 +44785,8 @@ export namespace Prisma { NOT?: AdminUserScalarWhereWithAggregatesInput | AdminUserScalarWhereWithAggregatesInput[] id?: StringWithAggregatesFilter<"AdminUser"> | string fullName?: StringWithAggregatesFilter<"AdminUser"> | string + firstName?: StringNullableWithAggregatesFilter<"AdminUser"> | string | null + lastName?: StringNullableWithAggregatesFilter<"AdminUser"> | string | null mobile?: StringWithAggregatesFilter<"AdminUser"> | string passwordHash?: StringWithAggregatesFilter<"AdminUser"> | string role?: EnumAdminRoleWithAggregatesFilter<"AdminUser"> | $Enums.AdminRole @@ -41950,6 +44795,94 @@ export namespace Prisma { wagePercent?: DecimalNullableWithAggregatesFilter<"AdminUser"> | Decimal | DecimalJsLike | number | string | null isActive?: BoolWithAggregatesFilter<"AdminUser"> | boolean createdAt?: DateTimeWithAggregatesFilter<"AdminUser"> | Date | string + avatarFileId?: StringNullableWithAggregatesFilter<"AdminUser"> | string | null + address?: StringNullableWithAggregatesFilter<"AdminUser"> | string | null + storeName?: StringNullableWithAggregatesFilter<"AdminUser"> | string | null + pasazhName?: StringNullableWithAggregatesFilter<"AdminUser"> | string | null + memberCode?: StringNullableWithAggregatesFilter<"AdminUser"> | string | null + } + + export type SalesExpertProfileWhereInput = { + AND?: SalesExpertProfileWhereInput | SalesExpertProfileWhereInput[] + OR?: SalesExpertProfileWhereInput[] + NOT?: SalesExpertProfileWhereInput | SalesExpertProfileWhereInput[] + id?: StringFilter<"SalesExpertProfile"> | string + adminUserId?: StringFilter<"SalesExpertProfile"> | string + cityId?: StringNullableFilter<"SalesExpertProfile"> | string | null + gramLevel?: DecimalFilter<"SalesExpertProfile"> | Decimal | DecimalJsLike | number | string + phone?: StringNullableFilter<"SalesExpertProfile"> | string | null + nationalCode?: StringNullableFilter<"SalesExpertProfile"> | string | null + tradingCustomerId?: StringNullableFilter<"SalesExpertProfile"> | string | null + createdAt?: DateTimeFilter<"SalesExpertProfile"> | Date | string + updatedAt?: DateTimeFilter<"SalesExpertProfile"> | Date | string + adminUser?: XOR + city?: XOR | null + tradingCustomer?: XOR | null + } + + export type SalesExpertProfileOrderByWithRelationInput = { + id?: SortOrder + adminUserId?: SortOrder + cityId?: SortOrderInput | SortOrder + gramLevel?: SortOrder + phone?: SortOrderInput | SortOrder + nationalCode?: SortOrderInput | SortOrder + tradingCustomerId?: SortOrderInput | SortOrder + createdAt?: SortOrder + updatedAt?: SortOrder + adminUser?: AdminUserOrderByWithRelationInput + city?: CityOrderByWithRelationInput + tradingCustomer?: CustomerOrderByWithRelationInput + } + + export type SalesExpertProfileWhereUniqueInput = Prisma.AtLeast<{ + id?: string + adminUserId?: string + tradingCustomerId?: string + AND?: SalesExpertProfileWhereInput | SalesExpertProfileWhereInput[] + OR?: SalesExpertProfileWhereInput[] + NOT?: SalesExpertProfileWhereInput | SalesExpertProfileWhereInput[] + cityId?: StringNullableFilter<"SalesExpertProfile"> | string | null + gramLevel?: DecimalFilter<"SalesExpertProfile"> | Decimal | DecimalJsLike | number | string + phone?: StringNullableFilter<"SalesExpertProfile"> | string | null + nationalCode?: StringNullableFilter<"SalesExpertProfile"> | string | null + createdAt?: DateTimeFilter<"SalesExpertProfile"> | Date | string + updatedAt?: DateTimeFilter<"SalesExpertProfile"> | Date | string + adminUser?: XOR + city?: XOR | null + tradingCustomer?: XOR | null + }, "id" | "adminUserId" | "tradingCustomerId"> + + export type SalesExpertProfileOrderByWithAggregationInput = { + id?: SortOrder + adminUserId?: SortOrder + cityId?: SortOrderInput | SortOrder + gramLevel?: SortOrder + phone?: SortOrderInput | SortOrder + nationalCode?: SortOrderInput | SortOrder + tradingCustomerId?: SortOrderInput | SortOrder + createdAt?: SortOrder + updatedAt?: SortOrder + _count?: SalesExpertProfileCountOrderByAggregateInput + _avg?: SalesExpertProfileAvgOrderByAggregateInput + _max?: SalesExpertProfileMaxOrderByAggregateInput + _min?: SalesExpertProfileMinOrderByAggregateInput + _sum?: SalesExpertProfileSumOrderByAggregateInput + } + + export type SalesExpertProfileScalarWhereWithAggregatesInput = { + AND?: SalesExpertProfileScalarWhereWithAggregatesInput | SalesExpertProfileScalarWhereWithAggregatesInput[] + OR?: SalesExpertProfileScalarWhereWithAggregatesInput[] + NOT?: SalesExpertProfileScalarWhereWithAggregatesInput | SalesExpertProfileScalarWhereWithAggregatesInput[] + id?: StringWithAggregatesFilter<"SalesExpertProfile"> | string + adminUserId?: StringWithAggregatesFilter<"SalesExpertProfile"> | string + cityId?: StringNullableWithAggregatesFilter<"SalesExpertProfile"> | string | null + gramLevel?: DecimalWithAggregatesFilter<"SalesExpertProfile"> | Decimal | DecimalJsLike | number | string + phone?: StringNullableWithAggregatesFilter<"SalesExpertProfile"> | string | null + nationalCode?: StringNullableWithAggregatesFilter<"SalesExpertProfile"> | string | null + tradingCustomerId?: StringNullableWithAggregatesFilter<"SalesExpertProfile"> | string | null + createdAt?: DateTimeWithAggregatesFilter<"SalesExpertProfile"> | Date | string + updatedAt?: DateTimeWithAggregatesFilter<"SalesExpertProfile"> | Date | string } export type CustomerWhereInput = { @@ -41958,6 +44891,8 @@ export namespace Prisma { NOT?: CustomerWhereInput | CustomerWhereInput[] id?: StringFilter<"Customer"> | string fullName?: StringFilter<"Customer"> | string + firstName?: StringNullableFilter<"Customer"> | string | null + lastName?: StringNullableFilter<"Customer"> | string | null mobile?: StringFilter<"Customer"> | string nationalCode?: StringFilter<"Customer"> | string passwordHash?: StringFilter<"Customer"> | string @@ -41973,6 +44908,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFilter<"Customer"> | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFilter<"Customer"> | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFilter<"Customer"> | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFilter<"Customer"> | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFilter<"Customer"> | $Enums.VerificationStep nationalIdImageUrl?: StringNullableFilter<"Customer"> | string | null businessLicenseUrl?: StringNullableFilter<"Customer"> | string | null @@ -41985,6 +44921,7 @@ export namespace Prisma { updatedAt?: DateTimeFilter<"Customer"> | Date | string city?: XOR | null referredByAgent?: XOR | null + salesExpertTradingFor?: XOR | null avatarFile?: XOR | null addresses?: CustomerAddressListRelationFilter orders?: OrderListRelationFilter @@ -41995,11 +44932,14 @@ export namespace Prisma { promotions?: PromotionListRelationFilter notifications?: NotificationRecipientListRelationFilter activityLogs?: CustomerActivityLogListRelationFilter + otpChallenges?: CustomerOtpListRelationFilter } export type CustomerOrderByWithRelationInput = { id?: SortOrder fullName?: SortOrder + firstName?: SortOrderInput | SortOrder + lastName?: SortOrderInput | SortOrder mobile?: SortOrder nationalCode?: SortOrder passwordHash?: SortOrder @@ -42015,6 +44955,7 @@ export namespace Prisma { goldCreditLimitGrams?: SortOrder goldDebtGrams?: SortOrder fiatDebtRial?: SortOrder + makingChargeOffsetPercent?: SortOrder verificationStep?: SortOrder nationalIdImageUrl?: SortOrderInput | SortOrder businessLicenseUrl?: SortOrderInput | SortOrder @@ -42027,6 +44968,7 @@ export namespace Prisma { updatedAt?: SortOrder city?: CityOrderByWithRelationInput referredByAgent?: AdminUserOrderByWithRelationInput + salesExpertTradingFor?: SalesExpertProfileOrderByWithRelationInput avatarFile?: UploadedFileOrderByWithRelationInput addresses?: CustomerAddressOrderByRelationAggregateInput orders?: OrderOrderByRelationAggregateInput @@ -42037,6 +44979,7 @@ export namespace Prisma { promotions?: PromotionOrderByRelationAggregateInput notifications?: NotificationRecipientOrderByRelationAggregateInput activityLogs?: CustomerActivityLogOrderByRelationAggregateInput + otpChallenges?: CustomerOtpOrderByRelationAggregateInput } export type CustomerWhereUniqueInput = Prisma.AtLeast<{ @@ -42047,6 +44990,8 @@ export namespace Prisma { OR?: CustomerWhereInput[] NOT?: CustomerWhereInput | CustomerWhereInput[] fullName?: StringFilter<"Customer"> | string + firstName?: StringNullableFilter<"Customer"> | string | null + lastName?: StringNullableFilter<"Customer"> | string | null passwordHash?: StringFilter<"Customer"> | string cityId?: StringNullableFilter<"Customer"> | string | null kind?: EnumCustomerKindFilter<"Customer"> | $Enums.CustomerKind @@ -42060,6 +45005,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFilter<"Customer"> | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFilter<"Customer"> | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFilter<"Customer"> | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFilter<"Customer"> | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFilter<"Customer"> | $Enums.VerificationStep nationalIdImageUrl?: StringNullableFilter<"Customer"> | string | null businessLicenseUrl?: StringNullableFilter<"Customer"> | string | null @@ -42072,6 +45018,7 @@ export namespace Prisma { updatedAt?: DateTimeFilter<"Customer"> | Date | string city?: XOR | null referredByAgent?: XOR | null + salesExpertTradingFor?: XOR | null avatarFile?: XOR | null addresses?: CustomerAddressListRelationFilter orders?: OrderListRelationFilter @@ -42082,11 +45029,14 @@ export namespace Prisma { promotions?: PromotionListRelationFilter notifications?: NotificationRecipientListRelationFilter activityLogs?: CustomerActivityLogListRelationFilter + otpChallenges?: CustomerOtpListRelationFilter }, "id" | "mobile" | "nationalCode"> export type CustomerOrderByWithAggregationInput = { id?: SortOrder fullName?: SortOrder + firstName?: SortOrderInput | SortOrder + lastName?: SortOrderInput | SortOrder mobile?: SortOrder nationalCode?: SortOrder passwordHash?: SortOrder @@ -42102,6 +45052,7 @@ export namespace Prisma { goldCreditLimitGrams?: SortOrder goldDebtGrams?: SortOrder fiatDebtRial?: SortOrder + makingChargeOffsetPercent?: SortOrder verificationStep?: SortOrder nationalIdImageUrl?: SortOrderInput | SortOrder businessLicenseUrl?: SortOrderInput | SortOrder @@ -42125,6 +45076,8 @@ export namespace Prisma { NOT?: CustomerScalarWhereWithAggregatesInput | CustomerScalarWhereWithAggregatesInput[] id?: StringWithAggregatesFilter<"Customer"> | string fullName?: StringWithAggregatesFilter<"Customer"> | string + firstName?: StringNullableWithAggregatesFilter<"Customer"> | string | null + lastName?: StringNullableWithAggregatesFilter<"Customer"> | string | null mobile?: StringWithAggregatesFilter<"Customer"> | string nationalCode?: StringWithAggregatesFilter<"Customer"> | string passwordHash?: StringWithAggregatesFilter<"Customer"> | string @@ -42140,6 +45093,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalWithAggregatesFilter<"Customer"> | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalWithAggregatesFilter<"Customer"> | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalWithAggregatesFilter<"Customer"> | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalWithAggregatesFilter<"Customer"> | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepWithAggregatesFilter<"Customer"> | $Enums.VerificationStep nationalIdImageUrl?: StringNullableWithAggregatesFilter<"Customer"> | string | null businessLicenseUrl?: StringNullableWithAggregatesFilter<"Customer"> | string | null @@ -42668,6 +45622,7 @@ export namespace Prisma { sortOrder?: IntFilter<"ProductImage"> | number role?: EnumProductImageRoleFilter<"ProductImage"> | $Enums.ProductImageRole product?: XOR + variants?: ProductVariantListRelationFilter } export type ProductImageOrderByWithRelationInput = { @@ -42677,6 +45632,7 @@ export namespace Prisma { sortOrder?: SortOrder role?: SortOrder product?: ProductOrderByWithRelationInput + variants?: ProductVariantOrderByRelationAggregateInput } export type ProductImageWhereUniqueInput = Prisma.AtLeast<{ @@ -42689,6 +45645,7 @@ export namespace Prisma { sortOrder?: IntFilter<"ProductImage"> | number role?: EnumProductImageRoleFilter<"ProductImage"> | $Enums.ProductImageRole product?: XOR + variants?: ProductVariantListRelationFilter }, "id"> export type ProductImageOrderByWithAggregationInput = { @@ -42728,7 +45685,9 @@ export namespace Prisma { qty?: IntFilter<"ProductVariant"> | number isPreOrder?: BoolFilter<"ProductVariant"> | boolean isActive?: BoolFilter<"ProductVariant"> | boolean + imageId?: StringNullableFilter<"ProductVariant"> | string | null product?: XOR + image?: XOR | null orderItems?: OrderItemListRelationFilter } @@ -42742,7 +45701,9 @@ export namespace Prisma { qty?: SortOrder isPreOrder?: SortOrder isActive?: SortOrder + imageId?: SortOrderInput | SortOrder product?: ProductOrderByWithRelationInput + image?: ProductImageOrderByWithRelationInput orderItems?: OrderItemOrderByRelationAggregateInput } @@ -42759,7 +45720,9 @@ export namespace Prisma { qty?: IntFilter<"ProductVariant"> | number isPreOrder?: BoolFilter<"ProductVariant"> | boolean isActive?: BoolFilter<"ProductVariant"> | boolean + imageId?: StringNullableFilter<"ProductVariant"> | string | null product?: XOR + image?: XOR | null orderItems?: OrderItemListRelationFilter }, "id" | "sku"> @@ -42773,6 +45736,7 @@ export namespace Prisma { qty?: SortOrder isPreOrder?: SortOrder isActive?: SortOrder + imageId?: SortOrderInput | SortOrder _count?: ProductVariantCountOrderByAggregateInput _avg?: ProductVariantAvgOrderByAggregateInput _max?: ProductVariantMaxOrderByAggregateInput @@ -42793,6 +45757,7 @@ export namespace Prisma { qty?: IntWithAggregatesFilter<"ProductVariant"> | number isPreOrder?: BoolWithAggregatesFilter<"ProductVariant"> | boolean isActive?: BoolWithAggregatesFilter<"ProductVariant"> | boolean + imageId?: StringNullableWithAggregatesFilter<"ProductVariant"> | string | null } export type OrderWhereInput = { @@ -44323,6 +47288,7 @@ export namespace Prisma { createdAt?: DateTimeFilter<"UploadedFile"> | Date | string uploadedByAdminUser?: XOR | null customerAvatars?: CustomerListRelationFilter + adminUserAvatars?: AdminUserListRelationFilter } export type UploadedFileOrderByWithRelationInput = { @@ -44336,6 +47302,7 @@ export namespace Prisma { createdAt?: SortOrder uploadedByAdminUser?: AdminUserOrderByWithRelationInput customerAvatars?: CustomerOrderByRelationAggregateInput + adminUserAvatars?: AdminUserOrderByRelationAggregateInput } export type UploadedFileWhereUniqueInput = Prisma.AtLeast<{ @@ -44352,6 +47319,7 @@ export namespace Prisma { createdAt?: DateTimeFilter<"UploadedFile"> | Date | string uploadedByAdminUser?: XOR | null customerAvatars?: CustomerListRelationFilter + adminUserAvatars?: AdminUserListRelationFilter }, "id"> export type UploadedFileOrderByWithAggregationInput = { @@ -44384,6 +47352,83 @@ export namespace Prisma { createdAt?: DateTimeWithAggregatesFilter<"UploadedFile"> | Date | string } + export type CustomerOtpWhereInput = { + AND?: CustomerOtpWhereInput | CustomerOtpWhereInput[] + OR?: CustomerOtpWhereInput[] + NOT?: CustomerOtpWhereInput | CustomerOtpWhereInput[] + id?: StringFilter<"CustomerOtp"> | string + mobile?: StringFilter<"CustomerOtp"> | string + customerId?: StringFilter<"CustomerOtp"> | string + codeHash?: StringFilter<"CustomerOtp"> | string + expiresAt?: DateTimeFilter<"CustomerOtp"> | Date | string + attempts?: IntFilter<"CustomerOtp"> | number + consumedAt?: DateTimeNullableFilter<"CustomerOtp"> | Date | string | null + requestIp?: StringNullableFilter<"CustomerOtp"> | string | null + createdAt?: DateTimeFilter<"CustomerOtp"> | Date | string + customer?: XOR + } + + export type CustomerOtpOrderByWithRelationInput = { + id?: SortOrder + mobile?: SortOrder + customerId?: SortOrder + codeHash?: SortOrder + expiresAt?: SortOrder + attempts?: SortOrder + consumedAt?: SortOrderInput | SortOrder + requestIp?: SortOrderInput | SortOrder + createdAt?: SortOrder + customer?: CustomerOrderByWithRelationInput + } + + export type CustomerOtpWhereUniqueInput = Prisma.AtLeast<{ + id?: string + AND?: CustomerOtpWhereInput | CustomerOtpWhereInput[] + OR?: CustomerOtpWhereInput[] + NOT?: CustomerOtpWhereInput | CustomerOtpWhereInput[] + mobile?: StringFilter<"CustomerOtp"> | string + customerId?: StringFilter<"CustomerOtp"> | string + codeHash?: StringFilter<"CustomerOtp"> | string + expiresAt?: DateTimeFilter<"CustomerOtp"> | Date | string + attempts?: IntFilter<"CustomerOtp"> | number + consumedAt?: DateTimeNullableFilter<"CustomerOtp"> | Date | string | null + requestIp?: StringNullableFilter<"CustomerOtp"> | string | null + createdAt?: DateTimeFilter<"CustomerOtp"> | Date | string + customer?: XOR + }, "id"> + + export type CustomerOtpOrderByWithAggregationInput = { + id?: SortOrder + mobile?: SortOrder + customerId?: SortOrder + codeHash?: SortOrder + expiresAt?: SortOrder + attempts?: SortOrder + consumedAt?: SortOrderInput | SortOrder + requestIp?: SortOrderInput | SortOrder + createdAt?: SortOrder + _count?: CustomerOtpCountOrderByAggregateInput + _avg?: CustomerOtpAvgOrderByAggregateInput + _max?: CustomerOtpMaxOrderByAggregateInput + _min?: CustomerOtpMinOrderByAggregateInput + _sum?: CustomerOtpSumOrderByAggregateInput + } + + export type CustomerOtpScalarWhereWithAggregatesInput = { + AND?: CustomerOtpScalarWhereWithAggregatesInput | CustomerOtpScalarWhereWithAggregatesInput[] + OR?: CustomerOtpScalarWhereWithAggregatesInput[] + NOT?: CustomerOtpScalarWhereWithAggregatesInput | CustomerOtpScalarWhereWithAggregatesInput[] + id?: StringWithAggregatesFilter<"CustomerOtp"> | string + mobile?: StringWithAggregatesFilter<"CustomerOtp"> | string + customerId?: StringWithAggregatesFilter<"CustomerOtp"> | string + codeHash?: StringWithAggregatesFilter<"CustomerOtp"> | string + expiresAt?: DateTimeWithAggregatesFilter<"CustomerOtp"> | Date | string + attempts?: IntWithAggregatesFilter<"CustomerOtp"> | number + consumedAt?: DateTimeNullableWithAggregatesFilter<"CustomerOtp"> | Date | string | null + requestIp?: StringNullableWithAggregatesFilter<"CustomerOtp"> | string | null + createdAt?: DateTimeWithAggregatesFilter<"CustomerOtp"> | Date | string + } + export type CityCreateInput = { id?: string name: string @@ -44394,6 +47439,7 @@ export namespace Prisma { addresses?: CustomerAddressCreateNestedManyWithoutCityInput branches?: BranchCreateNestedManyWithoutCityInput preOrders?: PreOrderCreateNestedManyWithoutCityInput + salesExpertProfiles?: SalesExpertProfileCreateNestedManyWithoutCityInput } export type CityUncheckedCreateInput = { @@ -44406,6 +47452,7 @@ export namespace Prisma { addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCityInput branches?: BranchUncheckedCreateNestedManyWithoutCityInput preOrders?: PreOrderUncheckedCreateNestedManyWithoutCityInput + salesExpertProfiles?: SalesExpertProfileUncheckedCreateNestedManyWithoutCityInput } export type CityUpdateInput = { @@ -44418,6 +47465,7 @@ export namespace Prisma { addresses?: CustomerAddressUpdateManyWithoutCityNestedInput branches?: BranchUpdateManyWithoutCityNestedInput preOrders?: PreOrderUpdateManyWithoutCityNestedInput + salesExpertProfiles?: SalesExpertProfileUpdateManyWithoutCityNestedInput } export type CityUncheckedUpdateInput = { @@ -44430,6 +47478,7 @@ export namespace Prisma { addresses?: CustomerAddressUncheckedUpdateManyWithoutCityNestedInput branches?: BranchUncheckedUpdateManyWithoutCityNestedInput preOrders?: PreOrderUncheckedUpdateManyWithoutCityNestedInput + salesExpertProfiles?: SalesExpertProfileUncheckedUpdateManyWithoutCityNestedInput } export type CityCreateManyInput = { @@ -44459,14 +47508,21 @@ export namespace Prisma { export type AdminUserCreateInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null userType?: UserTypeCreateNestedOneWithoutAdminUsersInput branch?: BranchCreateNestedOneWithoutAdminUsersInput + avatarFile?: UploadedFileCreateNestedOneWithoutAdminUserAvatarsInput referredCustomers?: CustomerCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationCreateNestedManyWithoutAllocatorAdminUserInput @@ -44476,11 +47532,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileCreateNestedOneWithoutAdminUserInput } export type AdminUserUncheckedCreateInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole @@ -44489,6 +47548,11 @@ export namespace Prisma { wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + avatarFileId?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null referredCustomers?: CustomerUncheckedCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopUncheckedCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationUncheckedCreateNestedManyWithoutAllocatorAdminUserInput @@ -44498,19 +47562,27 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationUncheckedCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileUncheckedCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileUncheckedCreateNestedOneWithoutAdminUserInput } export type AdminUserUpdateInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null userType?: UserTypeUpdateOneWithoutAdminUsersNestedInput branch?: BranchUpdateOneWithoutAdminUsersNestedInput + avatarFile?: UploadedFileUpdateOneWithoutAdminUserAvatarsNestedInput referredCustomers?: CustomerUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUpdateManyWithoutAllocatorAdminUserNestedInput @@ -44520,11 +47592,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUpdateOneWithoutAdminUserNestedInput } export type AdminUserUncheckedUpdateInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole @@ -44533,6 +47608,11 @@ export namespace Prisma { wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null referredCustomers?: CustomerUncheckedUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUncheckedUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUncheckedUpdateManyWithoutAllocatorAdminUserNestedInput @@ -44542,11 +47622,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUncheckedUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUncheckedUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUncheckedUpdateOneWithoutAdminUserNestedInput } export type AdminUserCreateManyInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole @@ -44555,22 +47638,35 @@ export namespace Prisma { wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + avatarFileId?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null } export type AdminUserUpdateManyMutationInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null } export type AdminUserUncheckedUpdateManyInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole @@ -44579,11 +47675,99 @@ export namespace Prisma { wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null + } + + export type SalesExpertProfileCreateInput = { + id?: string + gramLevel?: Decimal | DecimalJsLike | number | string + phone?: string | null + nationalCode?: string | null + createdAt?: Date | string + updatedAt?: Date | string + adminUser: AdminUserCreateNestedOneWithoutSalesExpertProfileInput + city?: CityCreateNestedOneWithoutSalesExpertProfilesInput + tradingCustomer?: CustomerCreateNestedOneWithoutSalesExpertTradingForInput + } + + export type SalesExpertProfileUncheckedCreateInput = { + id?: string + adminUserId: string + cityId?: string | null + gramLevel?: Decimal | DecimalJsLike | number | string + phone?: string | null + nationalCode?: string | null + tradingCustomerId?: string | null + createdAt?: Date | string + updatedAt?: Date | string + } + + export type SalesExpertProfileUpdateInput = { + id?: StringFieldUpdateOperationsInput | string + gramLevel?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + phone?: NullableStringFieldUpdateOperationsInput | string | null + nationalCode?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + adminUser?: AdminUserUpdateOneRequiredWithoutSalesExpertProfileNestedInput + city?: CityUpdateOneWithoutSalesExpertProfilesNestedInput + tradingCustomer?: CustomerUpdateOneWithoutSalesExpertTradingForNestedInput + } + + export type SalesExpertProfileUncheckedUpdateInput = { + id?: StringFieldUpdateOperationsInput | string + adminUserId?: StringFieldUpdateOperationsInput | string + cityId?: NullableStringFieldUpdateOperationsInput | string | null + gramLevel?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + phone?: NullableStringFieldUpdateOperationsInput | string | null + nationalCode?: NullableStringFieldUpdateOperationsInput | string | null + tradingCustomerId?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + } + + export type SalesExpertProfileCreateManyInput = { + id?: string + adminUserId: string + cityId?: string | null + gramLevel?: Decimal | DecimalJsLike | number | string + phone?: string | null + nationalCode?: string | null + tradingCustomerId?: string | null + createdAt?: Date | string + updatedAt?: Date | string + } + + export type SalesExpertProfileUpdateManyMutationInput = { + id?: StringFieldUpdateOperationsInput | string + gramLevel?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + phone?: NullableStringFieldUpdateOperationsInput | string | null + nationalCode?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + } + + export type SalesExpertProfileUncheckedUpdateManyInput = { + id?: StringFieldUpdateOperationsInput | string + adminUserId?: StringFieldUpdateOperationsInput | string + cityId?: NullableStringFieldUpdateOperationsInput | string | null + gramLevel?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + phone?: NullableStringFieldUpdateOperationsInput | string | null + nationalCode?: NullableStringFieldUpdateOperationsInput | string | null + tradingCustomerId?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string } export type CustomerCreateInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -44597,6 +47781,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -44608,6 +47793,7 @@ export namespace Prisma { updatedAt?: Date | string city?: CityCreateNestedOneWithoutCustomersInput referredByAgent?: AdminUserCreateNestedOneWithoutReferredCustomersInput + salesExpertTradingFor?: SalesExpertProfileCreateNestedOneWithoutTradingCustomerInput avatarFile?: UploadedFileCreateNestedOneWithoutCustomerAvatarsInput addresses?: CustomerAddressCreateNestedManyWithoutCustomerInput orders?: OrderCreateNestedManyWithoutCustomerInput @@ -44618,11 +47804,14 @@ export namespace Prisma { promotions?: PromotionCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpCreateNestedManyWithoutCustomerInput } export type CustomerUncheckedCreateInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -44638,6 +47827,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -44648,6 +47838,7 @@ export namespace Prisma { isArchived?: boolean createdAt?: Date | string updatedAt?: Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedCreateNestedOneWithoutTradingCustomerInput addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCustomerInput orders?: OrderUncheckedCreateNestedManyWithoutCustomerInput preOrders?: PreOrderUncheckedCreateNestedManyWithoutCustomerInput @@ -44657,11 +47848,14 @@ export namespace Prisma { promotions?: PromotionUncheckedCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientUncheckedCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogUncheckedCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpUncheckedCreateNestedManyWithoutCustomerInput } export type CustomerUpdateInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -44675,6 +47869,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -44686,6 +47881,7 @@ export namespace Prisma { updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string city?: CityUpdateOneWithoutCustomersNestedInput referredByAgent?: AdminUserUpdateOneWithoutReferredCustomersNestedInput + salesExpertTradingFor?: SalesExpertProfileUpdateOneWithoutTradingCustomerNestedInput avatarFile?: UploadedFileUpdateOneWithoutCustomerAvatarsNestedInput addresses?: CustomerAddressUpdateManyWithoutCustomerNestedInput orders?: OrderUpdateManyWithoutCustomerNestedInput @@ -44696,11 +47892,14 @@ export namespace Prisma { promotions?: PromotionUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUpdateManyWithoutCustomerNestedInput } export type CustomerUncheckedUpdateInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -44716,6 +47915,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -44726,6 +47926,7 @@ export namespace Prisma { isArchived?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedUpdateOneWithoutTradingCustomerNestedInput addresses?: CustomerAddressUncheckedUpdateManyWithoutCustomerNestedInput orders?: OrderUncheckedUpdateManyWithoutCustomerNestedInput preOrders?: PreOrderUncheckedUpdateManyWithoutCustomerNestedInput @@ -44735,11 +47936,14 @@ export namespace Prisma { promotions?: PromotionUncheckedUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUncheckedUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUncheckedUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUncheckedUpdateManyWithoutCustomerNestedInput } export type CustomerCreateManyInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -44755,6 +47959,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -44770,6 +47975,8 @@ export namespace Prisma { export type CustomerUpdateManyMutationInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -44783,6 +47990,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -44797,6 +48005,8 @@ export namespace Prisma { export type CustomerUncheckedUpdateManyInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -44812,6 +48022,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -45348,6 +48559,7 @@ export namespace Prisma { sortOrder?: number role?: $Enums.ProductImageRole product: ProductCreateNestedOneWithoutImagesInput + variants?: ProductVariantCreateNestedManyWithoutImageInput } export type ProductImageUncheckedCreateInput = { @@ -45356,6 +48568,7 @@ export namespace Prisma { url: string sortOrder?: number role?: $Enums.ProductImageRole + variants?: ProductVariantUncheckedCreateNestedManyWithoutImageInput } export type ProductImageUpdateInput = { @@ -45364,6 +48577,7 @@ export namespace Prisma { sortOrder?: IntFieldUpdateOperationsInput | number role?: EnumProductImageRoleFieldUpdateOperationsInput | $Enums.ProductImageRole product?: ProductUpdateOneRequiredWithoutImagesNestedInput + variants?: ProductVariantUpdateManyWithoutImageNestedInput } export type ProductImageUncheckedUpdateInput = { @@ -45372,6 +48586,7 @@ export namespace Prisma { url?: StringFieldUpdateOperationsInput | string sortOrder?: IntFieldUpdateOperationsInput | number role?: EnumProductImageRoleFieldUpdateOperationsInput | $Enums.ProductImageRole + variants?: ProductVariantUncheckedUpdateManyWithoutImageNestedInput } export type ProductImageCreateManyInput = { @@ -45407,6 +48622,7 @@ export namespace Prisma { isPreOrder?: boolean isActive?: boolean product: ProductCreateNestedOneWithoutVariantsInput + image?: ProductImageCreateNestedOneWithoutVariantsInput orderItems?: OrderItemCreateNestedManyWithoutProductVariantInput } @@ -45420,6 +48636,7 @@ export namespace Prisma { qty?: number isPreOrder?: boolean isActive?: boolean + imageId?: string | null orderItems?: OrderItemUncheckedCreateNestedManyWithoutProductVariantInput } @@ -45433,6 +48650,7 @@ export namespace Prisma { isPreOrder?: BoolFieldUpdateOperationsInput | boolean isActive?: BoolFieldUpdateOperationsInput | boolean product?: ProductUpdateOneRequiredWithoutVariantsNestedInput + image?: ProductImageUpdateOneWithoutVariantsNestedInput orderItems?: OrderItemUpdateManyWithoutProductVariantNestedInput } @@ -45446,6 +48664,7 @@ export namespace Prisma { qty?: IntFieldUpdateOperationsInput | number isPreOrder?: BoolFieldUpdateOperationsInput | boolean isActive?: BoolFieldUpdateOperationsInput | boolean + imageId?: NullableStringFieldUpdateOperationsInput | string | null orderItems?: OrderItemUncheckedUpdateManyWithoutProductVariantNestedInput } @@ -45459,6 +48678,7 @@ export namespace Prisma { qty?: number isPreOrder?: boolean isActive?: boolean + imageId?: string | null } export type ProductVariantUpdateManyMutationInput = { @@ -45482,6 +48702,7 @@ export namespace Prisma { qty?: IntFieldUpdateOperationsInput | number isPreOrder?: BoolFieldUpdateOperationsInput | boolean isActive?: BoolFieldUpdateOperationsInput | boolean + imageId?: NullableStringFieldUpdateOperationsInput | string | null } export type OrderCreateInput = { @@ -47048,6 +50269,7 @@ export namespace Prisma { createdAt?: Date | string uploadedByAdminUser?: AdminUserCreateNestedOneWithoutUploadedFilesInput customerAvatars?: CustomerCreateNestedManyWithoutAvatarFileInput + adminUserAvatars?: AdminUserCreateNestedManyWithoutAvatarFileInput } export type UploadedFileUncheckedCreateInput = { @@ -47060,6 +50282,7 @@ export namespace Prisma { uploadedByAdminUserId?: string | null createdAt?: Date | string customerAvatars?: CustomerUncheckedCreateNestedManyWithoutAvatarFileInput + adminUserAvatars?: AdminUserUncheckedCreateNestedManyWithoutAvatarFileInput } export type UploadedFileUpdateInput = { @@ -47072,6 +50295,7 @@ export namespace Prisma { createdAt?: DateTimeFieldUpdateOperationsInput | Date | string uploadedByAdminUser?: AdminUserUpdateOneWithoutUploadedFilesNestedInput customerAvatars?: CustomerUpdateManyWithoutAvatarFileNestedInput + adminUserAvatars?: AdminUserUpdateManyWithoutAvatarFileNestedInput } export type UploadedFileUncheckedUpdateInput = { @@ -47084,6 +50308,7 @@ export namespace Prisma { uploadedByAdminUserId?: NullableStringFieldUpdateOperationsInput | string | null createdAt?: DateTimeFieldUpdateOperationsInput | Date | string customerAvatars?: CustomerUncheckedUpdateManyWithoutAvatarFileNestedInput + adminUserAvatars?: AdminUserUncheckedUpdateManyWithoutAvatarFileNestedInput } export type UploadedFileCreateManyInput = { @@ -47118,6 +50343,89 @@ export namespace Prisma { createdAt?: DateTimeFieldUpdateOperationsInput | Date | string } + export type CustomerOtpCreateInput = { + id?: string + mobile: string + codeHash: string + expiresAt: Date | string + attempts?: number + consumedAt?: Date | string | null + requestIp?: string | null + createdAt?: Date | string + customer: CustomerCreateNestedOneWithoutOtpChallengesInput + } + + export type CustomerOtpUncheckedCreateInput = { + id?: string + mobile: string + customerId: string + codeHash: string + expiresAt: Date | string + attempts?: number + consumedAt?: Date | string | null + requestIp?: string | null + createdAt?: Date | string + } + + export type CustomerOtpUpdateInput = { + id?: StringFieldUpdateOperationsInput | string + mobile?: StringFieldUpdateOperationsInput | string + codeHash?: StringFieldUpdateOperationsInput | string + expiresAt?: DateTimeFieldUpdateOperationsInput | Date | string + attempts?: IntFieldUpdateOperationsInput | number + consumedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null + requestIp?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + customer?: CustomerUpdateOneRequiredWithoutOtpChallengesNestedInput + } + + export type CustomerOtpUncheckedUpdateInput = { + id?: StringFieldUpdateOperationsInput | string + mobile?: StringFieldUpdateOperationsInput | string + customerId?: StringFieldUpdateOperationsInput | string + codeHash?: StringFieldUpdateOperationsInput | string + expiresAt?: DateTimeFieldUpdateOperationsInput | Date | string + attempts?: IntFieldUpdateOperationsInput | number + consumedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null + requestIp?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + } + + export type CustomerOtpCreateManyInput = { + id?: string + mobile: string + customerId: string + codeHash: string + expiresAt: Date | string + attempts?: number + consumedAt?: Date | string | null + requestIp?: string | null + createdAt?: Date | string + } + + export type CustomerOtpUpdateManyMutationInput = { + id?: StringFieldUpdateOperationsInput | string + mobile?: StringFieldUpdateOperationsInput | string + codeHash?: StringFieldUpdateOperationsInput | string + expiresAt?: DateTimeFieldUpdateOperationsInput | Date | string + attempts?: IntFieldUpdateOperationsInput | number + consumedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null + requestIp?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + } + + export type CustomerOtpUncheckedUpdateManyInput = { + id?: StringFieldUpdateOperationsInput | string + mobile?: StringFieldUpdateOperationsInput | string + customerId?: StringFieldUpdateOperationsInput | string + codeHash?: StringFieldUpdateOperationsInput | string + expiresAt?: DateTimeFieldUpdateOperationsInput | Date | string + attempts?: IntFieldUpdateOperationsInput | number + consumedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null + requestIp?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + } + export type StringFilter<$PrismaModel = never> = { equals?: string | StringFieldRefInput<$PrismaModel> in?: string[] | ListStringFieldRefInput<$PrismaModel> @@ -47183,6 +50491,12 @@ export namespace Prisma { none?: PreOrderWhereInput } + export type SalesExpertProfileListRelationFilter = { + every?: SalesExpertProfileWhereInput + some?: SalesExpertProfileWhereInput + none?: SalesExpertProfileWhereInput + } + export type SortOrderInput = { sort: SortOrder nulls?: NullsOrder @@ -47204,6 +50518,10 @@ export namespace Prisma { _count?: SortOrder } + export type SalesExpertProfileOrderByRelationAggregateInput = { + _count?: SortOrder + } + export type CityCountOrderByAggregateInput = { id?: SortOrder name?: SortOrder @@ -47334,6 +50652,11 @@ export namespace Prisma { isNot?: BranchWhereInput | null } + export type UploadedFileNullableRelationFilter = { + is?: UploadedFileWhereInput | null + isNot?: UploadedFileWhereInput | null + } + export type WorkshopListRelationFilter = { every?: WorkshopWhereInput some?: WorkshopWhereInput @@ -47382,6 +50705,11 @@ export namespace Prisma { none?: UploadedFileWhereInput } + export type SalesExpertProfileNullableRelationFilter = { + is?: SalesExpertProfileWhereInput | null + isNot?: SalesExpertProfileWhereInput | null + } + export type WorkshopOrderByRelationAggregateInput = { _count?: SortOrder } @@ -47417,6 +50745,8 @@ export namespace Prisma { export type AdminUserCountOrderByAggregateInput = { id?: SortOrder fullName?: SortOrder + firstName?: SortOrder + lastName?: SortOrder mobile?: SortOrder passwordHash?: SortOrder role?: SortOrder @@ -47425,6 +50755,11 @@ export namespace Prisma { wagePercent?: SortOrder isActive?: SortOrder createdAt?: SortOrder + avatarFileId?: SortOrder + address?: SortOrder + storeName?: SortOrder + pasazhName?: SortOrder + memberCode?: SortOrder } export type AdminUserAvgOrderByAggregateInput = { @@ -47435,6 +50770,8 @@ export namespace Prisma { export type AdminUserMaxOrderByAggregateInput = { id?: SortOrder fullName?: SortOrder + firstName?: SortOrder + lastName?: SortOrder mobile?: SortOrder passwordHash?: SortOrder role?: SortOrder @@ -47443,11 +50780,18 @@ export namespace Prisma { wagePercent?: SortOrder isActive?: SortOrder createdAt?: SortOrder + avatarFileId?: SortOrder + address?: SortOrder + storeName?: SortOrder + pasazhName?: SortOrder + memberCode?: SortOrder } export type AdminUserMinOrderByAggregateInput = { id?: SortOrder fullName?: SortOrder + firstName?: SortOrder + lastName?: SortOrder mobile?: SortOrder passwordHash?: SortOrder role?: SortOrder @@ -47456,6 +50800,11 @@ export namespace Prisma { wagePercent?: SortOrder isActive?: SortOrder createdAt?: SortOrder + avatarFileId?: SortOrder + address?: SortOrder + storeName?: SortOrder + pasazhName?: SortOrder + memberCode?: SortOrder } export type AdminUserSumOrderByAggregateInput = { @@ -47511,13 +50860,6 @@ export namespace Prisma { _max?: NestedDateTimeFilter<$PrismaModel> } - export type EnumCustomerKindFilter<$PrismaModel = never> = { - equals?: $Enums.CustomerKind | EnumCustomerKindFieldRefInput<$PrismaModel> - in?: $Enums.CustomerKind[] | ListEnumCustomerKindFieldRefInput<$PrismaModel> - notIn?: $Enums.CustomerKind[] | ListEnumCustomerKindFieldRefInput<$PrismaModel> - not?: NestedEnumCustomerKindFilter<$PrismaModel> | $Enums.CustomerKind - } - export type DecimalFilter<$PrismaModel = never> = { equals?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> in?: Decimal[] | DecimalJsLike[] | number[] | string[] | ListDecimalFieldRefInput<$PrismaModel> @@ -47529,11 +50871,9 @@ export namespace Prisma { not?: NestedDecimalFilter<$PrismaModel> | Decimal | DecimalJsLike | number | string } - export type EnumVerificationStepFilter<$PrismaModel = never> = { - equals?: $Enums.VerificationStep | EnumVerificationStepFieldRefInput<$PrismaModel> - in?: $Enums.VerificationStep[] | ListEnumVerificationStepFieldRefInput<$PrismaModel> - notIn?: $Enums.VerificationStep[] | ListEnumVerificationStepFieldRefInput<$PrismaModel> - not?: NestedEnumVerificationStepFilter<$PrismaModel> | $Enums.VerificationStep + export type AdminUserRelationFilter = { + is?: AdminUserWhereInput + isNot?: AdminUserWhereInput } export type CityNullableRelationFilter = { @@ -47541,16 +50881,90 @@ export namespace Prisma { isNot?: CityWhereInput | null } + export type CustomerNullableRelationFilter = { + is?: CustomerWhereInput | null + isNot?: CustomerWhereInput | null + } + + export type SalesExpertProfileCountOrderByAggregateInput = { + id?: SortOrder + adminUserId?: SortOrder + cityId?: SortOrder + gramLevel?: SortOrder + phone?: SortOrder + nationalCode?: SortOrder + tradingCustomerId?: SortOrder + createdAt?: SortOrder + updatedAt?: SortOrder + } + + export type SalesExpertProfileAvgOrderByAggregateInput = { + gramLevel?: SortOrder + } + + export type SalesExpertProfileMaxOrderByAggregateInput = { + id?: SortOrder + adminUserId?: SortOrder + cityId?: SortOrder + gramLevel?: SortOrder + phone?: SortOrder + nationalCode?: SortOrder + tradingCustomerId?: SortOrder + createdAt?: SortOrder + updatedAt?: SortOrder + } + + export type SalesExpertProfileMinOrderByAggregateInput = { + id?: SortOrder + adminUserId?: SortOrder + cityId?: SortOrder + gramLevel?: SortOrder + phone?: SortOrder + nationalCode?: SortOrder + tradingCustomerId?: SortOrder + createdAt?: SortOrder + updatedAt?: SortOrder + } + + export type SalesExpertProfileSumOrderByAggregateInput = { + gramLevel?: SortOrder + } + + export type DecimalWithAggregatesFilter<$PrismaModel = never> = { + equals?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> + in?: Decimal[] | DecimalJsLike[] | number[] | string[] | ListDecimalFieldRefInput<$PrismaModel> + notIn?: Decimal[] | DecimalJsLike[] | number[] | string[] | ListDecimalFieldRefInput<$PrismaModel> + lt?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> + lte?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> + gt?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> + gte?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> + not?: NestedDecimalWithAggregatesFilter<$PrismaModel> | Decimal | DecimalJsLike | number | string + _count?: NestedIntFilter<$PrismaModel> + _avg?: NestedDecimalFilter<$PrismaModel> + _sum?: NestedDecimalFilter<$PrismaModel> + _min?: NestedDecimalFilter<$PrismaModel> + _max?: NestedDecimalFilter<$PrismaModel> + } + + export type EnumCustomerKindFilter<$PrismaModel = never> = { + equals?: $Enums.CustomerKind | EnumCustomerKindFieldRefInput<$PrismaModel> + in?: $Enums.CustomerKind[] | ListEnumCustomerKindFieldRefInput<$PrismaModel> + notIn?: $Enums.CustomerKind[] | ListEnumCustomerKindFieldRefInput<$PrismaModel> + not?: NestedEnumCustomerKindFilter<$PrismaModel> | $Enums.CustomerKind + } + + export type EnumVerificationStepFilter<$PrismaModel = never> = { + equals?: $Enums.VerificationStep | EnumVerificationStepFieldRefInput<$PrismaModel> + in?: $Enums.VerificationStep[] | ListEnumVerificationStepFieldRefInput<$PrismaModel> + notIn?: $Enums.VerificationStep[] | ListEnumVerificationStepFieldRefInput<$PrismaModel> + not?: NestedEnumVerificationStepFilter<$PrismaModel> | $Enums.VerificationStep + } + export type AdminUserNullableRelationFilter = { is?: AdminUserWhereInput | null isNot?: AdminUserWhereInput | null } - export type UploadedFileNullableRelationFilter = { - is?: UploadedFileWhereInput | null - isNot?: UploadedFileWhereInput | null - } - export type OrderListRelationFilter = { every?: OrderWhereInput some?: OrderWhereInput @@ -47587,6 +51001,12 @@ export namespace Prisma { none?: CustomerActivityLogWhereInput } + export type CustomerOtpListRelationFilter = { + every?: CustomerOtpWhereInput + some?: CustomerOtpWhereInput + none?: CustomerOtpWhereInput + } + export type OrderOrderByRelationAggregateInput = { _count?: SortOrder } @@ -47611,9 +51031,15 @@ export namespace Prisma { _count?: SortOrder } + export type CustomerOtpOrderByRelationAggregateInput = { + _count?: SortOrder + } + export type CustomerCountOrderByAggregateInput = { id?: SortOrder fullName?: SortOrder + firstName?: SortOrder + lastName?: SortOrder mobile?: SortOrder nationalCode?: SortOrder passwordHash?: SortOrder @@ -47629,6 +51055,7 @@ export namespace Prisma { goldCreditLimitGrams?: SortOrder goldDebtGrams?: SortOrder fiatDebtRial?: SortOrder + makingChargeOffsetPercent?: SortOrder verificationStep?: SortOrder nationalIdImageUrl?: SortOrder businessLicenseUrl?: SortOrder @@ -47645,11 +51072,14 @@ export namespace Prisma { goldCreditLimitGrams?: SortOrder goldDebtGrams?: SortOrder fiatDebtRial?: SortOrder + makingChargeOffsetPercent?: SortOrder } export type CustomerMaxOrderByAggregateInput = { id?: SortOrder fullName?: SortOrder + firstName?: SortOrder + lastName?: SortOrder mobile?: SortOrder nationalCode?: SortOrder passwordHash?: SortOrder @@ -47665,6 +51095,7 @@ export namespace Prisma { goldCreditLimitGrams?: SortOrder goldDebtGrams?: SortOrder fiatDebtRial?: SortOrder + makingChargeOffsetPercent?: SortOrder verificationStep?: SortOrder nationalIdImageUrl?: SortOrder businessLicenseUrl?: SortOrder @@ -47680,6 +51111,8 @@ export namespace Prisma { export type CustomerMinOrderByAggregateInput = { id?: SortOrder fullName?: SortOrder + firstName?: SortOrder + lastName?: SortOrder mobile?: SortOrder nationalCode?: SortOrder passwordHash?: SortOrder @@ -47695,6 +51128,7 @@ export namespace Prisma { goldCreditLimitGrams?: SortOrder goldDebtGrams?: SortOrder fiatDebtRial?: SortOrder + makingChargeOffsetPercent?: SortOrder verificationStep?: SortOrder nationalIdImageUrl?: SortOrder businessLicenseUrl?: SortOrder @@ -47711,6 +51145,7 @@ export namespace Prisma { goldCreditLimitGrams?: SortOrder goldDebtGrams?: SortOrder fiatDebtRial?: SortOrder + makingChargeOffsetPercent?: SortOrder } export type EnumCustomerKindWithAggregatesFilter<$PrismaModel = never> = { @@ -47723,22 +51158,6 @@ export namespace Prisma { _max?: NestedEnumCustomerKindFilter<$PrismaModel> } - export type DecimalWithAggregatesFilter<$PrismaModel = never> = { - equals?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> - in?: Decimal[] | DecimalJsLike[] | number[] | string[] | ListDecimalFieldRefInput<$PrismaModel> - notIn?: Decimal[] | DecimalJsLike[] | number[] | string[] | ListDecimalFieldRefInput<$PrismaModel> - lt?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> - lte?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> - gt?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> - gte?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> - not?: NestedDecimalWithAggregatesFilter<$PrismaModel> | Decimal | DecimalJsLike | number | string - _count?: NestedIntFilter<$PrismaModel> - _avg?: NestedDecimalFilter<$PrismaModel> - _sum?: NestedDecimalFilter<$PrismaModel> - _min?: NestedDecimalFilter<$PrismaModel> - _max?: NestedDecimalFilter<$PrismaModel> - } - export type EnumVerificationStepWithAggregatesFilter<$PrismaModel = never> = { equals?: $Enums.VerificationStep | EnumVerificationStepFieldRefInput<$PrismaModel> in?: $Enums.VerificationStep[] | ListEnumVerificationStepFieldRefInput<$PrismaModel> @@ -48139,6 +51558,11 @@ export namespace Prisma { _max?: NestedEnumProductImageRoleFilter<$PrismaModel> } + export type ProductImageNullableRelationFilter = { + is?: ProductImageWhereInput | null + isNot?: ProductImageWhereInput | null + } + export type OrderItemListRelationFilter = { every?: OrderItemWhereInput some?: OrderItemWhereInput @@ -48159,6 +51583,7 @@ export namespace Prisma { qty?: SortOrder isPreOrder?: SortOrder isActive?: SortOrder + imageId?: SortOrder } export type ProductVariantAvgOrderByAggregateInput = { @@ -48177,6 +51602,7 @@ export namespace Prisma { qty?: SortOrder isPreOrder?: SortOrder isActive?: SortOrder + imageId?: SortOrder } export type ProductVariantMinOrderByAggregateInput = { @@ -48189,6 +51615,7 @@ export namespace Prisma { qty?: SortOrder isPreOrder?: SortOrder isActive?: SortOrder + imageId?: SortOrder } export type ProductVariantSumOrderByAggregateInput = { @@ -48982,11 +52409,6 @@ export namespace Prisma { isNot?: ProductWhereInput | null } - export type CustomerNullableRelationFilter = { - is?: CustomerWhereInput | null - isNot?: CustomerWhereInput | null - } - export type PromotionCountOrderByAggregateInput = { id?: SortOrder title?: SortOrder @@ -49375,6 +52797,50 @@ export namespace Prisma { sizeBytes?: SortOrder } + export type CustomerOtpCountOrderByAggregateInput = { + id?: SortOrder + mobile?: SortOrder + customerId?: SortOrder + codeHash?: SortOrder + expiresAt?: SortOrder + attempts?: SortOrder + consumedAt?: SortOrder + requestIp?: SortOrder + createdAt?: SortOrder + } + + export type CustomerOtpAvgOrderByAggregateInput = { + attempts?: SortOrder + } + + export type CustomerOtpMaxOrderByAggregateInput = { + id?: SortOrder + mobile?: SortOrder + customerId?: SortOrder + codeHash?: SortOrder + expiresAt?: SortOrder + attempts?: SortOrder + consumedAt?: SortOrder + requestIp?: SortOrder + createdAt?: SortOrder + } + + export type CustomerOtpMinOrderByAggregateInput = { + id?: SortOrder + mobile?: SortOrder + customerId?: SortOrder + codeHash?: SortOrder + expiresAt?: SortOrder + attempts?: SortOrder + consumedAt?: SortOrder + requestIp?: SortOrder + createdAt?: SortOrder + } + + export type CustomerOtpSumOrderByAggregateInput = { + attempts?: SortOrder + } + export type CustomerCreateNestedManyWithoutCityInput = { create?: XOR | CustomerCreateWithoutCityInput[] | CustomerUncheckedCreateWithoutCityInput[] connectOrCreate?: CustomerCreateOrConnectWithoutCityInput | CustomerCreateOrConnectWithoutCityInput[] @@ -49403,6 +52869,13 @@ export namespace Prisma { connect?: PreOrderWhereUniqueInput | PreOrderWhereUniqueInput[] } + export type SalesExpertProfileCreateNestedManyWithoutCityInput = { + create?: XOR | SalesExpertProfileCreateWithoutCityInput[] | SalesExpertProfileUncheckedCreateWithoutCityInput[] + connectOrCreate?: SalesExpertProfileCreateOrConnectWithoutCityInput | SalesExpertProfileCreateOrConnectWithoutCityInput[] + createMany?: SalesExpertProfileCreateManyCityInputEnvelope + connect?: SalesExpertProfileWhereUniqueInput | SalesExpertProfileWhereUniqueInput[] + } + export type CustomerUncheckedCreateNestedManyWithoutCityInput = { create?: XOR | CustomerCreateWithoutCityInput[] | CustomerUncheckedCreateWithoutCityInput[] connectOrCreate?: CustomerCreateOrConnectWithoutCityInput | CustomerCreateOrConnectWithoutCityInput[] @@ -49431,6 +52904,13 @@ export namespace Prisma { connect?: PreOrderWhereUniqueInput | PreOrderWhereUniqueInput[] } + export type SalesExpertProfileUncheckedCreateNestedManyWithoutCityInput = { + create?: XOR | SalesExpertProfileCreateWithoutCityInput[] | SalesExpertProfileUncheckedCreateWithoutCityInput[] + connectOrCreate?: SalesExpertProfileCreateOrConnectWithoutCityInput | SalesExpertProfileCreateOrConnectWithoutCityInput[] + createMany?: SalesExpertProfileCreateManyCityInputEnvelope + connect?: SalesExpertProfileWhereUniqueInput | SalesExpertProfileWhereUniqueInput[] + } + export type StringFieldUpdateOperationsInput = { set?: string } @@ -49503,6 +52983,20 @@ export namespace Prisma { deleteMany?: PreOrderScalarWhereInput | PreOrderScalarWhereInput[] } + export type SalesExpertProfileUpdateManyWithoutCityNestedInput = { + create?: XOR | SalesExpertProfileCreateWithoutCityInput[] | SalesExpertProfileUncheckedCreateWithoutCityInput[] + connectOrCreate?: SalesExpertProfileCreateOrConnectWithoutCityInput | SalesExpertProfileCreateOrConnectWithoutCityInput[] + upsert?: SalesExpertProfileUpsertWithWhereUniqueWithoutCityInput | SalesExpertProfileUpsertWithWhereUniqueWithoutCityInput[] + createMany?: SalesExpertProfileCreateManyCityInputEnvelope + set?: SalesExpertProfileWhereUniqueInput | SalesExpertProfileWhereUniqueInput[] + disconnect?: SalesExpertProfileWhereUniqueInput | SalesExpertProfileWhereUniqueInput[] + delete?: SalesExpertProfileWhereUniqueInput | SalesExpertProfileWhereUniqueInput[] + connect?: SalesExpertProfileWhereUniqueInput | SalesExpertProfileWhereUniqueInput[] + update?: SalesExpertProfileUpdateWithWhereUniqueWithoutCityInput | SalesExpertProfileUpdateWithWhereUniqueWithoutCityInput[] + updateMany?: SalesExpertProfileUpdateManyWithWhereWithoutCityInput | SalesExpertProfileUpdateManyWithWhereWithoutCityInput[] + deleteMany?: SalesExpertProfileScalarWhereInput | SalesExpertProfileScalarWhereInput[] + } + export type CustomerUncheckedUpdateManyWithoutCityNestedInput = { create?: XOR | CustomerCreateWithoutCityInput[] | CustomerUncheckedCreateWithoutCityInput[] connectOrCreate?: CustomerCreateOrConnectWithoutCityInput | CustomerCreateOrConnectWithoutCityInput[] @@ -49559,6 +53053,20 @@ export namespace Prisma { deleteMany?: PreOrderScalarWhereInput | PreOrderScalarWhereInput[] } + export type SalesExpertProfileUncheckedUpdateManyWithoutCityNestedInput = { + create?: XOR | SalesExpertProfileCreateWithoutCityInput[] | SalesExpertProfileUncheckedCreateWithoutCityInput[] + connectOrCreate?: SalesExpertProfileCreateOrConnectWithoutCityInput | SalesExpertProfileCreateOrConnectWithoutCityInput[] + upsert?: SalesExpertProfileUpsertWithWhereUniqueWithoutCityInput | SalesExpertProfileUpsertWithWhereUniqueWithoutCityInput[] + createMany?: SalesExpertProfileCreateManyCityInputEnvelope + set?: SalesExpertProfileWhereUniqueInput | SalesExpertProfileWhereUniqueInput[] + disconnect?: SalesExpertProfileWhereUniqueInput | SalesExpertProfileWhereUniqueInput[] + delete?: SalesExpertProfileWhereUniqueInput | SalesExpertProfileWhereUniqueInput[] + connect?: SalesExpertProfileWhereUniqueInput | SalesExpertProfileWhereUniqueInput[] + update?: SalesExpertProfileUpdateWithWhereUniqueWithoutCityInput | SalesExpertProfileUpdateWithWhereUniqueWithoutCityInput[] + updateMany?: SalesExpertProfileUpdateManyWithWhereWithoutCityInput | SalesExpertProfileUpdateManyWithWhereWithoutCityInput[] + deleteMany?: SalesExpertProfileScalarWhereInput | SalesExpertProfileScalarWhereInput[] + } + export type UserTypeCreateNestedOneWithoutAdminUsersInput = { create?: XOR connectOrCreate?: UserTypeCreateOrConnectWithoutAdminUsersInput @@ -49571,6 +53079,12 @@ export namespace Prisma { connect?: BranchWhereUniqueInput } + export type UploadedFileCreateNestedOneWithoutAdminUserAvatarsInput = { + create?: XOR + connectOrCreate?: UploadedFileCreateOrConnectWithoutAdminUserAvatarsInput + connect?: UploadedFileWhereUniqueInput + } + export type CustomerCreateNestedManyWithoutReferredByAgentInput = { create?: XOR | CustomerCreateWithoutReferredByAgentInput[] | CustomerUncheckedCreateWithoutReferredByAgentInput[] connectOrCreate?: CustomerCreateOrConnectWithoutReferredByAgentInput | CustomerCreateOrConnectWithoutReferredByAgentInput[] @@ -49634,6 +53148,12 @@ export namespace Prisma { connect?: UploadedFileWhereUniqueInput | UploadedFileWhereUniqueInput[] } + export type SalesExpertProfileCreateNestedOneWithoutAdminUserInput = { + create?: XOR + connectOrCreate?: SalesExpertProfileCreateOrConnectWithoutAdminUserInput + connect?: SalesExpertProfileWhereUniqueInput + } + export type CustomerUncheckedCreateNestedManyWithoutReferredByAgentInput = { create?: XOR | CustomerCreateWithoutReferredByAgentInput[] | CustomerUncheckedCreateWithoutReferredByAgentInput[] connectOrCreate?: CustomerCreateOrConnectWithoutReferredByAgentInput | CustomerCreateOrConnectWithoutReferredByAgentInput[] @@ -49697,6 +53217,12 @@ export namespace Prisma { connect?: UploadedFileWhereUniqueInput | UploadedFileWhereUniqueInput[] } + export type SalesExpertProfileUncheckedCreateNestedOneWithoutAdminUserInput = { + create?: XOR + connectOrCreate?: SalesExpertProfileCreateOrConnectWithoutAdminUserInput + connect?: SalesExpertProfileWhereUniqueInput + } + export type EnumAdminRoleFieldUpdateOperationsInput = { set?: $Enums.AdminRole } @@ -49729,6 +53255,16 @@ export namespace Prisma { update?: XOR, BranchUncheckedUpdateWithoutAdminUsersInput> } + export type UploadedFileUpdateOneWithoutAdminUserAvatarsNestedInput = { + create?: XOR + connectOrCreate?: UploadedFileCreateOrConnectWithoutAdminUserAvatarsInput + upsert?: UploadedFileUpsertWithoutAdminUserAvatarsInput + disconnect?: UploadedFileWhereInput | boolean + delete?: UploadedFileWhereInput | boolean + connect?: UploadedFileWhereUniqueInput + update?: XOR, UploadedFileUncheckedUpdateWithoutAdminUserAvatarsInput> + } + export type CustomerUpdateManyWithoutReferredByAgentNestedInput = { create?: XOR | CustomerCreateWithoutReferredByAgentInput[] | CustomerUncheckedCreateWithoutReferredByAgentInput[] connectOrCreate?: CustomerCreateOrConnectWithoutReferredByAgentInput | CustomerCreateOrConnectWithoutReferredByAgentInput[] @@ -49855,6 +53391,16 @@ export namespace Prisma { deleteMany?: UploadedFileScalarWhereInput | UploadedFileScalarWhereInput[] } + export type SalesExpertProfileUpdateOneWithoutAdminUserNestedInput = { + create?: XOR + connectOrCreate?: SalesExpertProfileCreateOrConnectWithoutAdminUserInput + upsert?: SalesExpertProfileUpsertWithoutAdminUserInput + disconnect?: SalesExpertProfileWhereInput | boolean + delete?: SalesExpertProfileWhereInput | boolean + connect?: SalesExpertProfileWhereUniqueInput + update?: XOR, SalesExpertProfileUncheckedUpdateWithoutAdminUserInput> + } + export type NullableIntFieldUpdateOperationsInput = { set?: number | null increment?: number @@ -49989,6 +53535,70 @@ export namespace Prisma { deleteMany?: UploadedFileScalarWhereInput | UploadedFileScalarWhereInput[] } + export type SalesExpertProfileUncheckedUpdateOneWithoutAdminUserNestedInput = { + create?: XOR + connectOrCreate?: SalesExpertProfileCreateOrConnectWithoutAdminUserInput + upsert?: SalesExpertProfileUpsertWithoutAdminUserInput + disconnect?: SalesExpertProfileWhereInput | boolean + delete?: SalesExpertProfileWhereInput | boolean + connect?: SalesExpertProfileWhereUniqueInput + update?: XOR, SalesExpertProfileUncheckedUpdateWithoutAdminUserInput> + } + + export type AdminUserCreateNestedOneWithoutSalesExpertProfileInput = { + create?: XOR + connectOrCreate?: AdminUserCreateOrConnectWithoutSalesExpertProfileInput + connect?: AdminUserWhereUniqueInput + } + + export type CityCreateNestedOneWithoutSalesExpertProfilesInput = { + create?: XOR + connectOrCreate?: CityCreateOrConnectWithoutSalesExpertProfilesInput + connect?: CityWhereUniqueInput + } + + export type CustomerCreateNestedOneWithoutSalesExpertTradingForInput = { + create?: XOR + connectOrCreate?: CustomerCreateOrConnectWithoutSalesExpertTradingForInput + connect?: CustomerWhereUniqueInput + } + + export type DecimalFieldUpdateOperationsInput = { + set?: Decimal | DecimalJsLike | number | string + increment?: Decimal | DecimalJsLike | number | string + decrement?: Decimal | DecimalJsLike | number | string + multiply?: Decimal | DecimalJsLike | number | string + divide?: Decimal | DecimalJsLike | number | string + } + + export type AdminUserUpdateOneRequiredWithoutSalesExpertProfileNestedInput = { + create?: XOR + connectOrCreate?: AdminUserCreateOrConnectWithoutSalesExpertProfileInput + upsert?: AdminUserUpsertWithoutSalesExpertProfileInput + connect?: AdminUserWhereUniqueInput + update?: XOR, AdminUserUncheckedUpdateWithoutSalesExpertProfileInput> + } + + export type CityUpdateOneWithoutSalesExpertProfilesNestedInput = { + create?: XOR + connectOrCreate?: CityCreateOrConnectWithoutSalesExpertProfilesInput + upsert?: CityUpsertWithoutSalesExpertProfilesInput + disconnect?: CityWhereInput | boolean + delete?: CityWhereInput | boolean + connect?: CityWhereUniqueInput + update?: XOR, CityUncheckedUpdateWithoutSalesExpertProfilesInput> + } + + export type CustomerUpdateOneWithoutSalesExpertTradingForNestedInput = { + create?: XOR + connectOrCreate?: CustomerCreateOrConnectWithoutSalesExpertTradingForInput + upsert?: CustomerUpsertWithoutSalesExpertTradingForInput + disconnect?: CustomerWhereInput | boolean + delete?: CustomerWhereInput | boolean + connect?: CustomerWhereUniqueInput + update?: XOR, CustomerUncheckedUpdateWithoutSalesExpertTradingForInput> + } + export type CityCreateNestedOneWithoutCustomersInput = { create?: XOR connectOrCreate?: CityCreateOrConnectWithoutCustomersInput @@ -50001,6 +53611,12 @@ export namespace Prisma { connect?: AdminUserWhereUniqueInput } + export type SalesExpertProfileCreateNestedOneWithoutTradingCustomerInput = { + create?: XOR + connectOrCreate?: SalesExpertProfileCreateOrConnectWithoutTradingCustomerInput + connect?: SalesExpertProfileWhereUniqueInput + } + export type UploadedFileCreateNestedOneWithoutCustomerAvatarsInput = { create?: XOR connectOrCreate?: UploadedFileCreateOrConnectWithoutCustomerAvatarsInput @@ -50070,6 +53686,19 @@ export namespace Prisma { connect?: CustomerActivityLogWhereUniqueInput | CustomerActivityLogWhereUniqueInput[] } + export type CustomerOtpCreateNestedManyWithoutCustomerInput = { + create?: XOR | CustomerOtpCreateWithoutCustomerInput[] | CustomerOtpUncheckedCreateWithoutCustomerInput[] + connectOrCreate?: CustomerOtpCreateOrConnectWithoutCustomerInput | CustomerOtpCreateOrConnectWithoutCustomerInput[] + createMany?: CustomerOtpCreateManyCustomerInputEnvelope + connect?: CustomerOtpWhereUniqueInput | CustomerOtpWhereUniqueInput[] + } + + export type SalesExpertProfileUncheckedCreateNestedOneWithoutTradingCustomerInput = { + create?: XOR + connectOrCreate?: SalesExpertProfileCreateOrConnectWithoutTradingCustomerInput + connect?: SalesExpertProfileWhereUniqueInput + } + export type CustomerAddressUncheckedCreateNestedManyWithoutCustomerInput = { create?: XOR | CustomerAddressCreateWithoutCustomerInput[] | CustomerAddressUncheckedCreateWithoutCustomerInput[] connectOrCreate?: CustomerAddressCreateOrConnectWithoutCustomerInput | CustomerAddressCreateOrConnectWithoutCustomerInput[] @@ -50133,16 +53762,15 @@ export namespace Prisma { connect?: CustomerActivityLogWhereUniqueInput | CustomerActivityLogWhereUniqueInput[] } - export type EnumCustomerKindFieldUpdateOperationsInput = { - set?: $Enums.CustomerKind + export type CustomerOtpUncheckedCreateNestedManyWithoutCustomerInput = { + create?: XOR | CustomerOtpCreateWithoutCustomerInput[] | CustomerOtpUncheckedCreateWithoutCustomerInput[] + connectOrCreate?: CustomerOtpCreateOrConnectWithoutCustomerInput | CustomerOtpCreateOrConnectWithoutCustomerInput[] + createMany?: CustomerOtpCreateManyCustomerInputEnvelope + connect?: CustomerOtpWhereUniqueInput | CustomerOtpWhereUniqueInput[] } - export type DecimalFieldUpdateOperationsInput = { - set?: Decimal | DecimalJsLike | number | string - increment?: Decimal | DecimalJsLike | number | string - decrement?: Decimal | DecimalJsLike | number | string - multiply?: Decimal | DecimalJsLike | number | string - divide?: Decimal | DecimalJsLike | number | string + export type EnumCustomerKindFieldUpdateOperationsInput = { + set?: $Enums.CustomerKind } export type EnumVerificationStepFieldUpdateOperationsInput = { @@ -50169,6 +53797,16 @@ export namespace Prisma { update?: XOR, AdminUserUncheckedUpdateWithoutReferredCustomersInput> } + export type SalesExpertProfileUpdateOneWithoutTradingCustomerNestedInput = { + create?: XOR + connectOrCreate?: SalesExpertProfileCreateOrConnectWithoutTradingCustomerInput + upsert?: SalesExpertProfileUpsertWithoutTradingCustomerInput + disconnect?: SalesExpertProfileWhereInput | boolean + delete?: SalesExpertProfileWhereInput | boolean + connect?: SalesExpertProfileWhereUniqueInput + update?: XOR, SalesExpertProfileUncheckedUpdateWithoutTradingCustomerInput> + } + export type UploadedFileUpdateOneWithoutCustomerAvatarsNestedInput = { create?: XOR connectOrCreate?: UploadedFileCreateOrConnectWithoutCustomerAvatarsInput @@ -50305,6 +53943,30 @@ export namespace Prisma { deleteMany?: CustomerActivityLogScalarWhereInput | CustomerActivityLogScalarWhereInput[] } + export type CustomerOtpUpdateManyWithoutCustomerNestedInput = { + create?: XOR | CustomerOtpCreateWithoutCustomerInput[] | CustomerOtpUncheckedCreateWithoutCustomerInput[] + connectOrCreate?: CustomerOtpCreateOrConnectWithoutCustomerInput | CustomerOtpCreateOrConnectWithoutCustomerInput[] + upsert?: CustomerOtpUpsertWithWhereUniqueWithoutCustomerInput | CustomerOtpUpsertWithWhereUniqueWithoutCustomerInput[] + createMany?: CustomerOtpCreateManyCustomerInputEnvelope + set?: CustomerOtpWhereUniqueInput | CustomerOtpWhereUniqueInput[] + disconnect?: CustomerOtpWhereUniqueInput | CustomerOtpWhereUniqueInput[] + delete?: CustomerOtpWhereUniqueInput | CustomerOtpWhereUniqueInput[] + connect?: CustomerOtpWhereUniqueInput | CustomerOtpWhereUniqueInput[] + update?: CustomerOtpUpdateWithWhereUniqueWithoutCustomerInput | CustomerOtpUpdateWithWhereUniqueWithoutCustomerInput[] + updateMany?: CustomerOtpUpdateManyWithWhereWithoutCustomerInput | CustomerOtpUpdateManyWithWhereWithoutCustomerInput[] + deleteMany?: CustomerOtpScalarWhereInput | CustomerOtpScalarWhereInput[] + } + + export type SalesExpertProfileUncheckedUpdateOneWithoutTradingCustomerNestedInput = { + create?: XOR + connectOrCreate?: SalesExpertProfileCreateOrConnectWithoutTradingCustomerInput + upsert?: SalesExpertProfileUpsertWithoutTradingCustomerInput + disconnect?: SalesExpertProfileWhereInput | boolean + delete?: SalesExpertProfileWhereInput | boolean + connect?: SalesExpertProfileWhereUniqueInput + update?: XOR, SalesExpertProfileUncheckedUpdateWithoutTradingCustomerInput> + } + export type CustomerAddressUncheckedUpdateManyWithoutCustomerNestedInput = { create?: XOR | CustomerAddressCreateWithoutCustomerInput[] | CustomerAddressUncheckedCreateWithoutCustomerInput[] connectOrCreate?: CustomerAddressCreateOrConnectWithoutCustomerInput | CustomerAddressCreateOrConnectWithoutCustomerInput[] @@ -50431,6 +54093,20 @@ export namespace Prisma { deleteMany?: CustomerActivityLogScalarWhereInput | CustomerActivityLogScalarWhereInput[] } + export type CustomerOtpUncheckedUpdateManyWithoutCustomerNestedInput = { + create?: XOR | CustomerOtpCreateWithoutCustomerInput[] | CustomerOtpUncheckedCreateWithoutCustomerInput[] + connectOrCreate?: CustomerOtpCreateOrConnectWithoutCustomerInput | CustomerOtpCreateOrConnectWithoutCustomerInput[] + upsert?: CustomerOtpUpsertWithWhereUniqueWithoutCustomerInput | CustomerOtpUpsertWithWhereUniqueWithoutCustomerInput[] + createMany?: CustomerOtpCreateManyCustomerInputEnvelope + set?: CustomerOtpWhereUniqueInput | CustomerOtpWhereUniqueInput[] + disconnect?: CustomerOtpWhereUniqueInput | CustomerOtpWhereUniqueInput[] + delete?: CustomerOtpWhereUniqueInput | CustomerOtpWhereUniqueInput[] + connect?: CustomerOtpWhereUniqueInput | CustomerOtpWhereUniqueInput[] + update?: CustomerOtpUpdateWithWhereUniqueWithoutCustomerInput | CustomerOtpUpdateWithWhereUniqueWithoutCustomerInput[] + updateMany?: CustomerOtpUpdateManyWithWhereWithoutCustomerInput | CustomerOtpUpdateManyWithWhereWithoutCustomerInput[] + deleteMany?: CustomerOtpScalarWhereInput | CustomerOtpScalarWhereInput[] + } + export type CustomerCreateNestedOneWithoutAddressesInput = { create?: XOR connectOrCreate?: CustomerCreateOrConnectWithoutAddressesInput @@ -51255,6 +54931,20 @@ export namespace Prisma { connect?: ProductWhereUniqueInput } + export type ProductVariantCreateNestedManyWithoutImageInput = { + create?: XOR | ProductVariantCreateWithoutImageInput[] | ProductVariantUncheckedCreateWithoutImageInput[] + connectOrCreate?: ProductVariantCreateOrConnectWithoutImageInput | ProductVariantCreateOrConnectWithoutImageInput[] + createMany?: ProductVariantCreateManyImageInputEnvelope + connect?: ProductVariantWhereUniqueInput | ProductVariantWhereUniqueInput[] + } + + export type ProductVariantUncheckedCreateNestedManyWithoutImageInput = { + create?: XOR | ProductVariantCreateWithoutImageInput[] | ProductVariantUncheckedCreateWithoutImageInput[] + connectOrCreate?: ProductVariantCreateOrConnectWithoutImageInput | ProductVariantCreateOrConnectWithoutImageInput[] + createMany?: ProductVariantCreateManyImageInputEnvelope + connect?: ProductVariantWhereUniqueInput | ProductVariantWhereUniqueInput[] + } + export type EnumProductImageRoleFieldUpdateOperationsInput = { set?: $Enums.ProductImageRole } @@ -51267,12 +54957,46 @@ export namespace Prisma { update?: XOR, ProductUncheckedUpdateWithoutImagesInput> } + export type ProductVariantUpdateManyWithoutImageNestedInput = { + create?: XOR | ProductVariantCreateWithoutImageInput[] | ProductVariantUncheckedCreateWithoutImageInput[] + connectOrCreate?: ProductVariantCreateOrConnectWithoutImageInput | ProductVariantCreateOrConnectWithoutImageInput[] + upsert?: ProductVariantUpsertWithWhereUniqueWithoutImageInput | ProductVariantUpsertWithWhereUniqueWithoutImageInput[] + createMany?: ProductVariantCreateManyImageInputEnvelope + set?: ProductVariantWhereUniqueInput | ProductVariantWhereUniqueInput[] + disconnect?: ProductVariantWhereUniqueInput | ProductVariantWhereUniqueInput[] + delete?: ProductVariantWhereUniqueInput | ProductVariantWhereUniqueInput[] + connect?: ProductVariantWhereUniqueInput | ProductVariantWhereUniqueInput[] + update?: ProductVariantUpdateWithWhereUniqueWithoutImageInput | ProductVariantUpdateWithWhereUniqueWithoutImageInput[] + updateMany?: ProductVariantUpdateManyWithWhereWithoutImageInput | ProductVariantUpdateManyWithWhereWithoutImageInput[] + deleteMany?: ProductVariantScalarWhereInput | ProductVariantScalarWhereInput[] + } + + export type ProductVariantUncheckedUpdateManyWithoutImageNestedInput = { + create?: XOR | ProductVariantCreateWithoutImageInput[] | ProductVariantUncheckedCreateWithoutImageInput[] + connectOrCreate?: ProductVariantCreateOrConnectWithoutImageInput | ProductVariantCreateOrConnectWithoutImageInput[] + upsert?: ProductVariantUpsertWithWhereUniqueWithoutImageInput | ProductVariantUpsertWithWhereUniqueWithoutImageInput[] + createMany?: ProductVariantCreateManyImageInputEnvelope + set?: ProductVariantWhereUniqueInput | ProductVariantWhereUniqueInput[] + disconnect?: ProductVariantWhereUniqueInput | ProductVariantWhereUniqueInput[] + delete?: ProductVariantWhereUniqueInput | ProductVariantWhereUniqueInput[] + connect?: ProductVariantWhereUniqueInput | ProductVariantWhereUniqueInput[] + update?: ProductVariantUpdateWithWhereUniqueWithoutImageInput | ProductVariantUpdateWithWhereUniqueWithoutImageInput[] + updateMany?: ProductVariantUpdateManyWithWhereWithoutImageInput | ProductVariantUpdateManyWithWhereWithoutImageInput[] + deleteMany?: ProductVariantScalarWhereInput | ProductVariantScalarWhereInput[] + } + export type ProductCreateNestedOneWithoutVariantsInput = { create?: XOR connectOrCreate?: ProductCreateOrConnectWithoutVariantsInput connect?: ProductWhereUniqueInput } + export type ProductImageCreateNestedOneWithoutVariantsInput = { + create?: XOR + connectOrCreate?: ProductImageCreateOrConnectWithoutVariantsInput + connect?: ProductImageWhereUniqueInput + } + export type OrderItemCreateNestedManyWithoutProductVariantInput = { create?: XOR | OrderItemCreateWithoutProductVariantInput[] | OrderItemUncheckedCreateWithoutProductVariantInput[] connectOrCreate?: OrderItemCreateOrConnectWithoutProductVariantInput | OrderItemCreateOrConnectWithoutProductVariantInput[] @@ -51295,6 +55019,16 @@ export namespace Prisma { update?: XOR, ProductUncheckedUpdateWithoutVariantsInput> } + export type ProductImageUpdateOneWithoutVariantsNestedInput = { + create?: XOR + connectOrCreate?: ProductImageCreateOrConnectWithoutVariantsInput + upsert?: ProductImageUpsertWithoutVariantsInput + disconnect?: ProductImageWhereInput | boolean + delete?: ProductImageWhereInput | boolean + connect?: ProductImageWhereUniqueInput + update?: XOR, ProductImageUncheckedUpdateWithoutVariantsInput> + } + export type OrderItemUpdateManyWithoutProductVariantNestedInput = { create?: XOR | OrderItemCreateWithoutProductVariantInput[] | OrderItemUncheckedCreateWithoutProductVariantInput[] connectOrCreate?: OrderItemCreateOrConnectWithoutProductVariantInput | OrderItemCreateOrConnectWithoutProductVariantInput[] @@ -52288,6 +56022,13 @@ export namespace Prisma { connect?: CustomerWhereUniqueInput | CustomerWhereUniqueInput[] } + export type AdminUserCreateNestedManyWithoutAvatarFileInput = { + create?: XOR | AdminUserCreateWithoutAvatarFileInput[] | AdminUserUncheckedCreateWithoutAvatarFileInput[] + connectOrCreate?: AdminUserCreateOrConnectWithoutAvatarFileInput | AdminUserCreateOrConnectWithoutAvatarFileInput[] + createMany?: AdminUserCreateManyAvatarFileInputEnvelope + connect?: AdminUserWhereUniqueInput | AdminUserWhereUniqueInput[] + } + export type CustomerUncheckedCreateNestedManyWithoutAvatarFileInput = { create?: XOR | CustomerCreateWithoutAvatarFileInput[] | CustomerUncheckedCreateWithoutAvatarFileInput[] connectOrCreate?: CustomerCreateOrConnectWithoutAvatarFileInput | CustomerCreateOrConnectWithoutAvatarFileInput[] @@ -52295,6 +56036,13 @@ export namespace Prisma { connect?: CustomerWhereUniqueInput | CustomerWhereUniqueInput[] } + export type AdminUserUncheckedCreateNestedManyWithoutAvatarFileInput = { + create?: XOR | AdminUserCreateWithoutAvatarFileInput[] | AdminUserUncheckedCreateWithoutAvatarFileInput[] + connectOrCreate?: AdminUserCreateOrConnectWithoutAvatarFileInput | AdminUserCreateOrConnectWithoutAvatarFileInput[] + createMany?: AdminUserCreateManyAvatarFileInputEnvelope + connect?: AdminUserWhereUniqueInput | AdminUserWhereUniqueInput[] + } + export type AdminUserUpdateOneWithoutUploadedFilesNestedInput = { create?: XOR connectOrCreate?: AdminUserCreateOrConnectWithoutUploadedFilesInput @@ -52319,6 +56067,20 @@ export namespace Prisma { deleteMany?: CustomerScalarWhereInput | CustomerScalarWhereInput[] } + export type AdminUserUpdateManyWithoutAvatarFileNestedInput = { + create?: XOR | AdminUserCreateWithoutAvatarFileInput[] | AdminUserUncheckedCreateWithoutAvatarFileInput[] + connectOrCreate?: AdminUserCreateOrConnectWithoutAvatarFileInput | AdminUserCreateOrConnectWithoutAvatarFileInput[] + upsert?: AdminUserUpsertWithWhereUniqueWithoutAvatarFileInput | AdminUserUpsertWithWhereUniqueWithoutAvatarFileInput[] + createMany?: AdminUserCreateManyAvatarFileInputEnvelope + set?: AdminUserWhereUniqueInput | AdminUserWhereUniqueInput[] + disconnect?: AdminUserWhereUniqueInput | AdminUserWhereUniqueInput[] + delete?: AdminUserWhereUniqueInput | AdminUserWhereUniqueInput[] + connect?: AdminUserWhereUniqueInput | AdminUserWhereUniqueInput[] + update?: AdminUserUpdateWithWhereUniqueWithoutAvatarFileInput | AdminUserUpdateWithWhereUniqueWithoutAvatarFileInput[] + updateMany?: AdminUserUpdateManyWithWhereWithoutAvatarFileInput | AdminUserUpdateManyWithWhereWithoutAvatarFileInput[] + deleteMany?: AdminUserScalarWhereInput | AdminUserScalarWhereInput[] + } + export type CustomerUncheckedUpdateManyWithoutAvatarFileNestedInput = { create?: XOR | CustomerCreateWithoutAvatarFileInput[] | CustomerUncheckedCreateWithoutAvatarFileInput[] connectOrCreate?: CustomerCreateOrConnectWithoutAvatarFileInput | CustomerCreateOrConnectWithoutAvatarFileInput[] @@ -52333,6 +56095,34 @@ export namespace Prisma { deleteMany?: CustomerScalarWhereInput | CustomerScalarWhereInput[] } + export type AdminUserUncheckedUpdateManyWithoutAvatarFileNestedInput = { + create?: XOR | AdminUserCreateWithoutAvatarFileInput[] | AdminUserUncheckedCreateWithoutAvatarFileInput[] + connectOrCreate?: AdminUserCreateOrConnectWithoutAvatarFileInput | AdminUserCreateOrConnectWithoutAvatarFileInput[] + upsert?: AdminUserUpsertWithWhereUniqueWithoutAvatarFileInput | AdminUserUpsertWithWhereUniqueWithoutAvatarFileInput[] + createMany?: AdminUserCreateManyAvatarFileInputEnvelope + set?: AdminUserWhereUniqueInput | AdminUserWhereUniqueInput[] + disconnect?: AdminUserWhereUniqueInput | AdminUserWhereUniqueInput[] + delete?: AdminUserWhereUniqueInput | AdminUserWhereUniqueInput[] + connect?: AdminUserWhereUniqueInput | AdminUserWhereUniqueInput[] + update?: AdminUserUpdateWithWhereUniqueWithoutAvatarFileInput | AdminUserUpdateWithWhereUniqueWithoutAvatarFileInput[] + updateMany?: AdminUserUpdateManyWithWhereWithoutAvatarFileInput | AdminUserUpdateManyWithWhereWithoutAvatarFileInput[] + deleteMany?: AdminUserScalarWhereInput | AdminUserScalarWhereInput[] + } + + export type CustomerCreateNestedOneWithoutOtpChallengesInput = { + create?: XOR + connectOrCreate?: CustomerCreateOrConnectWithoutOtpChallengesInput + connect?: CustomerWhereUniqueInput + } + + export type CustomerUpdateOneRequiredWithoutOtpChallengesNestedInput = { + create?: XOR + connectOrCreate?: CustomerCreateOrConnectWithoutOtpChallengesInput + upsert?: CustomerUpsertWithoutOtpChallengesInput + connect?: CustomerWhereUniqueInput + update?: XOR, CustomerUncheckedUpdateWithoutOtpChallengesInput> + } + export type NestedStringFilter<$PrismaModel = never> = { equals?: string | StringFieldRefInput<$PrismaModel> in?: string[] | ListStringFieldRefInput<$PrismaModel> @@ -52526,13 +56316,6 @@ export namespace Prisma { _max?: NestedDateTimeFilter<$PrismaModel> } - export type NestedEnumCustomerKindFilter<$PrismaModel = never> = { - equals?: $Enums.CustomerKind | EnumCustomerKindFieldRefInput<$PrismaModel> - in?: $Enums.CustomerKind[] | ListEnumCustomerKindFieldRefInput<$PrismaModel> - notIn?: $Enums.CustomerKind[] | ListEnumCustomerKindFieldRefInput<$PrismaModel> - not?: NestedEnumCustomerKindFilter<$PrismaModel> | $Enums.CustomerKind - } - export type NestedDecimalFilter<$PrismaModel = never> = { equals?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> in?: Decimal[] | DecimalJsLike[] | number[] | string[] | ListDecimalFieldRefInput<$PrismaModel> @@ -52544,6 +56327,29 @@ export namespace Prisma { not?: NestedDecimalFilter<$PrismaModel> | Decimal | DecimalJsLike | number | string } + export type NestedDecimalWithAggregatesFilter<$PrismaModel = never> = { + equals?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> + in?: Decimal[] | DecimalJsLike[] | number[] | string[] | ListDecimalFieldRefInput<$PrismaModel> + notIn?: Decimal[] | DecimalJsLike[] | number[] | string[] | ListDecimalFieldRefInput<$PrismaModel> + lt?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> + lte?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> + gt?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> + gte?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> + not?: NestedDecimalWithAggregatesFilter<$PrismaModel> | Decimal | DecimalJsLike | number | string + _count?: NestedIntFilter<$PrismaModel> + _avg?: NestedDecimalFilter<$PrismaModel> + _sum?: NestedDecimalFilter<$PrismaModel> + _min?: NestedDecimalFilter<$PrismaModel> + _max?: NestedDecimalFilter<$PrismaModel> + } + + export type NestedEnumCustomerKindFilter<$PrismaModel = never> = { + equals?: $Enums.CustomerKind | EnumCustomerKindFieldRefInput<$PrismaModel> + in?: $Enums.CustomerKind[] | ListEnumCustomerKindFieldRefInput<$PrismaModel> + notIn?: $Enums.CustomerKind[] | ListEnumCustomerKindFieldRefInput<$PrismaModel> + not?: NestedEnumCustomerKindFilter<$PrismaModel> | $Enums.CustomerKind + } + export type NestedEnumVerificationStepFilter<$PrismaModel = never> = { equals?: $Enums.VerificationStep | EnumVerificationStepFieldRefInput<$PrismaModel> in?: $Enums.VerificationStep[] | ListEnumVerificationStepFieldRefInput<$PrismaModel> @@ -52561,22 +56367,6 @@ export namespace Prisma { _max?: NestedEnumCustomerKindFilter<$PrismaModel> } - export type NestedDecimalWithAggregatesFilter<$PrismaModel = never> = { - equals?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> - in?: Decimal[] | DecimalJsLike[] | number[] | string[] | ListDecimalFieldRefInput<$PrismaModel> - notIn?: Decimal[] | DecimalJsLike[] | number[] | string[] | ListDecimalFieldRefInput<$PrismaModel> - lt?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> - lte?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> - gt?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> - gte?: Decimal | DecimalJsLike | number | string | DecimalFieldRefInput<$PrismaModel> - not?: NestedDecimalWithAggregatesFilter<$PrismaModel> | Decimal | DecimalJsLike | number | string - _count?: NestedIntFilter<$PrismaModel> - _avg?: NestedDecimalFilter<$PrismaModel> - _sum?: NestedDecimalFilter<$PrismaModel> - _min?: NestedDecimalFilter<$PrismaModel> - _max?: NestedDecimalFilter<$PrismaModel> - } - export type NestedEnumVerificationStepWithAggregatesFilter<$PrismaModel = never> = { equals?: $Enums.VerificationStep | EnumVerificationStepFieldRefInput<$PrismaModel> in?: $Enums.VerificationStep[] | ListEnumVerificationStepFieldRefInput<$PrismaModel> @@ -52868,6 +56658,8 @@ export namespace Prisma { export type CustomerCreateWithoutCityInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -52881,6 +56673,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -52891,6 +56684,7 @@ export namespace Prisma { createdAt?: Date | string updatedAt?: Date | string referredByAgent?: AdminUserCreateNestedOneWithoutReferredCustomersInput + salesExpertTradingFor?: SalesExpertProfileCreateNestedOneWithoutTradingCustomerInput avatarFile?: UploadedFileCreateNestedOneWithoutCustomerAvatarsInput addresses?: CustomerAddressCreateNestedManyWithoutCustomerInput orders?: OrderCreateNestedManyWithoutCustomerInput @@ -52901,11 +56695,14 @@ export namespace Prisma { promotions?: PromotionCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpCreateNestedManyWithoutCustomerInput } export type CustomerUncheckedCreateWithoutCityInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -52920,6 +56717,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -52930,6 +56728,7 @@ export namespace Prisma { isArchived?: boolean createdAt?: Date | string updatedAt?: Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedCreateNestedOneWithoutTradingCustomerInput addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCustomerInput orders?: OrderUncheckedCreateNestedManyWithoutCustomerInput preOrders?: PreOrderUncheckedCreateNestedManyWithoutCustomerInput @@ -52939,6 +56738,7 @@ export namespace Prisma { promotions?: PromotionUncheckedCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientUncheckedCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogUncheckedCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpUncheckedCreateNestedManyWithoutCustomerInput } export type CustomerCreateOrConnectWithoutCityInput = { @@ -53055,6 +56855,38 @@ export namespace Prisma { skipDuplicates?: boolean } + export type SalesExpertProfileCreateWithoutCityInput = { + id?: string + gramLevel?: Decimal | DecimalJsLike | number | string + phone?: string | null + nationalCode?: string | null + createdAt?: Date | string + updatedAt?: Date | string + adminUser: AdminUserCreateNestedOneWithoutSalesExpertProfileInput + tradingCustomer?: CustomerCreateNestedOneWithoutSalesExpertTradingForInput + } + + export type SalesExpertProfileUncheckedCreateWithoutCityInput = { + id?: string + adminUserId: string + gramLevel?: Decimal | DecimalJsLike | number | string + phone?: string | null + nationalCode?: string | null + tradingCustomerId?: string | null + createdAt?: Date | string + updatedAt?: Date | string + } + + export type SalesExpertProfileCreateOrConnectWithoutCityInput = { + where: SalesExpertProfileWhereUniqueInput + create: XOR + } + + export type SalesExpertProfileCreateManyCityInputEnvelope = { + data: SalesExpertProfileCreateManyCityInput | SalesExpertProfileCreateManyCityInput[] + skipDuplicates?: boolean + } + export type CustomerUpsertWithWhereUniqueWithoutCityInput = { where: CustomerWhereUniqueInput update: XOR @@ -53077,6 +56909,8 @@ export namespace Prisma { NOT?: CustomerScalarWhereInput | CustomerScalarWhereInput[] id?: StringFilter<"Customer"> | string fullName?: StringFilter<"Customer"> | string + firstName?: StringNullableFilter<"Customer"> | string | null + lastName?: StringNullableFilter<"Customer"> | string | null mobile?: StringFilter<"Customer"> | string nationalCode?: StringFilter<"Customer"> | string passwordHash?: StringFilter<"Customer"> | string @@ -53092,6 +56926,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFilter<"Customer"> | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFilter<"Customer"> | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFilter<"Customer"> | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFilter<"Customer"> | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFilter<"Customer"> | $Enums.VerificationStep nationalIdImageUrl?: StringNullableFilter<"Customer"> | string | null businessLicenseUrl?: StringNullableFilter<"Customer"> | string | null @@ -53197,6 +57032,37 @@ export namespace Prisma { updatedAt?: DateTimeFilter<"PreOrder"> | Date | string } + export type SalesExpertProfileUpsertWithWhereUniqueWithoutCityInput = { + where: SalesExpertProfileWhereUniqueInput + update: XOR + create: XOR + } + + export type SalesExpertProfileUpdateWithWhereUniqueWithoutCityInput = { + where: SalesExpertProfileWhereUniqueInput + data: XOR + } + + export type SalesExpertProfileUpdateManyWithWhereWithoutCityInput = { + where: SalesExpertProfileScalarWhereInput + data: XOR + } + + export type SalesExpertProfileScalarWhereInput = { + AND?: SalesExpertProfileScalarWhereInput | SalesExpertProfileScalarWhereInput[] + OR?: SalesExpertProfileScalarWhereInput[] + NOT?: SalesExpertProfileScalarWhereInput | SalesExpertProfileScalarWhereInput[] + id?: StringFilter<"SalesExpertProfile"> | string + adminUserId?: StringFilter<"SalesExpertProfile"> | string + cityId?: StringNullableFilter<"SalesExpertProfile"> | string | null + gramLevel?: DecimalFilter<"SalesExpertProfile"> | Decimal | DecimalJsLike | number | string + phone?: StringNullableFilter<"SalesExpertProfile"> | string | null + nationalCode?: StringNullableFilter<"SalesExpertProfile"> | string | null + tradingCustomerId?: StringNullableFilter<"SalesExpertProfile"> | string | null + createdAt?: DateTimeFilter<"SalesExpertProfile"> | Date | string + updatedAt?: DateTimeFilter<"SalesExpertProfile"> | Date | string + } + export type UserTypeCreateWithoutAdminUsersInput = { id: number title: string @@ -53245,9 +57111,40 @@ export namespace Prisma { create: XOR } + export type UploadedFileCreateWithoutAdminUserAvatarsInput = { + id?: string + url: string + originalName: string + mimeType: string + sizeBytes: number + purpose?: string | null + createdAt?: Date | string + uploadedByAdminUser?: AdminUserCreateNestedOneWithoutUploadedFilesInput + customerAvatars?: CustomerCreateNestedManyWithoutAvatarFileInput + } + + export type UploadedFileUncheckedCreateWithoutAdminUserAvatarsInput = { + id?: string + url: string + originalName: string + mimeType: string + sizeBytes: number + purpose?: string | null + uploadedByAdminUserId?: string | null + createdAt?: Date | string + customerAvatars?: CustomerUncheckedCreateNestedManyWithoutAvatarFileInput + } + + export type UploadedFileCreateOrConnectWithoutAdminUserAvatarsInput = { + where: UploadedFileWhereUniqueInput + create: XOR + } + export type CustomerCreateWithoutReferredByAgentInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -53261,6 +57158,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -53271,6 +57169,7 @@ export namespace Prisma { createdAt?: Date | string updatedAt?: Date | string city?: CityCreateNestedOneWithoutCustomersInput + salesExpertTradingFor?: SalesExpertProfileCreateNestedOneWithoutTradingCustomerInput avatarFile?: UploadedFileCreateNestedOneWithoutCustomerAvatarsInput addresses?: CustomerAddressCreateNestedManyWithoutCustomerInput orders?: OrderCreateNestedManyWithoutCustomerInput @@ -53281,11 +57180,14 @@ export namespace Prisma { promotions?: PromotionCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpCreateNestedManyWithoutCustomerInput } export type CustomerUncheckedCreateWithoutReferredByAgentInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -53300,6 +57202,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -53310,6 +57213,7 @@ export namespace Prisma { isArchived?: boolean createdAt?: Date | string updatedAt?: Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedCreateNestedOneWithoutTradingCustomerInput addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCustomerInput orders?: OrderUncheckedCreateNestedManyWithoutCustomerInput preOrders?: PreOrderUncheckedCreateNestedManyWithoutCustomerInput @@ -53319,6 +57223,7 @@ export namespace Prisma { promotions?: PromotionUncheckedCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientUncheckedCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogUncheckedCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpUncheckedCreateNestedManyWithoutCustomerInput } export type CustomerCreateOrConnectWithoutReferredByAgentInput = { @@ -53562,6 +57467,7 @@ export namespace Prisma { purpose?: string | null createdAt?: Date | string customerAvatars?: CustomerCreateNestedManyWithoutAvatarFileInput + adminUserAvatars?: AdminUserCreateNestedManyWithoutAvatarFileInput } export type UploadedFileUncheckedCreateWithoutUploadedByAdminUserInput = { @@ -53573,6 +57479,7 @@ export namespace Prisma { purpose?: string | null createdAt?: Date | string customerAvatars?: CustomerUncheckedCreateNestedManyWithoutAvatarFileInput + adminUserAvatars?: AdminUserUncheckedCreateNestedManyWithoutAvatarFileInput } export type UploadedFileCreateOrConnectWithoutUploadedByAdminUserInput = { @@ -53585,6 +57492,33 @@ export namespace Prisma { skipDuplicates?: boolean } + export type SalesExpertProfileCreateWithoutAdminUserInput = { + id?: string + gramLevel?: Decimal | DecimalJsLike | number | string + phone?: string | null + nationalCode?: string | null + createdAt?: Date | string + updatedAt?: Date | string + city?: CityCreateNestedOneWithoutSalesExpertProfilesInput + tradingCustomer?: CustomerCreateNestedOneWithoutSalesExpertTradingForInput + } + + export type SalesExpertProfileUncheckedCreateWithoutAdminUserInput = { + id?: string + cityId?: string | null + gramLevel?: Decimal | DecimalJsLike | number | string + phone?: string | null + nationalCode?: string | null + tradingCustomerId?: string | null + createdAt?: Date | string + updatedAt?: Date | string + } + + export type SalesExpertProfileCreateOrConnectWithoutAdminUserInput = { + where: SalesExpertProfileWhereUniqueInput + create: XOR + } + export type UserTypeUpsertWithoutAdminUsersInput = { update: XOR create: XOR @@ -53645,6 +57579,41 @@ export namespace Prisma { allocations?: BranchAllocationUncheckedUpdateManyWithoutBranchNestedInput } + export type UploadedFileUpsertWithoutAdminUserAvatarsInput = { + update: XOR + create: XOR + where?: UploadedFileWhereInput + } + + export type UploadedFileUpdateToOneWithWhereWithoutAdminUserAvatarsInput = { + where?: UploadedFileWhereInput + data: XOR + } + + export type UploadedFileUpdateWithoutAdminUserAvatarsInput = { + id?: StringFieldUpdateOperationsInput | string + url?: StringFieldUpdateOperationsInput | string + originalName?: StringFieldUpdateOperationsInput | string + mimeType?: StringFieldUpdateOperationsInput | string + sizeBytes?: IntFieldUpdateOperationsInput | number + purpose?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + uploadedByAdminUser?: AdminUserUpdateOneWithoutUploadedFilesNestedInput + customerAvatars?: CustomerUpdateManyWithoutAvatarFileNestedInput + } + + export type UploadedFileUncheckedUpdateWithoutAdminUserAvatarsInput = { + id?: StringFieldUpdateOperationsInput | string + url?: StringFieldUpdateOperationsInput | string + originalName?: StringFieldUpdateOperationsInput | string + mimeType?: StringFieldUpdateOperationsInput | string + sizeBytes?: IntFieldUpdateOperationsInput | number + purpose?: NullableStringFieldUpdateOperationsInput | string | null + uploadedByAdminUserId?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + customerAvatars?: CustomerUncheckedUpdateManyWithoutAvatarFileNestedInput + } + export type CustomerUpsertWithWhereUniqueWithoutReferredByAgentInput = { where: CustomerWhereUniqueInput update: XOR @@ -53904,6 +57873,423 @@ export namespace Prisma { createdAt?: DateTimeFilter<"UploadedFile"> | Date | string } + export type SalesExpertProfileUpsertWithoutAdminUserInput = { + update: XOR + create: XOR + where?: SalesExpertProfileWhereInput + } + + export type SalesExpertProfileUpdateToOneWithWhereWithoutAdminUserInput = { + where?: SalesExpertProfileWhereInput + data: XOR + } + + export type SalesExpertProfileUpdateWithoutAdminUserInput = { + id?: StringFieldUpdateOperationsInput | string + gramLevel?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + phone?: NullableStringFieldUpdateOperationsInput | string | null + nationalCode?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + city?: CityUpdateOneWithoutSalesExpertProfilesNestedInput + tradingCustomer?: CustomerUpdateOneWithoutSalesExpertTradingForNestedInput + } + + export type SalesExpertProfileUncheckedUpdateWithoutAdminUserInput = { + id?: StringFieldUpdateOperationsInput | string + cityId?: NullableStringFieldUpdateOperationsInput | string | null + gramLevel?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + phone?: NullableStringFieldUpdateOperationsInput | string | null + nationalCode?: NullableStringFieldUpdateOperationsInput | string | null + tradingCustomerId?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + } + + export type AdminUserCreateWithoutSalesExpertProfileInput = { + id?: string + fullName: string + firstName?: string | null + lastName?: string | null + mobile: string + passwordHash: string + role: $Enums.AdminRole + wagePercent?: Decimal | DecimalJsLike | number | string | null + isActive?: boolean + createdAt?: Date | string + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null + userType?: UserTypeCreateNestedOneWithoutAdminUsersInput + branch?: BranchCreateNestedOneWithoutAdminUsersInput + avatarFile?: UploadedFileCreateNestedOneWithoutAdminUserAvatarsInput + referredCustomers?: CustomerCreateNestedManyWithoutReferredByAgentInput + supervisedWorkshops?: WorkshopCreateNestedManyWithoutSupervisorInput + branchAllocations?: BranchAllocationCreateNestedManyWithoutAllocatorAdminUserInput + postedLedgerEntries?: LedgerEntryCreateNestedManyWithoutPostedByAdminUserInput + orderStatusEvents?: OrderStatusEventCreateNestedManyWithoutActorAdminUserInput + preOrderStatusEvents?: PreOrderStatusEventCreateNestedManyWithoutActorAdminUserInput + sentSmsMessages?: SmsMessageCreateNestedManyWithoutSentByAdminUserInput + sentNotifications?: NotificationCreateNestedManyWithoutSentByAdminUserInput + uploadedFiles?: UploadedFileCreateNestedManyWithoutUploadedByAdminUserInput + } + + export type AdminUserUncheckedCreateWithoutSalesExpertProfileInput = { + id?: string + fullName: string + firstName?: string | null + lastName?: string | null + mobile: string + passwordHash: string + role: $Enums.AdminRole + userTypeId?: number | null + branchId?: string | null + wagePercent?: Decimal | DecimalJsLike | number | string | null + isActive?: boolean + createdAt?: Date | string + avatarFileId?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null + referredCustomers?: CustomerUncheckedCreateNestedManyWithoutReferredByAgentInput + supervisedWorkshops?: WorkshopUncheckedCreateNestedManyWithoutSupervisorInput + branchAllocations?: BranchAllocationUncheckedCreateNestedManyWithoutAllocatorAdminUserInput + postedLedgerEntries?: LedgerEntryUncheckedCreateNestedManyWithoutPostedByAdminUserInput + orderStatusEvents?: OrderStatusEventUncheckedCreateNestedManyWithoutActorAdminUserInput + preOrderStatusEvents?: PreOrderStatusEventUncheckedCreateNestedManyWithoutActorAdminUserInput + sentSmsMessages?: SmsMessageUncheckedCreateNestedManyWithoutSentByAdminUserInput + sentNotifications?: NotificationUncheckedCreateNestedManyWithoutSentByAdminUserInput + uploadedFiles?: UploadedFileUncheckedCreateNestedManyWithoutUploadedByAdminUserInput + } + + export type AdminUserCreateOrConnectWithoutSalesExpertProfileInput = { + where: AdminUserWhereUniqueInput + create: XOR + } + + export type CityCreateWithoutSalesExpertProfilesInput = { + id?: string + name: string + province?: string | null + percent?: Decimal | DecimalJsLike | number | string | null + percentAux?: Decimal | DecimalJsLike | number | string | null + customers?: CustomerCreateNestedManyWithoutCityInput + addresses?: CustomerAddressCreateNestedManyWithoutCityInput + branches?: BranchCreateNestedManyWithoutCityInput + preOrders?: PreOrderCreateNestedManyWithoutCityInput + } + + export type CityUncheckedCreateWithoutSalesExpertProfilesInput = { + id?: string + name: string + province?: string | null + percent?: Decimal | DecimalJsLike | number | string | null + percentAux?: Decimal | DecimalJsLike | number | string | null + customers?: CustomerUncheckedCreateNestedManyWithoutCityInput + addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCityInput + branches?: BranchUncheckedCreateNestedManyWithoutCityInput + preOrders?: PreOrderUncheckedCreateNestedManyWithoutCityInput + } + + export type CityCreateOrConnectWithoutSalesExpertProfilesInput = { + where: CityWhereUniqueInput + create: XOR + } + + export type CustomerCreateWithoutSalesExpertTradingForInput = { + id?: string + fullName: string + firstName?: string | null + lastName?: string | null + mobile: string + nationalCode: string + passwordHash: string + kind?: $Enums.CustomerKind + phone?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + zone?: string | null + memberCode?: string | null + goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string + goldDebtGrams?: Decimal | DecimalJsLike | number | string + fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string + verificationStep?: $Enums.VerificationStep + nationalIdImageUrl?: string | null + businessLicenseUrl?: string | null + shopImageUrl?: string | null + canPlaceOrders?: boolean + isActive?: boolean + isArchived?: boolean + createdAt?: Date | string + updatedAt?: Date | string + city?: CityCreateNestedOneWithoutCustomersInput + referredByAgent?: AdminUserCreateNestedOneWithoutReferredCustomersInput + avatarFile?: UploadedFileCreateNestedOneWithoutCustomerAvatarsInput + addresses?: CustomerAddressCreateNestedManyWithoutCustomerInput + orders?: OrderCreateNestedManyWithoutCustomerInput + preOrders?: PreOrderCreateNestedManyWithoutCustomerInput + ledgerEntries?: LedgerEntryCreateNestedManyWithoutCustomerInput + walletBonuses?: WalletBonusCreateNestedManyWithoutCustomerInput + comments?: CommentCreateNestedManyWithoutCustomerInput + promotions?: PromotionCreateNestedManyWithoutCustomerInput + notifications?: NotificationRecipientCreateNestedManyWithoutCustomerInput + activityLogs?: CustomerActivityLogCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpCreateNestedManyWithoutCustomerInput + } + + export type CustomerUncheckedCreateWithoutSalesExpertTradingForInput = { + id?: string + fullName: string + firstName?: string | null + lastName?: string | null + mobile: string + nationalCode: string + passwordHash: string + cityId?: string | null + kind?: $Enums.CustomerKind + phone?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + zone?: string | null + memberCode?: string | null + referredByAgentId?: string | null + goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string + goldDebtGrams?: Decimal | DecimalJsLike | number | string + fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string + verificationStep?: $Enums.VerificationStep + nationalIdImageUrl?: string | null + businessLicenseUrl?: string | null + shopImageUrl?: string | null + avatarFileId?: string | null + canPlaceOrders?: boolean + isActive?: boolean + isArchived?: boolean + createdAt?: Date | string + updatedAt?: Date | string + addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCustomerInput + orders?: OrderUncheckedCreateNestedManyWithoutCustomerInput + preOrders?: PreOrderUncheckedCreateNestedManyWithoutCustomerInput + ledgerEntries?: LedgerEntryUncheckedCreateNestedManyWithoutCustomerInput + walletBonuses?: WalletBonusUncheckedCreateNestedManyWithoutCustomerInput + comments?: CommentUncheckedCreateNestedManyWithoutCustomerInput + promotions?: PromotionUncheckedCreateNestedManyWithoutCustomerInput + notifications?: NotificationRecipientUncheckedCreateNestedManyWithoutCustomerInput + activityLogs?: CustomerActivityLogUncheckedCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpUncheckedCreateNestedManyWithoutCustomerInput + } + + export type CustomerCreateOrConnectWithoutSalesExpertTradingForInput = { + where: CustomerWhereUniqueInput + create: XOR + } + + export type AdminUserUpsertWithoutSalesExpertProfileInput = { + update: XOR + create: XOR + where?: AdminUserWhereInput + } + + export type AdminUserUpdateToOneWithWhereWithoutSalesExpertProfileInput = { + where?: AdminUserWhereInput + data: XOR + } + + export type AdminUserUpdateWithoutSalesExpertProfileInput = { + id?: StringFieldUpdateOperationsInput | string + fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null + mobile?: StringFieldUpdateOperationsInput | string + passwordHash?: StringFieldUpdateOperationsInput | string + role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole + wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null + isActive?: BoolFieldUpdateOperationsInput | boolean + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null + userType?: UserTypeUpdateOneWithoutAdminUsersNestedInput + branch?: BranchUpdateOneWithoutAdminUsersNestedInput + avatarFile?: UploadedFileUpdateOneWithoutAdminUserAvatarsNestedInput + referredCustomers?: CustomerUpdateManyWithoutReferredByAgentNestedInput + supervisedWorkshops?: WorkshopUpdateManyWithoutSupervisorNestedInput + branchAllocations?: BranchAllocationUpdateManyWithoutAllocatorAdminUserNestedInput + postedLedgerEntries?: LedgerEntryUpdateManyWithoutPostedByAdminUserNestedInput + orderStatusEvents?: OrderStatusEventUpdateManyWithoutActorAdminUserNestedInput + preOrderStatusEvents?: PreOrderStatusEventUpdateManyWithoutActorAdminUserNestedInput + sentSmsMessages?: SmsMessageUpdateManyWithoutSentByAdminUserNestedInput + sentNotifications?: NotificationUpdateManyWithoutSentByAdminUserNestedInput + uploadedFiles?: UploadedFileUpdateManyWithoutUploadedByAdminUserNestedInput + } + + export type AdminUserUncheckedUpdateWithoutSalesExpertProfileInput = { + id?: StringFieldUpdateOperationsInput | string + fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null + mobile?: StringFieldUpdateOperationsInput | string + passwordHash?: StringFieldUpdateOperationsInput | string + role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole + userTypeId?: NullableIntFieldUpdateOperationsInput | number | null + branchId?: NullableStringFieldUpdateOperationsInput | string | null + wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null + isActive?: BoolFieldUpdateOperationsInput | boolean + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null + referredCustomers?: CustomerUncheckedUpdateManyWithoutReferredByAgentNestedInput + supervisedWorkshops?: WorkshopUncheckedUpdateManyWithoutSupervisorNestedInput + branchAllocations?: BranchAllocationUncheckedUpdateManyWithoutAllocatorAdminUserNestedInput + postedLedgerEntries?: LedgerEntryUncheckedUpdateManyWithoutPostedByAdminUserNestedInput + orderStatusEvents?: OrderStatusEventUncheckedUpdateManyWithoutActorAdminUserNestedInput + preOrderStatusEvents?: PreOrderStatusEventUncheckedUpdateManyWithoutActorAdminUserNestedInput + sentSmsMessages?: SmsMessageUncheckedUpdateManyWithoutSentByAdminUserNestedInput + sentNotifications?: NotificationUncheckedUpdateManyWithoutSentByAdminUserNestedInput + uploadedFiles?: UploadedFileUncheckedUpdateManyWithoutUploadedByAdminUserNestedInput + } + + export type CityUpsertWithoutSalesExpertProfilesInput = { + update: XOR + create: XOR + where?: CityWhereInput + } + + export type CityUpdateToOneWithWhereWithoutSalesExpertProfilesInput = { + where?: CityWhereInput + data: XOR + } + + export type CityUpdateWithoutSalesExpertProfilesInput = { + id?: StringFieldUpdateOperationsInput | string + name?: StringFieldUpdateOperationsInput | string + province?: NullableStringFieldUpdateOperationsInput | string | null + percent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null + percentAux?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null + customers?: CustomerUpdateManyWithoutCityNestedInput + addresses?: CustomerAddressUpdateManyWithoutCityNestedInput + branches?: BranchUpdateManyWithoutCityNestedInput + preOrders?: PreOrderUpdateManyWithoutCityNestedInput + } + + export type CityUncheckedUpdateWithoutSalesExpertProfilesInput = { + id?: StringFieldUpdateOperationsInput | string + name?: StringFieldUpdateOperationsInput | string + province?: NullableStringFieldUpdateOperationsInput | string | null + percent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null + percentAux?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null + customers?: CustomerUncheckedUpdateManyWithoutCityNestedInput + addresses?: CustomerAddressUncheckedUpdateManyWithoutCityNestedInput + branches?: BranchUncheckedUpdateManyWithoutCityNestedInput + preOrders?: PreOrderUncheckedUpdateManyWithoutCityNestedInput + } + + export type CustomerUpsertWithoutSalesExpertTradingForInput = { + update: XOR + create: XOR + where?: CustomerWhereInput + } + + export type CustomerUpdateToOneWithWhereWithoutSalesExpertTradingForInput = { + where?: CustomerWhereInput + data: XOR + } + + export type CustomerUpdateWithoutSalesExpertTradingForInput = { + id?: StringFieldUpdateOperationsInput | string + fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null + mobile?: StringFieldUpdateOperationsInput | string + nationalCode?: StringFieldUpdateOperationsInput | string + passwordHash?: StringFieldUpdateOperationsInput | string + kind?: EnumCustomerKindFieldUpdateOperationsInput | $Enums.CustomerKind + phone?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + zone?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null + goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep + nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null + businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null + shopImageUrl?: NullableStringFieldUpdateOperationsInput | string | null + canPlaceOrders?: BoolFieldUpdateOperationsInput | boolean + isActive?: BoolFieldUpdateOperationsInput | boolean + isArchived?: BoolFieldUpdateOperationsInput | boolean + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + city?: CityUpdateOneWithoutCustomersNestedInput + referredByAgent?: AdminUserUpdateOneWithoutReferredCustomersNestedInput + avatarFile?: UploadedFileUpdateOneWithoutCustomerAvatarsNestedInput + addresses?: CustomerAddressUpdateManyWithoutCustomerNestedInput + orders?: OrderUpdateManyWithoutCustomerNestedInput + preOrders?: PreOrderUpdateManyWithoutCustomerNestedInput + ledgerEntries?: LedgerEntryUpdateManyWithoutCustomerNestedInput + walletBonuses?: WalletBonusUpdateManyWithoutCustomerNestedInput + comments?: CommentUpdateManyWithoutCustomerNestedInput + promotions?: PromotionUpdateManyWithoutCustomerNestedInput + notifications?: NotificationRecipientUpdateManyWithoutCustomerNestedInput + activityLogs?: CustomerActivityLogUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUpdateManyWithoutCustomerNestedInput + } + + export type CustomerUncheckedUpdateWithoutSalesExpertTradingForInput = { + id?: StringFieldUpdateOperationsInput | string + fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null + mobile?: StringFieldUpdateOperationsInput | string + nationalCode?: StringFieldUpdateOperationsInput | string + passwordHash?: StringFieldUpdateOperationsInput | string + cityId?: NullableStringFieldUpdateOperationsInput | string | null + kind?: EnumCustomerKindFieldUpdateOperationsInput | $Enums.CustomerKind + phone?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + zone?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null + referredByAgentId?: NullableStringFieldUpdateOperationsInput | string | null + goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep + nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null + businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null + shopImageUrl?: NullableStringFieldUpdateOperationsInput | string | null + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + canPlaceOrders?: BoolFieldUpdateOperationsInput | boolean + isActive?: BoolFieldUpdateOperationsInput | boolean + isArchived?: BoolFieldUpdateOperationsInput | boolean + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + addresses?: CustomerAddressUncheckedUpdateManyWithoutCustomerNestedInput + orders?: OrderUncheckedUpdateManyWithoutCustomerNestedInput + preOrders?: PreOrderUncheckedUpdateManyWithoutCustomerNestedInput + ledgerEntries?: LedgerEntryUncheckedUpdateManyWithoutCustomerNestedInput + walletBonuses?: WalletBonusUncheckedUpdateManyWithoutCustomerNestedInput + comments?: CommentUncheckedUpdateManyWithoutCustomerNestedInput + promotions?: PromotionUncheckedUpdateManyWithoutCustomerNestedInput + notifications?: NotificationRecipientUncheckedUpdateManyWithoutCustomerNestedInput + activityLogs?: CustomerActivityLogUncheckedUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUncheckedUpdateManyWithoutCustomerNestedInput + } + export type CityCreateWithoutCustomersInput = { id?: string name: string @@ -53913,6 +58299,7 @@ export namespace Prisma { addresses?: CustomerAddressCreateNestedManyWithoutCityInput branches?: BranchCreateNestedManyWithoutCityInput preOrders?: PreOrderCreateNestedManyWithoutCityInput + salesExpertProfiles?: SalesExpertProfileCreateNestedManyWithoutCityInput } export type CityUncheckedCreateWithoutCustomersInput = { @@ -53924,6 +58311,7 @@ export namespace Prisma { addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCityInput branches?: BranchUncheckedCreateNestedManyWithoutCityInput preOrders?: PreOrderUncheckedCreateNestedManyWithoutCityInput + salesExpertProfiles?: SalesExpertProfileUncheckedCreateNestedManyWithoutCityInput } export type CityCreateOrConnectWithoutCustomersInput = { @@ -53934,14 +58322,21 @@ export namespace Prisma { export type AdminUserCreateWithoutReferredCustomersInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null userType?: UserTypeCreateNestedOneWithoutAdminUsersInput branch?: BranchCreateNestedOneWithoutAdminUsersInput + avatarFile?: UploadedFileCreateNestedOneWithoutAdminUserAvatarsInput supervisedWorkshops?: WorkshopCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationCreateNestedManyWithoutAllocatorAdminUserInput postedLedgerEntries?: LedgerEntryCreateNestedManyWithoutPostedByAdminUserInput @@ -53950,11 +58345,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileCreateNestedOneWithoutAdminUserInput } export type AdminUserUncheckedCreateWithoutReferredCustomersInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole @@ -53963,6 +58361,11 @@ export namespace Prisma { wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + avatarFileId?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null supervisedWorkshops?: WorkshopUncheckedCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationUncheckedCreateNestedManyWithoutAllocatorAdminUserInput postedLedgerEntries?: LedgerEntryUncheckedCreateNestedManyWithoutPostedByAdminUserInput @@ -53971,6 +58374,7 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationUncheckedCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileUncheckedCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileUncheckedCreateNestedOneWithoutAdminUserInput } export type AdminUserCreateOrConnectWithoutReferredCustomersInput = { @@ -53978,6 +58382,33 @@ export namespace Prisma { create: XOR } + export type SalesExpertProfileCreateWithoutTradingCustomerInput = { + id?: string + gramLevel?: Decimal | DecimalJsLike | number | string + phone?: string | null + nationalCode?: string | null + createdAt?: Date | string + updatedAt?: Date | string + adminUser: AdminUserCreateNestedOneWithoutSalesExpertProfileInput + city?: CityCreateNestedOneWithoutSalesExpertProfilesInput + } + + export type SalesExpertProfileUncheckedCreateWithoutTradingCustomerInput = { + id?: string + adminUserId: string + cityId?: string | null + gramLevel?: Decimal | DecimalJsLike | number | string + phone?: string | null + nationalCode?: string | null + createdAt?: Date | string + updatedAt?: Date | string + } + + export type SalesExpertProfileCreateOrConnectWithoutTradingCustomerInput = { + where: SalesExpertProfileWhereUniqueInput + create: XOR + } + export type UploadedFileCreateWithoutCustomerAvatarsInput = { id?: string url: string @@ -53987,6 +58418,7 @@ export namespace Prisma { purpose?: string | null createdAt?: Date | string uploadedByAdminUser?: AdminUserCreateNestedOneWithoutUploadedFilesInput + adminUserAvatars?: AdminUserCreateNestedManyWithoutAvatarFileInput } export type UploadedFileUncheckedCreateWithoutCustomerAvatarsInput = { @@ -53998,6 +58430,7 @@ export namespace Prisma { purpose?: string | null uploadedByAdminUserId?: string | null createdAt?: Date | string + adminUserAvatars?: AdminUserUncheckedCreateNestedManyWithoutAvatarFileInput } export type UploadedFileCreateOrConnectWithoutCustomerAvatarsInput = { @@ -54337,6 +58770,38 @@ export namespace Prisma { skipDuplicates?: boolean } + export type CustomerOtpCreateWithoutCustomerInput = { + id?: string + mobile: string + codeHash: string + expiresAt: Date | string + attempts?: number + consumedAt?: Date | string | null + requestIp?: string | null + createdAt?: Date | string + } + + export type CustomerOtpUncheckedCreateWithoutCustomerInput = { + id?: string + mobile: string + codeHash: string + expiresAt: Date | string + attempts?: number + consumedAt?: Date | string | null + requestIp?: string | null + createdAt?: Date | string + } + + export type CustomerOtpCreateOrConnectWithoutCustomerInput = { + where: CustomerOtpWhereUniqueInput + create: XOR + } + + export type CustomerOtpCreateManyCustomerInputEnvelope = { + data: CustomerOtpCreateManyCustomerInput | CustomerOtpCreateManyCustomerInput[] + skipDuplicates?: boolean + } + export type CityUpsertWithoutCustomersInput = { update: XOR create: XOR @@ -54357,6 +58822,7 @@ export namespace Prisma { addresses?: CustomerAddressUpdateManyWithoutCityNestedInput branches?: BranchUpdateManyWithoutCityNestedInput preOrders?: PreOrderUpdateManyWithoutCityNestedInput + salesExpertProfiles?: SalesExpertProfileUpdateManyWithoutCityNestedInput } export type CityUncheckedUpdateWithoutCustomersInput = { @@ -54368,6 +58834,7 @@ export namespace Prisma { addresses?: CustomerAddressUncheckedUpdateManyWithoutCityNestedInput branches?: BranchUncheckedUpdateManyWithoutCityNestedInput preOrders?: PreOrderUncheckedUpdateManyWithoutCityNestedInput + salesExpertProfiles?: SalesExpertProfileUncheckedUpdateManyWithoutCityNestedInput } export type AdminUserUpsertWithoutReferredCustomersInput = { @@ -54384,14 +58851,21 @@ export namespace Prisma { export type AdminUserUpdateWithoutReferredCustomersInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null userType?: UserTypeUpdateOneWithoutAdminUsersNestedInput branch?: BranchUpdateOneWithoutAdminUsersNestedInput + avatarFile?: UploadedFileUpdateOneWithoutAdminUserAvatarsNestedInput supervisedWorkshops?: WorkshopUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUpdateManyWithoutAllocatorAdminUserNestedInput postedLedgerEntries?: LedgerEntryUpdateManyWithoutPostedByAdminUserNestedInput @@ -54400,11 +58874,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUpdateOneWithoutAdminUserNestedInput } export type AdminUserUncheckedUpdateWithoutReferredCustomersInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole @@ -54413,6 +58890,11 @@ export namespace Prisma { wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null supervisedWorkshops?: WorkshopUncheckedUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUncheckedUpdateManyWithoutAllocatorAdminUserNestedInput postedLedgerEntries?: LedgerEntryUncheckedUpdateManyWithoutPostedByAdminUserNestedInput @@ -54421,6 +58903,40 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUncheckedUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUncheckedUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUncheckedUpdateOneWithoutAdminUserNestedInput + } + + export type SalesExpertProfileUpsertWithoutTradingCustomerInput = { + update: XOR + create: XOR + where?: SalesExpertProfileWhereInput + } + + export type SalesExpertProfileUpdateToOneWithWhereWithoutTradingCustomerInput = { + where?: SalesExpertProfileWhereInput + data: XOR + } + + export type SalesExpertProfileUpdateWithoutTradingCustomerInput = { + id?: StringFieldUpdateOperationsInput | string + gramLevel?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + phone?: NullableStringFieldUpdateOperationsInput | string | null + nationalCode?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + adminUser?: AdminUserUpdateOneRequiredWithoutSalesExpertProfileNestedInput + city?: CityUpdateOneWithoutSalesExpertProfilesNestedInput + } + + export type SalesExpertProfileUncheckedUpdateWithoutTradingCustomerInput = { + id?: StringFieldUpdateOperationsInput | string + adminUserId?: StringFieldUpdateOperationsInput | string + cityId?: NullableStringFieldUpdateOperationsInput | string | null + gramLevel?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + phone?: NullableStringFieldUpdateOperationsInput | string | null + nationalCode?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string } export type UploadedFileUpsertWithoutCustomerAvatarsInput = { @@ -54443,6 +58959,7 @@ export namespace Prisma { purpose?: NullableStringFieldUpdateOperationsInput | string | null createdAt?: DateTimeFieldUpdateOperationsInput | Date | string uploadedByAdminUser?: AdminUserUpdateOneWithoutUploadedFilesNestedInput + adminUserAvatars?: AdminUserUpdateManyWithoutAvatarFileNestedInput } export type UploadedFileUncheckedUpdateWithoutCustomerAvatarsInput = { @@ -54454,6 +58971,7 @@ export namespace Prisma { purpose?: NullableStringFieldUpdateOperationsInput | string | null uploadedByAdminUserId?: NullableStringFieldUpdateOperationsInput | string | null createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + adminUserAvatars?: AdminUserUncheckedUpdateManyWithoutAvatarFileNestedInput } export type CustomerAddressUpsertWithWhereUniqueWithoutCustomerInput = { @@ -54690,9 +59208,42 @@ export namespace Prisma { createdAt?: DateTimeFilter<"CustomerActivityLog"> | Date | string } + export type CustomerOtpUpsertWithWhereUniqueWithoutCustomerInput = { + where: CustomerOtpWhereUniqueInput + update: XOR + create: XOR + } + + export type CustomerOtpUpdateWithWhereUniqueWithoutCustomerInput = { + where: CustomerOtpWhereUniqueInput + data: XOR + } + + export type CustomerOtpUpdateManyWithWhereWithoutCustomerInput = { + where: CustomerOtpScalarWhereInput + data: XOR + } + + export type CustomerOtpScalarWhereInput = { + AND?: CustomerOtpScalarWhereInput | CustomerOtpScalarWhereInput[] + OR?: CustomerOtpScalarWhereInput[] + NOT?: CustomerOtpScalarWhereInput | CustomerOtpScalarWhereInput[] + id?: StringFilter<"CustomerOtp"> | string + mobile?: StringFilter<"CustomerOtp"> | string + customerId?: StringFilter<"CustomerOtp"> | string + codeHash?: StringFilter<"CustomerOtp"> | string + expiresAt?: DateTimeFilter<"CustomerOtp"> | Date | string + attempts?: IntFilter<"CustomerOtp"> | number + consumedAt?: DateTimeNullableFilter<"CustomerOtp"> | Date | string | null + requestIp?: StringNullableFilter<"CustomerOtp"> | string | null + createdAt?: DateTimeFilter<"CustomerOtp"> | Date | string + } + export type CustomerCreateWithoutAddressesInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -54706,6 +59257,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -54717,6 +59269,7 @@ export namespace Prisma { updatedAt?: Date | string city?: CityCreateNestedOneWithoutCustomersInput referredByAgent?: AdminUserCreateNestedOneWithoutReferredCustomersInput + salesExpertTradingFor?: SalesExpertProfileCreateNestedOneWithoutTradingCustomerInput avatarFile?: UploadedFileCreateNestedOneWithoutCustomerAvatarsInput orders?: OrderCreateNestedManyWithoutCustomerInput preOrders?: PreOrderCreateNestedManyWithoutCustomerInput @@ -54726,11 +59279,14 @@ export namespace Prisma { promotions?: PromotionCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpCreateNestedManyWithoutCustomerInput } export type CustomerUncheckedCreateWithoutAddressesInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -54746,6 +59302,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -54756,6 +59313,7 @@ export namespace Prisma { isArchived?: boolean createdAt?: Date | string updatedAt?: Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedCreateNestedOneWithoutTradingCustomerInput orders?: OrderUncheckedCreateNestedManyWithoutCustomerInput preOrders?: PreOrderUncheckedCreateNestedManyWithoutCustomerInput ledgerEntries?: LedgerEntryUncheckedCreateNestedManyWithoutCustomerInput @@ -54764,6 +59322,7 @@ export namespace Prisma { promotions?: PromotionUncheckedCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientUncheckedCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogUncheckedCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpUncheckedCreateNestedManyWithoutCustomerInput } export type CustomerCreateOrConnectWithoutAddressesInput = { @@ -54780,6 +59339,7 @@ export namespace Prisma { customers?: CustomerCreateNestedManyWithoutCityInput branches?: BranchCreateNestedManyWithoutCityInput preOrders?: PreOrderCreateNestedManyWithoutCityInput + salesExpertProfiles?: SalesExpertProfileCreateNestedManyWithoutCityInput } export type CityUncheckedCreateWithoutAddressesInput = { @@ -54791,6 +59351,7 @@ export namespace Prisma { customers?: CustomerUncheckedCreateNestedManyWithoutCityInput branches?: BranchUncheckedCreateNestedManyWithoutCityInput preOrders?: PreOrderUncheckedCreateNestedManyWithoutCityInput + salesExpertProfiles?: SalesExpertProfileUncheckedCreateNestedManyWithoutCityInput } export type CityCreateOrConnectWithoutAddressesInput = { @@ -54812,6 +59373,8 @@ export namespace Prisma { export type CustomerUpdateWithoutAddressesInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -54825,6 +59388,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -54836,6 +59400,7 @@ export namespace Prisma { updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string city?: CityUpdateOneWithoutCustomersNestedInput referredByAgent?: AdminUserUpdateOneWithoutReferredCustomersNestedInput + salesExpertTradingFor?: SalesExpertProfileUpdateOneWithoutTradingCustomerNestedInput avatarFile?: UploadedFileUpdateOneWithoutCustomerAvatarsNestedInput orders?: OrderUpdateManyWithoutCustomerNestedInput preOrders?: PreOrderUpdateManyWithoutCustomerNestedInput @@ -54845,11 +59410,14 @@ export namespace Prisma { promotions?: PromotionUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUpdateManyWithoutCustomerNestedInput } export type CustomerUncheckedUpdateWithoutAddressesInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -54865,6 +59433,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -54875,6 +59444,7 @@ export namespace Prisma { isArchived?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedUpdateOneWithoutTradingCustomerNestedInput orders?: OrderUncheckedUpdateManyWithoutCustomerNestedInput preOrders?: PreOrderUncheckedUpdateManyWithoutCustomerNestedInput ledgerEntries?: LedgerEntryUncheckedUpdateManyWithoutCustomerNestedInput @@ -54883,6 +59453,7 @@ export namespace Prisma { promotions?: PromotionUncheckedUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUncheckedUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUncheckedUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUncheckedUpdateManyWithoutCustomerNestedInput } export type CityUpsertWithoutAddressesInput = { @@ -54905,6 +59476,7 @@ export namespace Prisma { customers?: CustomerUpdateManyWithoutCityNestedInput branches?: BranchUpdateManyWithoutCityNestedInput preOrders?: PreOrderUpdateManyWithoutCityNestedInput + salesExpertProfiles?: SalesExpertProfileUpdateManyWithoutCityNestedInput } export type CityUncheckedUpdateWithoutAddressesInput = { @@ -54916,6 +59488,7 @@ export namespace Prisma { customers?: CustomerUncheckedUpdateManyWithoutCityNestedInput branches?: BranchUncheckedUpdateManyWithoutCityNestedInput preOrders?: PreOrderUncheckedUpdateManyWithoutCityNestedInput + salesExpertProfiles?: SalesExpertProfileUncheckedUpdateManyWithoutCityNestedInput } export type CityCreateWithoutBranchesInput = { @@ -54927,6 +59500,7 @@ export namespace Prisma { customers?: CustomerCreateNestedManyWithoutCityInput addresses?: CustomerAddressCreateNestedManyWithoutCityInput preOrders?: PreOrderCreateNestedManyWithoutCityInput + salesExpertProfiles?: SalesExpertProfileCreateNestedManyWithoutCityInput } export type CityUncheckedCreateWithoutBranchesInput = { @@ -54938,6 +59512,7 @@ export namespace Prisma { customers?: CustomerUncheckedCreateNestedManyWithoutCityInput addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCityInput preOrders?: PreOrderUncheckedCreateNestedManyWithoutCityInput + salesExpertProfiles?: SalesExpertProfileUncheckedCreateNestedManyWithoutCityInput } export type CityCreateOrConnectWithoutBranchesInput = { @@ -54948,13 +59523,20 @@ export namespace Prisma { export type AdminUserCreateWithoutBranchInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null userType?: UserTypeCreateNestedOneWithoutAdminUsersInput + avatarFile?: UploadedFileCreateNestedOneWithoutAdminUserAvatarsInput referredCustomers?: CustomerCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationCreateNestedManyWithoutAllocatorAdminUserInput @@ -54964,11 +59546,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileCreateNestedOneWithoutAdminUserInput } export type AdminUserUncheckedCreateWithoutBranchInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole @@ -54976,6 +59561,11 @@ export namespace Prisma { wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + avatarFileId?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null referredCustomers?: CustomerUncheckedCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopUncheckedCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationUncheckedCreateNestedManyWithoutAllocatorAdminUserInput @@ -54985,6 +59575,7 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationUncheckedCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileUncheckedCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileUncheckedCreateNestedOneWithoutAdminUserInput } export type AdminUserCreateOrConnectWithoutBranchInput = { @@ -55091,6 +59682,7 @@ export namespace Prisma { customers?: CustomerUpdateManyWithoutCityNestedInput addresses?: CustomerAddressUpdateManyWithoutCityNestedInput preOrders?: PreOrderUpdateManyWithoutCityNestedInput + salesExpertProfiles?: SalesExpertProfileUpdateManyWithoutCityNestedInput } export type CityUncheckedUpdateWithoutBranchesInput = { @@ -55102,6 +59694,7 @@ export namespace Prisma { customers?: CustomerUncheckedUpdateManyWithoutCityNestedInput addresses?: CustomerAddressUncheckedUpdateManyWithoutCityNestedInput preOrders?: PreOrderUncheckedUpdateManyWithoutCityNestedInput + salesExpertProfiles?: SalesExpertProfileUncheckedUpdateManyWithoutCityNestedInput } export type AdminUserUpsertWithWhereUniqueWithoutBranchInput = { @@ -55126,6 +59719,8 @@ export namespace Prisma { NOT?: AdminUserScalarWhereInput | AdminUserScalarWhereInput[] id?: StringFilter<"AdminUser"> | string fullName?: StringFilter<"AdminUser"> | string + firstName?: StringNullableFilter<"AdminUser"> | string | null + lastName?: StringNullableFilter<"AdminUser"> | string | null mobile?: StringFilter<"AdminUser"> | string passwordHash?: StringFilter<"AdminUser"> | string role?: EnumAdminRoleFilter<"AdminUser"> | $Enums.AdminRole @@ -55134,6 +59729,11 @@ export namespace Prisma { wagePercent?: DecimalNullableFilter<"AdminUser"> | Decimal | DecimalJsLike | number | string | null isActive?: BoolFilter<"AdminUser"> | boolean createdAt?: DateTimeFilter<"AdminUser"> | Date | string + avatarFileId?: StringNullableFilter<"AdminUser"> | string | null + address?: StringNullableFilter<"AdminUser"> | string | null + storeName?: StringNullableFilter<"AdminUser"> | string | null + pasazhName?: StringNullableFilter<"AdminUser"> | string | null + memberCode?: StringNullableFilter<"AdminUser"> | string | null } export type OrderUpsertWithWhereUniqueWithoutBranchInput = { @@ -55171,14 +59771,21 @@ export namespace Prisma { export type AdminUserCreateWithoutSupervisedWorkshopsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null userType?: UserTypeCreateNestedOneWithoutAdminUsersInput branch?: BranchCreateNestedOneWithoutAdminUsersInput + avatarFile?: UploadedFileCreateNestedOneWithoutAdminUserAvatarsInput referredCustomers?: CustomerCreateNestedManyWithoutReferredByAgentInput branchAllocations?: BranchAllocationCreateNestedManyWithoutAllocatorAdminUserInput postedLedgerEntries?: LedgerEntryCreateNestedManyWithoutPostedByAdminUserInput @@ -55187,11 +59794,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileCreateNestedOneWithoutAdminUserInput } export type AdminUserUncheckedCreateWithoutSupervisedWorkshopsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole @@ -55200,6 +59810,11 @@ export namespace Prisma { wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + avatarFileId?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null referredCustomers?: CustomerUncheckedCreateNestedManyWithoutReferredByAgentInput branchAllocations?: BranchAllocationUncheckedCreateNestedManyWithoutAllocatorAdminUserInput postedLedgerEntries?: LedgerEntryUncheckedCreateNestedManyWithoutPostedByAdminUserInput @@ -55208,6 +59823,7 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationUncheckedCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileUncheckedCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileUncheckedCreateNestedOneWithoutAdminUserInput } export type AdminUserCreateOrConnectWithoutSupervisedWorkshopsInput = { @@ -55277,14 +59893,21 @@ export namespace Prisma { export type AdminUserUpdateWithoutSupervisedWorkshopsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null userType?: UserTypeUpdateOneWithoutAdminUsersNestedInput branch?: BranchUpdateOneWithoutAdminUsersNestedInput + avatarFile?: UploadedFileUpdateOneWithoutAdminUserAvatarsNestedInput referredCustomers?: CustomerUpdateManyWithoutReferredByAgentNestedInput branchAllocations?: BranchAllocationUpdateManyWithoutAllocatorAdminUserNestedInput postedLedgerEntries?: LedgerEntryUpdateManyWithoutPostedByAdminUserNestedInput @@ -55293,11 +59916,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUpdateOneWithoutAdminUserNestedInput } export type AdminUserUncheckedUpdateWithoutSupervisedWorkshopsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole @@ -55306,6 +59932,11 @@ export namespace Prisma { wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null referredCustomers?: CustomerUncheckedUpdateManyWithoutReferredByAgentNestedInput branchAllocations?: BranchAllocationUncheckedUpdateManyWithoutAllocatorAdminUserNestedInput postedLedgerEntries?: LedgerEntryUncheckedUpdateManyWithoutPostedByAdminUserNestedInput @@ -55314,6 +59945,7 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUncheckedUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUncheckedUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUncheckedUpdateOneWithoutAdminUserNestedInput } export type PreOrderUpsertWithWhereUniqueWithoutWorkshopInput = { @@ -55952,6 +60584,7 @@ export namespace Prisma { qty?: number isPreOrder?: boolean isActive?: boolean + image?: ProductImageCreateNestedOneWithoutVariantsInput orderItems?: OrderItemCreateNestedManyWithoutProductVariantInput } @@ -55964,6 +60597,7 @@ export namespace Prisma { qty?: number isPreOrder?: boolean isActive?: boolean + imageId?: string | null orderItems?: OrderItemUncheckedCreateNestedManyWithoutProductVariantInput } @@ -55982,6 +60616,7 @@ export namespace Prisma { url: string sortOrder?: number role?: $Enums.ProductImageRole + variants?: ProductVariantCreateNestedManyWithoutImageInput } export type ProductImageUncheckedCreateWithoutProductInput = { @@ -55989,6 +60624,7 @@ export namespace Prisma { url: string sortOrder?: number role?: $Enums.ProductImageRole + variants?: ProductVariantUncheckedCreateNestedManyWithoutImageInput } export type ProductImageCreateOrConnectWithoutProductInput = { @@ -56208,6 +60844,7 @@ export namespace Prisma { qty?: IntFilter<"ProductVariant"> | number isPreOrder?: BoolFilter<"ProductVariant"> | boolean isActive?: BoolFilter<"ProductVariant"> | boolean + imageId?: StringNullableFilter<"ProductVariant"> | string | null } export type ProductImageUpsertWithWhereUniqueWithoutProductInput = { @@ -56350,6 +60987,42 @@ export namespace Prisma { create: XOR } + export type ProductVariantCreateWithoutImageInput = { + id?: string + title: string + sku?: string | null + rashWeightGrams: Decimal | DecimalJsLike | number | string + defaultWagePercent: Decimal | DecimalJsLike | number | string + qty?: number + isPreOrder?: boolean + isActive?: boolean + product: ProductCreateNestedOneWithoutVariantsInput + orderItems?: OrderItemCreateNestedManyWithoutProductVariantInput + } + + export type ProductVariantUncheckedCreateWithoutImageInput = { + id?: string + productId: string + title: string + sku?: string | null + rashWeightGrams: Decimal | DecimalJsLike | number | string + defaultWagePercent: Decimal | DecimalJsLike | number | string + qty?: number + isPreOrder?: boolean + isActive?: boolean + orderItems?: OrderItemUncheckedCreateNestedManyWithoutProductVariantInput + } + + export type ProductVariantCreateOrConnectWithoutImageInput = { + where: ProductVariantWhereUniqueInput + create: XOR + } + + export type ProductVariantCreateManyImageInputEnvelope = { + data: ProductVariantCreateManyImageInput | ProductVariantCreateManyImageInput[] + skipDuplicates?: boolean + } + export type ProductUpsertWithoutImagesInput = { update: XOR create: XOR @@ -56395,6 +61068,22 @@ export namespace Prisma { allocationItems?: BranchAllocationItemUncheckedUpdateManyWithoutProductNestedInput } + export type ProductVariantUpsertWithWhereUniqueWithoutImageInput = { + where: ProductVariantWhereUniqueInput + update: XOR + create: XOR + } + + export type ProductVariantUpdateWithWhereUniqueWithoutImageInput = { + where: ProductVariantWhereUniqueInput + data: XOR + } + + export type ProductVariantUpdateManyWithWhereWithoutImageInput = { + where: ProductVariantScalarWhereInput + data: XOR + } + export type ProductCreateWithoutVariantsInput = { id?: string title: string @@ -56434,6 +61123,27 @@ export namespace Prisma { create: XOR } + export type ProductImageCreateWithoutVariantsInput = { + id?: string + url: string + sortOrder?: number + role?: $Enums.ProductImageRole + product: ProductCreateNestedOneWithoutImagesInput + } + + export type ProductImageUncheckedCreateWithoutVariantsInput = { + id?: string + productId: string + url: string + sortOrder?: number + role?: $Enums.ProductImageRole + } + + export type ProductImageCreateOrConnectWithoutVariantsInput = { + where: ProductImageWhereUniqueInput + create: XOR + } + export type OrderItemCreateWithoutProductVariantInput = { id?: string quantity: number @@ -56511,6 +61221,33 @@ export namespace Prisma { allocationItems?: BranchAllocationItemUncheckedUpdateManyWithoutProductNestedInput } + export type ProductImageUpsertWithoutVariantsInput = { + update: XOR + create: XOR + where?: ProductImageWhereInput + } + + export type ProductImageUpdateToOneWithWhereWithoutVariantsInput = { + where?: ProductImageWhereInput + data: XOR + } + + export type ProductImageUpdateWithoutVariantsInput = { + id?: StringFieldUpdateOperationsInput | string + url?: StringFieldUpdateOperationsInput | string + sortOrder?: IntFieldUpdateOperationsInput | number + role?: EnumProductImageRoleFieldUpdateOperationsInput | $Enums.ProductImageRole + product?: ProductUpdateOneRequiredWithoutImagesNestedInput + } + + export type ProductImageUncheckedUpdateWithoutVariantsInput = { + id?: StringFieldUpdateOperationsInput | string + productId?: StringFieldUpdateOperationsInput | string + url?: StringFieldUpdateOperationsInput | string + sortOrder?: IntFieldUpdateOperationsInput | number + role?: EnumProductImageRoleFieldUpdateOperationsInput | $Enums.ProductImageRole + } + export type OrderItemUpsertWithWhereUniqueWithoutProductVariantInput = { where: OrderItemWhereUniqueInput update: XOR @@ -56545,6 +61282,8 @@ export namespace Prisma { export type CustomerCreateWithoutOrdersInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -56558,6 +61297,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -56569,6 +61309,7 @@ export namespace Prisma { updatedAt?: Date | string city?: CityCreateNestedOneWithoutCustomersInput referredByAgent?: AdminUserCreateNestedOneWithoutReferredCustomersInput + salesExpertTradingFor?: SalesExpertProfileCreateNestedOneWithoutTradingCustomerInput avatarFile?: UploadedFileCreateNestedOneWithoutCustomerAvatarsInput addresses?: CustomerAddressCreateNestedManyWithoutCustomerInput preOrders?: PreOrderCreateNestedManyWithoutCustomerInput @@ -56578,11 +61319,14 @@ export namespace Prisma { promotions?: PromotionCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpCreateNestedManyWithoutCustomerInput } export type CustomerUncheckedCreateWithoutOrdersInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -56598,6 +61342,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -56608,6 +61353,7 @@ export namespace Prisma { isArchived?: boolean createdAt?: Date | string updatedAt?: Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedCreateNestedOneWithoutTradingCustomerInput addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCustomerInput preOrders?: PreOrderUncheckedCreateNestedManyWithoutCustomerInput ledgerEntries?: LedgerEntryUncheckedCreateNestedManyWithoutCustomerInput @@ -56616,6 +61362,7 @@ export namespace Prisma { promotions?: PromotionUncheckedCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientUncheckedCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogUncheckedCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpUncheckedCreateNestedManyWithoutCustomerInput } export type CustomerCreateOrConnectWithoutOrdersInput = { @@ -56825,6 +61572,8 @@ export namespace Prisma { export type CustomerUpdateWithoutOrdersInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -56838,6 +61587,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -56849,6 +61599,7 @@ export namespace Prisma { updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string city?: CityUpdateOneWithoutCustomersNestedInput referredByAgent?: AdminUserUpdateOneWithoutReferredCustomersNestedInput + salesExpertTradingFor?: SalesExpertProfileUpdateOneWithoutTradingCustomerNestedInput avatarFile?: UploadedFileUpdateOneWithoutCustomerAvatarsNestedInput addresses?: CustomerAddressUpdateManyWithoutCustomerNestedInput preOrders?: PreOrderUpdateManyWithoutCustomerNestedInput @@ -56858,11 +61609,14 @@ export namespace Prisma { promotions?: PromotionUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUpdateManyWithoutCustomerNestedInput } export type CustomerUncheckedUpdateWithoutOrdersInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -56878,6 +61632,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -56888,6 +61643,7 @@ export namespace Prisma { isArchived?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedUpdateOneWithoutTradingCustomerNestedInput addresses?: CustomerAddressUncheckedUpdateManyWithoutCustomerNestedInput preOrders?: PreOrderUncheckedUpdateManyWithoutCustomerNestedInput ledgerEntries?: LedgerEntryUncheckedUpdateManyWithoutCustomerNestedInput @@ -56896,6 +61652,7 @@ export namespace Prisma { promotions?: PromotionUncheckedUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUncheckedUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUncheckedUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUncheckedUpdateManyWithoutCustomerNestedInput } export type BranchUpsertWithoutOrdersInput = { @@ -57081,6 +61838,7 @@ export namespace Prisma { isPreOrder?: boolean isActive?: boolean product: ProductCreateNestedOneWithoutVariantsInput + image?: ProductImageCreateNestedOneWithoutVariantsInput } export type ProductVariantUncheckedCreateWithoutOrderItemsInput = { @@ -57093,6 +61851,7 @@ export namespace Prisma { qty?: number isPreOrder?: boolean isActive?: boolean + imageId?: string | null } export type ProductVariantCreateOrConnectWithoutOrderItemsInput = { @@ -57170,6 +61929,7 @@ export namespace Prisma { isPreOrder?: BoolFieldUpdateOperationsInput | boolean isActive?: BoolFieldUpdateOperationsInput | boolean product?: ProductUpdateOneRequiredWithoutVariantsNestedInput + image?: ProductImageUpdateOneWithoutVariantsNestedInput } export type ProductVariantUncheckedUpdateWithoutOrderItemsInput = { @@ -57182,6 +61942,7 @@ export namespace Prisma { qty?: IntFieldUpdateOperationsInput | number isPreOrder?: BoolFieldUpdateOperationsInput | boolean isActive?: BoolFieldUpdateOperationsInput | boolean + imageId?: NullableStringFieldUpdateOperationsInput | string | null } export type OrderCreateWithoutStatusEventsInput = { @@ -57230,14 +61991,21 @@ export namespace Prisma { export type AdminUserCreateWithoutOrderStatusEventsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null userType?: UserTypeCreateNestedOneWithoutAdminUsersInput branch?: BranchCreateNestedOneWithoutAdminUsersInput + avatarFile?: UploadedFileCreateNestedOneWithoutAdminUserAvatarsInput referredCustomers?: CustomerCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationCreateNestedManyWithoutAllocatorAdminUserInput @@ -57246,11 +62014,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileCreateNestedOneWithoutAdminUserInput } export type AdminUserUncheckedCreateWithoutOrderStatusEventsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole @@ -57259,6 +62030,11 @@ export namespace Prisma { wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + avatarFileId?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null referredCustomers?: CustomerUncheckedCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopUncheckedCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationUncheckedCreateNestedManyWithoutAllocatorAdminUserInput @@ -57267,6 +62043,7 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationUncheckedCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileUncheckedCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileUncheckedCreateNestedOneWithoutAdminUserInput } export type AdminUserCreateOrConnectWithoutOrderStatusEventsInput = { @@ -57337,14 +62114,21 @@ export namespace Prisma { export type AdminUserUpdateWithoutOrderStatusEventsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null userType?: UserTypeUpdateOneWithoutAdminUsersNestedInput branch?: BranchUpdateOneWithoutAdminUsersNestedInput + avatarFile?: UploadedFileUpdateOneWithoutAdminUserAvatarsNestedInput referredCustomers?: CustomerUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUpdateManyWithoutAllocatorAdminUserNestedInput @@ -57353,11 +62137,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUpdateOneWithoutAdminUserNestedInput } export type AdminUserUncheckedUpdateWithoutOrderStatusEventsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole @@ -57366,6 +62153,11 @@ export namespace Prisma { wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null referredCustomers?: CustomerUncheckedUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUncheckedUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUncheckedUpdateManyWithoutAllocatorAdminUserNestedInput @@ -57374,11 +62166,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUncheckedUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUncheckedUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUncheckedUpdateOneWithoutAdminUserNestedInput } export type CustomerCreateWithoutPreOrdersInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -57392,6 +62187,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -57403,6 +62199,7 @@ export namespace Prisma { updatedAt?: Date | string city?: CityCreateNestedOneWithoutCustomersInput referredByAgent?: AdminUserCreateNestedOneWithoutReferredCustomersInput + salesExpertTradingFor?: SalesExpertProfileCreateNestedOneWithoutTradingCustomerInput avatarFile?: UploadedFileCreateNestedOneWithoutCustomerAvatarsInput addresses?: CustomerAddressCreateNestedManyWithoutCustomerInput orders?: OrderCreateNestedManyWithoutCustomerInput @@ -57412,11 +62209,14 @@ export namespace Prisma { promotions?: PromotionCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpCreateNestedManyWithoutCustomerInput } export type CustomerUncheckedCreateWithoutPreOrdersInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -57432,6 +62232,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -57442,6 +62243,7 @@ export namespace Prisma { isArchived?: boolean createdAt?: Date | string updatedAt?: Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedCreateNestedOneWithoutTradingCustomerInput addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCustomerInput orders?: OrderUncheckedCreateNestedManyWithoutCustomerInput ledgerEntries?: LedgerEntryUncheckedCreateNestedManyWithoutCustomerInput @@ -57450,6 +62252,7 @@ export namespace Prisma { promotions?: PromotionUncheckedCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientUncheckedCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogUncheckedCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpUncheckedCreateNestedManyWithoutCustomerInput } export type CustomerCreateOrConnectWithoutPreOrdersInput = { @@ -57466,6 +62269,7 @@ export namespace Prisma { customers?: CustomerCreateNestedManyWithoutCityInput addresses?: CustomerAddressCreateNestedManyWithoutCityInput branches?: BranchCreateNestedManyWithoutCityInput + salesExpertProfiles?: SalesExpertProfileCreateNestedManyWithoutCityInput } export type CityUncheckedCreateWithoutPreOrdersInput = { @@ -57477,6 +62281,7 @@ export namespace Prisma { customers?: CustomerUncheckedCreateNestedManyWithoutCityInput addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCityInput branches?: BranchUncheckedCreateNestedManyWithoutCityInput + salesExpertProfiles?: SalesExpertProfileUncheckedCreateNestedManyWithoutCityInput } export type CityCreateOrConnectWithoutPreOrdersInput = { @@ -57590,6 +62395,8 @@ export namespace Prisma { export type CustomerUpdateWithoutPreOrdersInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -57603,6 +62410,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -57614,6 +62422,7 @@ export namespace Prisma { updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string city?: CityUpdateOneWithoutCustomersNestedInput referredByAgent?: AdminUserUpdateOneWithoutReferredCustomersNestedInput + salesExpertTradingFor?: SalesExpertProfileUpdateOneWithoutTradingCustomerNestedInput avatarFile?: UploadedFileUpdateOneWithoutCustomerAvatarsNestedInput addresses?: CustomerAddressUpdateManyWithoutCustomerNestedInput orders?: OrderUpdateManyWithoutCustomerNestedInput @@ -57623,11 +62432,14 @@ export namespace Prisma { promotions?: PromotionUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUpdateManyWithoutCustomerNestedInput } export type CustomerUncheckedUpdateWithoutPreOrdersInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -57643,6 +62455,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -57653,6 +62466,7 @@ export namespace Prisma { isArchived?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedUpdateOneWithoutTradingCustomerNestedInput addresses?: CustomerAddressUncheckedUpdateManyWithoutCustomerNestedInput orders?: OrderUncheckedUpdateManyWithoutCustomerNestedInput ledgerEntries?: LedgerEntryUncheckedUpdateManyWithoutCustomerNestedInput @@ -57661,6 +62475,7 @@ export namespace Prisma { promotions?: PromotionUncheckedUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUncheckedUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUncheckedUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUncheckedUpdateManyWithoutCustomerNestedInput } export type CityUpsertWithoutPreOrdersInput = { @@ -57683,6 +62498,7 @@ export namespace Prisma { customers?: CustomerUpdateManyWithoutCityNestedInput addresses?: CustomerAddressUpdateManyWithoutCityNestedInput branches?: BranchUpdateManyWithoutCityNestedInput + salesExpertProfiles?: SalesExpertProfileUpdateManyWithoutCityNestedInput } export type CityUncheckedUpdateWithoutPreOrdersInput = { @@ -57694,6 +62510,7 @@ export namespace Prisma { customers?: CustomerUncheckedUpdateManyWithoutCityNestedInput addresses?: CustomerAddressUncheckedUpdateManyWithoutCityNestedInput branches?: BranchUncheckedUpdateManyWithoutCityNestedInput + salesExpertProfiles?: SalesExpertProfileUncheckedUpdateManyWithoutCityNestedInput } export type WorkshopUpsertWithoutPreOrdersInput = { @@ -57836,14 +62653,21 @@ export namespace Prisma { export type AdminUserCreateWithoutPreOrderStatusEventsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null userType?: UserTypeCreateNestedOneWithoutAdminUsersInput branch?: BranchCreateNestedOneWithoutAdminUsersInput + avatarFile?: UploadedFileCreateNestedOneWithoutAdminUserAvatarsInput referredCustomers?: CustomerCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationCreateNestedManyWithoutAllocatorAdminUserInput @@ -57852,11 +62676,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileCreateNestedOneWithoutAdminUserInput } export type AdminUserUncheckedCreateWithoutPreOrderStatusEventsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole @@ -57865,6 +62692,11 @@ export namespace Prisma { wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + avatarFileId?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null referredCustomers?: CustomerUncheckedCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopUncheckedCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationUncheckedCreateNestedManyWithoutAllocatorAdminUserInput @@ -57873,6 +62705,7 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationUncheckedCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileUncheckedCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileUncheckedCreateNestedOneWithoutAdminUserInput } export type AdminUserCreateOrConnectWithoutPreOrderStatusEventsInput = { @@ -57943,14 +62776,21 @@ export namespace Prisma { export type AdminUserUpdateWithoutPreOrderStatusEventsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null userType?: UserTypeUpdateOneWithoutAdminUsersNestedInput branch?: BranchUpdateOneWithoutAdminUsersNestedInput + avatarFile?: UploadedFileUpdateOneWithoutAdminUserAvatarsNestedInput referredCustomers?: CustomerUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUpdateManyWithoutAllocatorAdminUserNestedInput @@ -57959,11 +62799,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUpdateOneWithoutAdminUserNestedInput } export type AdminUserUncheckedUpdateWithoutPreOrderStatusEventsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole @@ -57972,6 +62815,11 @@ export namespace Prisma { wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null referredCustomers?: CustomerUncheckedUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUncheckedUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUncheckedUpdateManyWithoutAllocatorAdminUserNestedInput @@ -57980,11 +62828,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUncheckedUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUncheckedUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUncheckedUpdateOneWithoutAdminUserNestedInput } export type CustomerCreateWithoutLedgerEntriesInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -57998,6 +62849,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -58009,6 +62861,7 @@ export namespace Prisma { updatedAt?: Date | string city?: CityCreateNestedOneWithoutCustomersInput referredByAgent?: AdminUserCreateNestedOneWithoutReferredCustomersInput + salesExpertTradingFor?: SalesExpertProfileCreateNestedOneWithoutTradingCustomerInput avatarFile?: UploadedFileCreateNestedOneWithoutCustomerAvatarsInput addresses?: CustomerAddressCreateNestedManyWithoutCustomerInput orders?: OrderCreateNestedManyWithoutCustomerInput @@ -58018,11 +62871,14 @@ export namespace Prisma { promotions?: PromotionCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpCreateNestedManyWithoutCustomerInput } export type CustomerUncheckedCreateWithoutLedgerEntriesInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -58038,6 +62894,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -58048,6 +62905,7 @@ export namespace Prisma { isArchived?: boolean createdAt?: Date | string updatedAt?: Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedCreateNestedOneWithoutTradingCustomerInput addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCustomerInput orders?: OrderUncheckedCreateNestedManyWithoutCustomerInput preOrders?: PreOrderUncheckedCreateNestedManyWithoutCustomerInput @@ -58056,6 +62914,7 @@ export namespace Prisma { promotions?: PromotionUncheckedCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientUncheckedCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogUncheckedCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpUncheckedCreateNestedManyWithoutCustomerInput } export type CustomerCreateOrConnectWithoutLedgerEntriesInput = { @@ -58109,14 +62968,21 @@ export namespace Prisma { export type AdminUserCreateWithoutPostedLedgerEntriesInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null userType?: UserTypeCreateNestedOneWithoutAdminUsersInput branch?: BranchCreateNestedOneWithoutAdminUsersInput + avatarFile?: UploadedFileCreateNestedOneWithoutAdminUserAvatarsInput referredCustomers?: CustomerCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationCreateNestedManyWithoutAllocatorAdminUserInput @@ -58125,11 +62991,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileCreateNestedOneWithoutAdminUserInput } export type AdminUserUncheckedCreateWithoutPostedLedgerEntriesInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole @@ -58138,6 +63007,11 @@ export namespace Prisma { wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + avatarFileId?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null referredCustomers?: CustomerUncheckedCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopUncheckedCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationUncheckedCreateNestedManyWithoutAllocatorAdminUserInput @@ -58146,6 +63020,7 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationUncheckedCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileUncheckedCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileUncheckedCreateNestedOneWithoutAdminUserInput } export type AdminUserCreateOrConnectWithoutPostedLedgerEntriesInput = { @@ -58167,6 +63042,8 @@ export namespace Prisma { export type CustomerUpdateWithoutLedgerEntriesInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -58180,6 +63057,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -58191,6 +63069,7 @@ export namespace Prisma { updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string city?: CityUpdateOneWithoutCustomersNestedInput referredByAgent?: AdminUserUpdateOneWithoutReferredCustomersNestedInput + salesExpertTradingFor?: SalesExpertProfileUpdateOneWithoutTradingCustomerNestedInput avatarFile?: UploadedFileUpdateOneWithoutCustomerAvatarsNestedInput addresses?: CustomerAddressUpdateManyWithoutCustomerNestedInput orders?: OrderUpdateManyWithoutCustomerNestedInput @@ -58200,11 +63079,14 @@ export namespace Prisma { promotions?: PromotionUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUpdateManyWithoutCustomerNestedInput } export type CustomerUncheckedUpdateWithoutLedgerEntriesInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -58220,6 +63102,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -58230,6 +63113,7 @@ export namespace Prisma { isArchived?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedUpdateOneWithoutTradingCustomerNestedInput addresses?: CustomerAddressUncheckedUpdateManyWithoutCustomerNestedInput orders?: OrderUncheckedUpdateManyWithoutCustomerNestedInput preOrders?: PreOrderUncheckedUpdateManyWithoutCustomerNestedInput @@ -58238,6 +63122,7 @@ export namespace Prisma { promotions?: PromotionUncheckedUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUncheckedUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUncheckedUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUncheckedUpdateManyWithoutCustomerNestedInput } export type OrderUpsertWithoutLedgerEntriesInput = { @@ -58303,14 +63188,21 @@ export namespace Prisma { export type AdminUserUpdateWithoutPostedLedgerEntriesInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null userType?: UserTypeUpdateOneWithoutAdminUsersNestedInput branch?: BranchUpdateOneWithoutAdminUsersNestedInput + avatarFile?: UploadedFileUpdateOneWithoutAdminUserAvatarsNestedInput referredCustomers?: CustomerUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUpdateManyWithoutAllocatorAdminUserNestedInput @@ -58319,11 +63211,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUpdateOneWithoutAdminUserNestedInput } export type AdminUserUncheckedUpdateWithoutPostedLedgerEntriesInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole @@ -58332,6 +63227,11 @@ export namespace Prisma { wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null referredCustomers?: CustomerUncheckedUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUncheckedUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUncheckedUpdateManyWithoutAllocatorAdminUserNestedInput @@ -58340,11 +63240,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUncheckedUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUncheckedUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUncheckedUpdateOneWithoutAdminUserNestedInput } export type CustomerCreateWithoutWalletBonusesInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -58358,6 +63261,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -58369,6 +63273,7 @@ export namespace Prisma { updatedAt?: Date | string city?: CityCreateNestedOneWithoutCustomersInput referredByAgent?: AdminUserCreateNestedOneWithoutReferredCustomersInput + salesExpertTradingFor?: SalesExpertProfileCreateNestedOneWithoutTradingCustomerInput avatarFile?: UploadedFileCreateNestedOneWithoutCustomerAvatarsInput addresses?: CustomerAddressCreateNestedManyWithoutCustomerInput orders?: OrderCreateNestedManyWithoutCustomerInput @@ -58378,11 +63283,14 @@ export namespace Prisma { promotions?: PromotionCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpCreateNestedManyWithoutCustomerInput } export type CustomerUncheckedCreateWithoutWalletBonusesInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -58398,6 +63306,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -58408,6 +63317,7 @@ export namespace Prisma { isArchived?: boolean createdAt?: Date | string updatedAt?: Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedCreateNestedOneWithoutTradingCustomerInput addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCustomerInput orders?: OrderUncheckedCreateNestedManyWithoutCustomerInput preOrders?: PreOrderUncheckedCreateNestedManyWithoutCustomerInput @@ -58416,6 +63326,7 @@ export namespace Prisma { promotions?: PromotionUncheckedCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientUncheckedCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogUncheckedCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpUncheckedCreateNestedManyWithoutCustomerInput } export type CustomerCreateOrConnectWithoutWalletBonusesInput = { @@ -58437,6 +63348,8 @@ export namespace Prisma { export type CustomerUpdateWithoutWalletBonusesInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -58450,6 +63363,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -58461,6 +63375,7 @@ export namespace Prisma { updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string city?: CityUpdateOneWithoutCustomersNestedInput referredByAgent?: AdminUserUpdateOneWithoutReferredCustomersNestedInput + salesExpertTradingFor?: SalesExpertProfileUpdateOneWithoutTradingCustomerNestedInput avatarFile?: UploadedFileUpdateOneWithoutCustomerAvatarsNestedInput addresses?: CustomerAddressUpdateManyWithoutCustomerNestedInput orders?: OrderUpdateManyWithoutCustomerNestedInput @@ -58470,11 +63385,14 @@ export namespace Prisma { promotions?: PromotionUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUpdateManyWithoutCustomerNestedInput } export type CustomerUncheckedUpdateWithoutWalletBonusesInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -58490,6 +63408,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -58500,6 +63419,7 @@ export namespace Prisma { isArchived?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedUpdateOneWithoutTradingCustomerNestedInput addresses?: CustomerAddressUncheckedUpdateManyWithoutCustomerNestedInput orders?: OrderUncheckedUpdateManyWithoutCustomerNestedInput preOrders?: PreOrderUncheckedUpdateManyWithoutCustomerNestedInput @@ -58508,6 +63428,7 @@ export namespace Prisma { promotions?: PromotionUncheckedUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUncheckedUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUncheckedUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUncheckedUpdateManyWithoutCustomerNestedInput } export type PermissionCreateWithoutUserTypeInput = { @@ -58531,13 +63452,20 @@ export namespace Prisma { export type AdminUserCreateWithoutUserTypeInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null branch?: BranchCreateNestedOneWithoutAdminUsersInput + avatarFile?: UploadedFileCreateNestedOneWithoutAdminUserAvatarsInput referredCustomers?: CustomerCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationCreateNestedManyWithoutAllocatorAdminUserInput @@ -58547,11 +63475,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileCreateNestedOneWithoutAdminUserInput } export type AdminUserUncheckedCreateWithoutUserTypeInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole @@ -58559,6 +63490,11 @@ export namespace Prisma { wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + avatarFileId?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null referredCustomers?: CustomerUncheckedCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopUncheckedCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationUncheckedCreateNestedManyWithoutAllocatorAdminUserInput @@ -58568,6 +63504,7 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationUncheckedCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileUncheckedCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileUncheckedCreateNestedOneWithoutAdminUserInput } export type AdminUserCreateOrConnectWithoutUserTypeInput = { @@ -58710,6 +63647,8 @@ export namespace Prisma { export type CustomerCreateWithoutCommentsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -58723,6 +63662,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -58734,6 +63674,7 @@ export namespace Prisma { updatedAt?: Date | string city?: CityCreateNestedOneWithoutCustomersInput referredByAgent?: AdminUserCreateNestedOneWithoutReferredCustomersInput + salesExpertTradingFor?: SalesExpertProfileCreateNestedOneWithoutTradingCustomerInput avatarFile?: UploadedFileCreateNestedOneWithoutCustomerAvatarsInput addresses?: CustomerAddressCreateNestedManyWithoutCustomerInput orders?: OrderCreateNestedManyWithoutCustomerInput @@ -58743,11 +63684,14 @@ export namespace Prisma { promotions?: PromotionCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpCreateNestedManyWithoutCustomerInput } export type CustomerUncheckedCreateWithoutCommentsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -58763,6 +63707,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -58773,6 +63718,7 @@ export namespace Prisma { isArchived?: boolean createdAt?: Date | string updatedAt?: Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedCreateNestedOneWithoutTradingCustomerInput addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCustomerInput orders?: OrderUncheckedCreateNestedManyWithoutCustomerInput preOrders?: PreOrderUncheckedCreateNestedManyWithoutCustomerInput @@ -58781,6 +63727,7 @@ export namespace Prisma { promotions?: PromotionUncheckedCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientUncheckedCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogUncheckedCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpUncheckedCreateNestedManyWithoutCustomerInput } export type CustomerCreateOrConnectWithoutCommentsInput = { @@ -58906,6 +63853,8 @@ export namespace Prisma { export type CustomerUpdateWithoutCommentsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -58919,6 +63868,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -58930,6 +63880,7 @@ export namespace Prisma { updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string city?: CityUpdateOneWithoutCustomersNestedInput referredByAgent?: AdminUserUpdateOneWithoutReferredCustomersNestedInput + salesExpertTradingFor?: SalesExpertProfileUpdateOneWithoutTradingCustomerNestedInput avatarFile?: UploadedFileUpdateOneWithoutCustomerAvatarsNestedInput addresses?: CustomerAddressUpdateManyWithoutCustomerNestedInput orders?: OrderUpdateManyWithoutCustomerNestedInput @@ -58939,11 +63890,14 @@ export namespace Prisma { promotions?: PromotionUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUpdateManyWithoutCustomerNestedInput } export type CustomerUncheckedUpdateWithoutCommentsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -58959,6 +63913,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -58969,6 +63924,7 @@ export namespace Prisma { isArchived?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedUpdateOneWithoutTradingCustomerNestedInput addresses?: CustomerAddressUncheckedUpdateManyWithoutCustomerNestedInput orders?: OrderUncheckedUpdateManyWithoutCustomerNestedInput preOrders?: PreOrderUncheckedUpdateManyWithoutCustomerNestedInput @@ -58977,6 +63933,7 @@ export namespace Prisma { promotions?: PromotionUncheckedUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUncheckedUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUncheckedUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUncheckedUpdateManyWithoutCustomerNestedInput } export type CommentUpsertWithoutRepliesInput = { @@ -59101,6 +64058,8 @@ export namespace Prisma { export type CustomerCreateWithoutPromotionsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -59114,6 +64073,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -59125,6 +64085,7 @@ export namespace Prisma { updatedAt?: Date | string city?: CityCreateNestedOneWithoutCustomersInput referredByAgent?: AdminUserCreateNestedOneWithoutReferredCustomersInput + salesExpertTradingFor?: SalesExpertProfileCreateNestedOneWithoutTradingCustomerInput avatarFile?: UploadedFileCreateNestedOneWithoutCustomerAvatarsInput addresses?: CustomerAddressCreateNestedManyWithoutCustomerInput orders?: OrderCreateNestedManyWithoutCustomerInput @@ -59134,11 +64095,14 @@ export namespace Prisma { comments?: CommentCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpCreateNestedManyWithoutCustomerInput } export type CustomerUncheckedCreateWithoutPromotionsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -59154,6 +64118,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -59164,6 +64129,7 @@ export namespace Prisma { isArchived?: boolean createdAt?: Date | string updatedAt?: Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedCreateNestedOneWithoutTradingCustomerInput addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCustomerInput orders?: OrderUncheckedCreateNestedManyWithoutCustomerInput preOrders?: PreOrderUncheckedCreateNestedManyWithoutCustomerInput @@ -59172,6 +64138,7 @@ export namespace Prisma { comments?: CommentUncheckedCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientUncheckedCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogUncheckedCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpUncheckedCreateNestedManyWithoutCustomerInput } export type CustomerCreateOrConnectWithoutPromotionsInput = { @@ -59292,6 +64259,8 @@ export namespace Prisma { export type CustomerUpdateWithoutPromotionsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -59305,6 +64274,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -59316,6 +64286,7 @@ export namespace Prisma { updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string city?: CityUpdateOneWithoutCustomersNestedInput referredByAgent?: AdminUserUpdateOneWithoutReferredCustomersNestedInput + salesExpertTradingFor?: SalesExpertProfileUpdateOneWithoutTradingCustomerNestedInput avatarFile?: UploadedFileUpdateOneWithoutCustomerAvatarsNestedInput addresses?: CustomerAddressUpdateManyWithoutCustomerNestedInput orders?: OrderUpdateManyWithoutCustomerNestedInput @@ -59325,11 +64296,14 @@ export namespace Prisma { comments?: CommentUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUpdateManyWithoutCustomerNestedInput } export type CustomerUncheckedUpdateWithoutPromotionsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -59345,6 +64319,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -59355,6 +64330,7 @@ export namespace Prisma { isArchived?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedUpdateOneWithoutTradingCustomerNestedInput addresses?: CustomerAddressUncheckedUpdateManyWithoutCustomerNestedInput orders?: OrderUncheckedUpdateManyWithoutCustomerNestedInput preOrders?: PreOrderUncheckedUpdateManyWithoutCustomerNestedInput @@ -59363,6 +64339,7 @@ export namespace Prisma { comments?: CommentUncheckedUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUncheckedUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUncheckedUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUncheckedUpdateManyWithoutCustomerNestedInput } export type BrandUpsertWithoutPromotionsInput = { @@ -59418,14 +64395,21 @@ export namespace Prisma { export type AdminUserCreateWithoutBranchAllocationsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null userType?: UserTypeCreateNestedOneWithoutAdminUsersInput branch?: BranchCreateNestedOneWithoutAdminUsersInput + avatarFile?: UploadedFileCreateNestedOneWithoutAdminUserAvatarsInput referredCustomers?: CustomerCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopCreateNestedManyWithoutSupervisorInput postedLedgerEntries?: LedgerEntryCreateNestedManyWithoutPostedByAdminUserInput @@ -59434,11 +64418,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileCreateNestedOneWithoutAdminUserInput } export type AdminUserUncheckedCreateWithoutBranchAllocationsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole @@ -59447,6 +64434,11 @@ export namespace Prisma { wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + avatarFileId?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null referredCustomers?: CustomerUncheckedCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopUncheckedCreateNestedManyWithoutSupervisorInput postedLedgerEntries?: LedgerEntryUncheckedCreateNestedManyWithoutPostedByAdminUserInput @@ -59455,6 +64447,7 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationUncheckedCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileUncheckedCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileUncheckedCreateNestedOneWithoutAdminUserInput } export type AdminUserCreateOrConnectWithoutBranchAllocationsInput = { @@ -59531,14 +64524,21 @@ export namespace Prisma { export type AdminUserUpdateWithoutBranchAllocationsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null userType?: UserTypeUpdateOneWithoutAdminUsersNestedInput branch?: BranchUpdateOneWithoutAdminUsersNestedInput + avatarFile?: UploadedFileUpdateOneWithoutAdminUserAvatarsNestedInput referredCustomers?: CustomerUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUpdateManyWithoutSupervisorNestedInput postedLedgerEntries?: LedgerEntryUpdateManyWithoutPostedByAdminUserNestedInput @@ -59547,11 +64547,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUpdateOneWithoutAdminUserNestedInput } export type AdminUserUncheckedUpdateWithoutBranchAllocationsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole @@ -59560,6 +64563,11 @@ export namespace Prisma { wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null referredCustomers?: CustomerUncheckedUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUncheckedUpdateManyWithoutSupervisorNestedInput postedLedgerEntries?: LedgerEntryUncheckedUpdateManyWithoutPostedByAdminUserNestedInput @@ -59568,6 +64576,7 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUncheckedUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUncheckedUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUncheckedUpdateOneWithoutAdminUserNestedInput } export type BranchAllocationItemUpsertWithWhereUniqueWithoutAllocationInput = { @@ -59721,14 +64730,21 @@ export namespace Prisma { export type AdminUserCreateWithoutSentSmsMessagesInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null userType?: UserTypeCreateNestedOneWithoutAdminUsersInput branch?: BranchCreateNestedOneWithoutAdminUsersInput + avatarFile?: UploadedFileCreateNestedOneWithoutAdminUserAvatarsInput referredCustomers?: CustomerCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationCreateNestedManyWithoutAllocatorAdminUserInput @@ -59737,11 +64753,14 @@ export namespace Prisma { preOrderStatusEvents?: PreOrderStatusEventCreateNestedManyWithoutActorAdminUserInput sentNotifications?: NotificationCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileCreateNestedOneWithoutAdminUserInput } export type AdminUserUncheckedCreateWithoutSentSmsMessagesInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole @@ -59750,6 +64769,11 @@ export namespace Prisma { wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + avatarFileId?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null referredCustomers?: CustomerUncheckedCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopUncheckedCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationUncheckedCreateNestedManyWithoutAllocatorAdminUserInput @@ -59758,6 +64782,7 @@ export namespace Prisma { preOrderStatusEvents?: PreOrderStatusEventUncheckedCreateNestedManyWithoutActorAdminUserInput sentNotifications?: NotificationUncheckedCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileUncheckedCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileUncheckedCreateNestedOneWithoutAdminUserInput } export type AdminUserCreateOrConnectWithoutSentSmsMessagesInput = { @@ -59779,14 +64804,21 @@ export namespace Prisma { export type AdminUserUpdateWithoutSentSmsMessagesInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null userType?: UserTypeUpdateOneWithoutAdminUsersNestedInput branch?: BranchUpdateOneWithoutAdminUsersNestedInput + avatarFile?: UploadedFileUpdateOneWithoutAdminUserAvatarsNestedInput referredCustomers?: CustomerUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUpdateManyWithoutAllocatorAdminUserNestedInput @@ -59795,11 +64827,14 @@ export namespace Prisma { preOrderStatusEvents?: PreOrderStatusEventUpdateManyWithoutActorAdminUserNestedInput sentNotifications?: NotificationUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUpdateOneWithoutAdminUserNestedInput } export type AdminUserUncheckedUpdateWithoutSentSmsMessagesInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole @@ -59808,6 +64843,11 @@ export namespace Prisma { wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null referredCustomers?: CustomerUncheckedUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUncheckedUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUncheckedUpdateManyWithoutAllocatorAdminUserNestedInput @@ -59816,19 +64856,27 @@ export namespace Prisma { preOrderStatusEvents?: PreOrderStatusEventUncheckedUpdateManyWithoutActorAdminUserNestedInput sentNotifications?: NotificationUncheckedUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUncheckedUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUncheckedUpdateOneWithoutAdminUserNestedInput } export type AdminUserCreateWithoutSentNotificationsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null userType?: UserTypeCreateNestedOneWithoutAdminUsersInput branch?: BranchCreateNestedOneWithoutAdminUsersInput + avatarFile?: UploadedFileCreateNestedOneWithoutAdminUserAvatarsInput referredCustomers?: CustomerCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationCreateNestedManyWithoutAllocatorAdminUserInput @@ -59837,11 +64885,14 @@ export namespace Prisma { preOrderStatusEvents?: PreOrderStatusEventCreateNestedManyWithoutActorAdminUserInput sentSmsMessages?: SmsMessageCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileCreateNestedOneWithoutAdminUserInput } export type AdminUserUncheckedCreateWithoutSentNotificationsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole @@ -59850,6 +64901,11 @@ export namespace Prisma { wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + avatarFileId?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null referredCustomers?: CustomerUncheckedCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopUncheckedCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationUncheckedCreateNestedManyWithoutAllocatorAdminUserInput @@ -59858,6 +64914,7 @@ export namespace Prisma { preOrderStatusEvents?: PreOrderStatusEventUncheckedCreateNestedManyWithoutActorAdminUserInput sentSmsMessages?: SmsMessageUncheckedCreateNestedManyWithoutSentByAdminUserInput uploadedFiles?: UploadedFileUncheckedCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileUncheckedCreateNestedOneWithoutAdminUserInput } export type AdminUserCreateOrConnectWithoutSentNotificationsInput = { @@ -59901,14 +64958,21 @@ export namespace Prisma { export type AdminUserUpdateWithoutSentNotificationsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null userType?: UserTypeUpdateOneWithoutAdminUsersNestedInput branch?: BranchUpdateOneWithoutAdminUsersNestedInput + avatarFile?: UploadedFileUpdateOneWithoutAdminUserAvatarsNestedInput referredCustomers?: CustomerUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUpdateManyWithoutAllocatorAdminUserNestedInput @@ -59917,11 +64981,14 @@ export namespace Prisma { preOrderStatusEvents?: PreOrderStatusEventUpdateManyWithoutActorAdminUserNestedInput sentSmsMessages?: SmsMessageUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUpdateOneWithoutAdminUserNestedInput } export type AdminUserUncheckedUpdateWithoutSentNotificationsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole @@ -59930,6 +64997,11 @@ export namespace Prisma { wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null referredCustomers?: CustomerUncheckedUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUncheckedUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUncheckedUpdateManyWithoutAllocatorAdminUserNestedInput @@ -59938,6 +65010,7 @@ export namespace Prisma { preOrderStatusEvents?: PreOrderStatusEventUncheckedUpdateManyWithoutActorAdminUserNestedInput sentSmsMessages?: SmsMessageUncheckedUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUncheckedUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUncheckedUpdateOneWithoutAdminUserNestedInput } export type NotificationRecipientUpsertWithWhereUniqueWithoutNotificationInput = { @@ -59982,6 +65055,8 @@ export namespace Prisma { export type CustomerCreateWithoutNotificationsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -59995,6 +65070,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -60006,6 +65082,7 @@ export namespace Prisma { updatedAt?: Date | string city?: CityCreateNestedOneWithoutCustomersInput referredByAgent?: AdminUserCreateNestedOneWithoutReferredCustomersInput + salesExpertTradingFor?: SalesExpertProfileCreateNestedOneWithoutTradingCustomerInput avatarFile?: UploadedFileCreateNestedOneWithoutCustomerAvatarsInput addresses?: CustomerAddressCreateNestedManyWithoutCustomerInput orders?: OrderCreateNestedManyWithoutCustomerInput @@ -60015,11 +65092,14 @@ export namespace Prisma { comments?: CommentCreateNestedManyWithoutCustomerInput promotions?: PromotionCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpCreateNestedManyWithoutCustomerInput } export type CustomerUncheckedCreateWithoutNotificationsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -60035,6 +65115,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -60045,6 +65126,7 @@ export namespace Prisma { isArchived?: boolean createdAt?: Date | string updatedAt?: Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedCreateNestedOneWithoutTradingCustomerInput addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCustomerInput orders?: OrderUncheckedCreateNestedManyWithoutCustomerInput preOrders?: PreOrderUncheckedCreateNestedManyWithoutCustomerInput @@ -60053,6 +65135,7 @@ export namespace Prisma { comments?: CommentUncheckedCreateNestedManyWithoutCustomerInput promotions?: PromotionUncheckedCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogUncheckedCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpUncheckedCreateNestedManyWithoutCustomerInput } export type CustomerCreateOrConnectWithoutNotificationsInput = { @@ -60103,6 +65186,8 @@ export namespace Prisma { export type CustomerUpdateWithoutNotificationsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -60116,6 +65201,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -60127,6 +65213,7 @@ export namespace Prisma { updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string city?: CityUpdateOneWithoutCustomersNestedInput referredByAgent?: AdminUserUpdateOneWithoutReferredCustomersNestedInput + salesExpertTradingFor?: SalesExpertProfileUpdateOneWithoutTradingCustomerNestedInput avatarFile?: UploadedFileUpdateOneWithoutCustomerAvatarsNestedInput addresses?: CustomerAddressUpdateManyWithoutCustomerNestedInput orders?: OrderUpdateManyWithoutCustomerNestedInput @@ -60136,11 +65223,14 @@ export namespace Prisma { comments?: CommentUpdateManyWithoutCustomerNestedInput promotions?: PromotionUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUpdateManyWithoutCustomerNestedInput } export type CustomerUncheckedUpdateWithoutNotificationsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -60156,6 +65246,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -60166,6 +65257,7 @@ export namespace Prisma { isArchived?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedUpdateOneWithoutTradingCustomerNestedInput addresses?: CustomerAddressUncheckedUpdateManyWithoutCustomerNestedInput orders?: OrderUncheckedUpdateManyWithoutCustomerNestedInput preOrders?: PreOrderUncheckedUpdateManyWithoutCustomerNestedInput @@ -60174,11 +65266,14 @@ export namespace Prisma { comments?: CommentUncheckedUpdateManyWithoutCustomerNestedInput promotions?: PromotionUncheckedUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUncheckedUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUncheckedUpdateManyWithoutCustomerNestedInput } export type CustomerCreateWithoutActivityLogsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -60192,6 +65287,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -60203,6 +65299,7 @@ export namespace Prisma { updatedAt?: Date | string city?: CityCreateNestedOneWithoutCustomersInput referredByAgent?: AdminUserCreateNestedOneWithoutReferredCustomersInput + salesExpertTradingFor?: SalesExpertProfileCreateNestedOneWithoutTradingCustomerInput avatarFile?: UploadedFileCreateNestedOneWithoutCustomerAvatarsInput addresses?: CustomerAddressCreateNestedManyWithoutCustomerInput orders?: OrderCreateNestedManyWithoutCustomerInput @@ -60212,11 +65309,14 @@ export namespace Prisma { comments?: CommentCreateNestedManyWithoutCustomerInput promotions?: PromotionCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpCreateNestedManyWithoutCustomerInput } export type CustomerUncheckedCreateWithoutActivityLogsInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -60232,6 +65332,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -60242,6 +65343,7 @@ export namespace Prisma { isArchived?: boolean createdAt?: Date | string updatedAt?: Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedCreateNestedOneWithoutTradingCustomerInput addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCustomerInput orders?: OrderUncheckedCreateNestedManyWithoutCustomerInput preOrders?: PreOrderUncheckedCreateNestedManyWithoutCustomerInput @@ -60250,6 +65352,7 @@ export namespace Prisma { comments?: CommentUncheckedCreateNestedManyWithoutCustomerInput promotions?: PromotionUncheckedCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientUncheckedCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpUncheckedCreateNestedManyWithoutCustomerInput } export type CustomerCreateOrConnectWithoutActivityLogsInput = { @@ -60271,6 +65374,8 @@ export namespace Prisma { export type CustomerUpdateWithoutActivityLogsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -60284,6 +65389,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -60295,6 +65401,7 @@ export namespace Prisma { updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string city?: CityUpdateOneWithoutCustomersNestedInput referredByAgent?: AdminUserUpdateOneWithoutReferredCustomersNestedInput + salesExpertTradingFor?: SalesExpertProfileUpdateOneWithoutTradingCustomerNestedInput avatarFile?: UploadedFileUpdateOneWithoutCustomerAvatarsNestedInput addresses?: CustomerAddressUpdateManyWithoutCustomerNestedInput orders?: OrderUpdateManyWithoutCustomerNestedInput @@ -60304,11 +65411,14 @@ export namespace Prisma { comments?: CommentUpdateManyWithoutCustomerNestedInput promotions?: PromotionUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUpdateManyWithoutCustomerNestedInput } export type CustomerUncheckedUpdateWithoutActivityLogsInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -60324,6 +65434,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -60334,6 +65445,7 @@ export namespace Prisma { isArchived?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedUpdateOneWithoutTradingCustomerNestedInput addresses?: CustomerAddressUncheckedUpdateManyWithoutCustomerNestedInput orders?: OrderUncheckedUpdateManyWithoutCustomerNestedInput preOrders?: PreOrderUncheckedUpdateManyWithoutCustomerNestedInput @@ -60342,19 +65454,27 @@ export namespace Prisma { comments?: CommentUncheckedUpdateManyWithoutCustomerNestedInput promotions?: PromotionUncheckedUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUncheckedUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUncheckedUpdateManyWithoutCustomerNestedInput } export type AdminUserCreateWithoutUploadedFilesInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null userType?: UserTypeCreateNestedOneWithoutAdminUsersInput branch?: BranchCreateNestedOneWithoutAdminUsersInput + avatarFile?: UploadedFileCreateNestedOneWithoutAdminUserAvatarsInput referredCustomers?: CustomerCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationCreateNestedManyWithoutAllocatorAdminUserInput @@ -60363,11 +65483,14 @@ export namespace Prisma { preOrderStatusEvents?: PreOrderStatusEventCreateNestedManyWithoutActorAdminUserInput sentSmsMessages?: SmsMessageCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationCreateNestedManyWithoutSentByAdminUserInput + salesExpertProfile?: SalesExpertProfileCreateNestedOneWithoutAdminUserInput } export type AdminUserUncheckedCreateWithoutUploadedFilesInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole @@ -60376,6 +65499,11 @@ export namespace Prisma { wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + avatarFileId?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null referredCustomers?: CustomerUncheckedCreateNestedManyWithoutReferredByAgentInput supervisedWorkshops?: WorkshopUncheckedCreateNestedManyWithoutSupervisorInput branchAllocations?: BranchAllocationUncheckedCreateNestedManyWithoutAllocatorAdminUserInput @@ -60384,6 +65512,7 @@ export namespace Prisma { preOrderStatusEvents?: PreOrderStatusEventUncheckedCreateNestedManyWithoutActorAdminUserInput sentSmsMessages?: SmsMessageUncheckedCreateNestedManyWithoutSentByAdminUserInput sentNotifications?: NotificationUncheckedCreateNestedManyWithoutSentByAdminUserInput + salesExpertProfile?: SalesExpertProfileUncheckedCreateNestedOneWithoutAdminUserInput } export type AdminUserCreateOrConnectWithoutUploadedFilesInput = { @@ -60394,6 +65523,8 @@ export namespace Prisma { export type CustomerCreateWithoutAvatarFileInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -60407,6 +65538,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -60418,6 +65550,7 @@ export namespace Prisma { updatedAt?: Date | string city?: CityCreateNestedOneWithoutCustomersInput referredByAgent?: AdminUserCreateNestedOneWithoutReferredCustomersInput + salesExpertTradingFor?: SalesExpertProfileCreateNestedOneWithoutTradingCustomerInput addresses?: CustomerAddressCreateNestedManyWithoutCustomerInput orders?: OrderCreateNestedManyWithoutCustomerInput preOrders?: PreOrderCreateNestedManyWithoutCustomerInput @@ -60427,11 +65560,14 @@ export namespace Prisma { promotions?: PromotionCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpCreateNestedManyWithoutCustomerInput } export type CustomerUncheckedCreateWithoutAvatarFileInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -60447,6 +65583,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -60456,6 +65593,7 @@ export namespace Prisma { isArchived?: boolean createdAt?: Date | string updatedAt?: Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedCreateNestedOneWithoutTradingCustomerInput addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCustomerInput orders?: OrderUncheckedCreateNestedManyWithoutCustomerInput preOrders?: PreOrderUncheckedCreateNestedManyWithoutCustomerInput @@ -60465,6 +65603,7 @@ export namespace Prisma { promotions?: PromotionUncheckedCreateNestedManyWithoutCustomerInput notifications?: NotificationRecipientUncheckedCreateNestedManyWithoutCustomerInput activityLogs?: CustomerActivityLogUncheckedCreateNestedManyWithoutCustomerInput + otpChallenges?: CustomerOtpUncheckedCreateNestedManyWithoutCustomerInput } export type CustomerCreateOrConnectWithoutAvatarFileInput = { @@ -60477,6 +65616,74 @@ export namespace Prisma { skipDuplicates?: boolean } + export type AdminUserCreateWithoutAvatarFileInput = { + id?: string + fullName: string + firstName?: string | null + lastName?: string | null + mobile: string + passwordHash: string + role: $Enums.AdminRole + wagePercent?: Decimal | DecimalJsLike | number | string | null + isActive?: boolean + createdAt?: Date | string + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null + userType?: UserTypeCreateNestedOneWithoutAdminUsersInput + branch?: BranchCreateNestedOneWithoutAdminUsersInput + referredCustomers?: CustomerCreateNestedManyWithoutReferredByAgentInput + supervisedWorkshops?: WorkshopCreateNestedManyWithoutSupervisorInput + branchAllocations?: BranchAllocationCreateNestedManyWithoutAllocatorAdminUserInput + postedLedgerEntries?: LedgerEntryCreateNestedManyWithoutPostedByAdminUserInput + orderStatusEvents?: OrderStatusEventCreateNestedManyWithoutActorAdminUserInput + preOrderStatusEvents?: PreOrderStatusEventCreateNestedManyWithoutActorAdminUserInput + sentSmsMessages?: SmsMessageCreateNestedManyWithoutSentByAdminUserInput + sentNotifications?: NotificationCreateNestedManyWithoutSentByAdminUserInput + uploadedFiles?: UploadedFileCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileCreateNestedOneWithoutAdminUserInput + } + + export type AdminUserUncheckedCreateWithoutAvatarFileInput = { + id?: string + fullName: string + firstName?: string | null + lastName?: string | null + mobile: string + passwordHash: string + role: $Enums.AdminRole + userTypeId?: number | null + branchId?: string | null + wagePercent?: Decimal | DecimalJsLike | number | string | null + isActive?: boolean + createdAt?: Date | string + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null + referredCustomers?: CustomerUncheckedCreateNestedManyWithoutReferredByAgentInput + supervisedWorkshops?: WorkshopUncheckedCreateNestedManyWithoutSupervisorInput + branchAllocations?: BranchAllocationUncheckedCreateNestedManyWithoutAllocatorAdminUserInput + postedLedgerEntries?: LedgerEntryUncheckedCreateNestedManyWithoutPostedByAdminUserInput + orderStatusEvents?: OrderStatusEventUncheckedCreateNestedManyWithoutActorAdminUserInput + preOrderStatusEvents?: PreOrderStatusEventUncheckedCreateNestedManyWithoutActorAdminUserInput + sentSmsMessages?: SmsMessageUncheckedCreateNestedManyWithoutSentByAdminUserInput + sentNotifications?: NotificationUncheckedCreateNestedManyWithoutSentByAdminUserInput + uploadedFiles?: UploadedFileUncheckedCreateNestedManyWithoutUploadedByAdminUserInput + salesExpertProfile?: SalesExpertProfileUncheckedCreateNestedOneWithoutAdminUserInput + } + + export type AdminUserCreateOrConnectWithoutAvatarFileInput = { + where: AdminUserWhereUniqueInput + create: XOR + } + + export type AdminUserCreateManyAvatarFileInputEnvelope = { + data: AdminUserCreateManyAvatarFileInput | AdminUserCreateManyAvatarFileInput[] + skipDuplicates?: boolean + } + export type AdminUserUpsertWithoutUploadedFilesInput = { update: XOR create: XOR @@ -60491,14 +65698,21 @@ export namespace Prisma { export type AdminUserUpdateWithoutUploadedFilesInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null userType?: UserTypeUpdateOneWithoutAdminUsersNestedInput branch?: BranchUpdateOneWithoutAdminUsersNestedInput + avatarFile?: UploadedFileUpdateOneWithoutAdminUserAvatarsNestedInput referredCustomers?: CustomerUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUpdateManyWithoutAllocatorAdminUserNestedInput @@ -60507,11 +65721,14 @@ export namespace Prisma { preOrderStatusEvents?: PreOrderStatusEventUpdateManyWithoutActorAdminUserNestedInput sentSmsMessages?: SmsMessageUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUpdateManyWithoutSentByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUpdateOneWithoutAdminUserNestedInput } export type AdminUserUncheckedUpdateWithoutUploadedFilesInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole @@ -60520,6 +65737,11 @@ export namespace Prisma { wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null referredCustomers?: CustomerUncheckedUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUncheckedUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUncheckedUpdateManyWithoutAllocatorAdminUserNestedInput @@ -60528,6 +65750,7 @@ export namespace Prisma { preOrderStatusEvents?: PreOrderStatusEventUncheckedUpdateManyWithoutActorAdminUserNestedInput sentSmsMessages?: SmsMessageUncheckedUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUncheckedUpdateManyWithoutSentByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUncheckedUpdateOneWithoutAdminUserNestedInput } export type CustomerUpsertWithWhereUniqueWithoutAvatarFileInput = { @@ -60546,9 +65769,215 @@ export namespace Prisma { data: XOR } + export type AdminUserUpsertWithWhereUniqueWithoutAvatarFileInput = { + where: AdminUserWhereUniqueInput + update: XOR + create: XOR + } + + export type AdminUserUpdateWithWhereUniqueWithoutAvatarFileInput = { + where: AdminUserWhereUniqueInput + data: XOR + } + + export type AdminUserUpdateManyWithWhereWithoutAvatarFileInput = { + where: AdminUserScalarWhereInput + data: XOR + } + + export type CustomerCreateWithoutOtpChallengesInput = { + id?: string + fullName: string + firstName?: string | null + lastName?: string | null + mobile: string + nationalCode: string + passwordHash: string + kind?: $Enums.CustomerKind + phone?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + zone?: string | null + memberCode?: string | null + goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string + goldDebtGrams?: Decimal | DecimalJsLike | number | string + fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string + verificationStep?: $Enums.VerificationStep + nationalIdImageUrl?: string | null + businessLicenseUrl?: string | null + shopImageUrl?: string | null + canPlaceOrders?: boolean + isActive?: boolean + isArchived?: boolean + createdAt?: Date | string + updatedAt?: Date | string + city?: CityCreateNestedOneWithoutCustomersInput + referredByAgent?: AdminUserCreateNestedOneWithoutReferredCustomersInput + salesExpertTradingFor?: SalesExpertProfileCreateNestedOneWithoutTradingCustomerInput + avatarFile?: UploadedFileCreateNestedOneWithoutCustomerAvatarsInput + addresses?: CustomerAddressCreateNestedManyWithoutCustomerInput + orders?: OrderCreateNestedManyWithoutCustomerInput + preOrders?: PreOrderCreateNestedManyWithoutCustomerInput + ledgerEntries?: LedgerEntryCreateNestedManyWithoutCustomerInput + walletBonuses?: WalletBonusCreateNestedManyWithoutCustomerInput + comments?: CommentCreateNestedManyWithoutCustomerInput + promotions?: PromotionCreateNestedManyWithoutCustomerInput + notifications?: NotificationRecipientCreateNestedManyWithoutCustomerInput + activityLogs?: CustomerActivityLogCreateNestedManyWithoutCustomerInput + } + + export type CustomerUncheckedCreateWithoutOtpChallengesInput = { + id?: string + fullName: string + firstName?: string | null + lastName?: string | null + mobile: string + nationalCode: string + passwordHash: string + cityId?: string | null + kind?: $Enums.CustomerKind + phone?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + zone?: string | null + memberCode?: string | null + referredByAgentId?: string | null + goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string + goldDebtGrams?: Decimal | DecimalJsLike | number | string + fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string + verificationStep?: $Enums.VerificationStep + nationalIdImageUrl?: string | null + businessLicenseUrl?: string | null + shopImageUrl?: string | null + avatarFileId?: string | null + canPlaceOrders?: boolean + isActive?: boolean + isArchived?: boolean + createdAt?: Date | string + updatedAt?: Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedCreateNestedOneWithoutTradingCustomerInput + addresses?: CustomerAddressUncheckedCreateNestedManyWithoutCustomerInput + orders?: OrderUncheckedCreateNestedManyWithoutCustomerInput + preOrders?: PreOrderUncheckedCreateNestedManyWithoutCustomerInput + ledgerEntries?: LedgerEntryUncheckedCreateNestedManyWithoutCustomerInput + walletBonuses?: WalletBonusUncheckedCreateNestedManyWithoutCustomerInput + comments?: CommentUncheckedCreateNestedManyWithoutCustomerInput + promotions?: PromotionUncheckedCreateNestedManyWithoutCustomerInput + notifications?: NotificationRecipientUncheckedCreateNestedManyWithoutCustomerInput + activityLogs?: CustomerActivityLogUncheckedCreateNestedManyWithoutCustomerInput + } + + export type CustomerCreateOrConnectWithoutOtpChallengesInput = { + where: CustomerWhereUniqueInput + create: XOR + } + + export type CustomerUpsertWithoutOtpChallengesInput = { + update: XOR + create: XOR + where?: CustomerWhereInput + } + + export type CustomerUpdateToOneWithWhereWithoutOtpChallengesInput = { + where?: CustomerWhereInput + data: XOR + } + + export type CustomerUpdateWithoutOtpChallengesInput = { + id?: StringFieldUpdateOperationsInput | string + fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null + mobile?: StringFieldUpdateOperationsInput | string + nationalCode?: StringFieldUpdateOperationsInput | string + passwordHash?: StringFieldUpdateOperationsInput | string + kind?: EnumCustomerKindFieldUpdateOperationsInput | $Enums.CustomerKind + phone?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + zone?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null + goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep + nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null + businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null + shopImageUrl?: NullableStringFieldUpdateOperationsInput | string | null + canPlaceOrders?: BoolFieldUpdateOperationsInput | boolean + isActive?: BoolFieldUpdateOperationsInput | boolean + isArchived?: BoolFieldUpdateOperationsInput | boolean + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + city?: CityUpdateOneWithoutCustomersNestedInput + referredByAgent?: AdminUserUpdateOneWithoutReferredCustomersNestedInput + salesExpertTradingFor?: SalesExpertProfileUpdateOneWithoutTradingCustomerNestedInput + avatarFile?: UploadedFileUpdateOneWithoutCustomerAvatarsNestedInput + addresses?: CustomerAddressUpdateManyWithoutCustomerNestedInput + orders?: OrderUpdateManyWithoutCustomerNestedInput + preOrders?: PreOrderUpdateManyWithoutCustomerNestedInput + ledgerEntries?: LedgerEntryUpdateManyWithoutCustomerNestedInput + walletBonuses?: WalletBonusUpdateManyWithoutCustomerNestedInput + comments?: CommentUpdateManyWithoutCustomerNestedInput + promotions?: PromotionUpdateManyWithoutCustomerNestedInput + notifications?: NotificationRecipientUpdateManyWithoutCustomerNestedInput + activityLogs?: CustomerActivityLogUpdateManyWithoutCustomerNestedInput + } + + export type CustomerUncheckedUpdateWithoutOtpChallengesInput = { + id?: StringFieldUpdateOperationsInput | string + fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null + mobile?: StringFieldUpdateOperationsInput | string + nationalCode?: StringFieldUpdateOperationsInput | string + passwordHash?: StringFieldUpdateOperationsInput | string + cityId?: NullableStringFieldUpdateOperationsInput | string | null + kind?: EnumCustomerKindFieldUpdateOperationsInput | $Enums.CustomerKind + phone?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + zone?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null + referredByAgentId?: NullableStringFieldUpdateOperationsInput | string | null + goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep + nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null + businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null + shopImageUrl?: NullableStringFieldUpdateOperationsInput | string | null + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + canPlaceOrders?: BoolFieldUpdateOperationsInput | boolean + isActive?: BoolFieldUpdateOperationsInput | boolean + isArchived?: BoolFieldUpdateOperationsInput | boolean + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedUpdateOneWithoutTradingCustomerNestedInput + addresses?: CustomerAddressUncheckedUpdateManyWithoutCustomerNestedInput + orders?: OrderUncheckedUpdateManyWithoutCustomerNestedInput + preOrders?: PreOrderUncheckedUpdateManyWithoutCustomerNestedInput + ledgerEntries?: LedgerEntryUncheckedUpdateManyWithoutCustomerNestedInput + walletBonuses?: WalletBonusUncheckedUpdateManyWithoutCustomerNestedInput + comments?: CommentUncheckedUpdateManyWithoutCustomerNestedInput + promotions?: PromotionUncheckedUpdateManyWithoutCustomerNestedInput + notifications?: NotificationRecipientUncheckedUpdateManyWithoutCustomerNestedInput + activityLogs?: CustomerActivityLogUncheckedUpdateManyWithoutCustomerNestedInput + } + export type CustomerCreateManyCityInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -60563,6 +65992,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -60608,9 +66038,22 @@ export namespace Prisma { updatedAt?: Date | string } + export type SalesExpertProfileCreateManyCityInput = { + id?: string + adminUserId: string + gramLevel?: Decimal | DecimalJsLike | number | string + phone?: string | null + nationalCode?: string | null + tradingCustomerId?: string | null + createdAt?: Date | string + updatedAt?: Date | string + } + export type CustomerUpdateWithoutCityInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -60624,6 +66067,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -60634,6 +66078,7 @@ export namespace Prisma { createdAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string referredByAgent?: AdminUserUpdateOneWithoutReferredCustomersNestedInput + salesExpertTradingFor?: SalesExpertProfileUpdateOneWithoutTradingCustomerNestedInput avatarFile?: UploadedFileUpdateOneWithoutCustomerAvatarsNestedInput addresses?: CustomerAddressUpdateManyWithoutCustomerNestedInput orders?: OrderUpdateManyWithoutCustomerNestedInput @@ -60644,11 +66089,14 @@ export namespace Prisma { promotions?: PromotionUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUpdateManyWithoutCustomerNestedInput } export type CustomerUncheckedUpdateWithoutCityInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -60663,6 +66111,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -60673,6 +66122,7 @@ export namespace Prisma { isArchived?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedUpdateOneWithoutTradingCustomerNestedInput addresses?: CustomerAddressUncheckedUpdateManyWithoutCustomerNestedInput orders?: OrderUncheckedUpdateManyWithoutCustomerNestedInput preOrders?: PreOrderUncheckedUpdateManyWithoutCustomerNestedInput @@ -60682,11 +66132,14 @@ export namespace Prisma { promotions?: PromotionUncheckedUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUncheckedUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUncheckedUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUncheckedUpdateManyWithoutCustomerNestedInput } export type CustomerUncheckedUpdateManyWithoutCityInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -60701,6 +66154,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -60820,9 +66274,44 @@ export namespace Prisma { updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string } + export type SalesExpertProfileUpdateWithoutCityInput = { + id?: StringFieldUpdateOperationsInput | string + gramLevel?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + phone?: NullableStringFieldUpdateOperationsInput | string | null + nationalCode?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + adminUser?: AdminUserUpdateOneRequiredWithoutSalesExpertProfileNestedInput + tradingCustomer?: CustomerUpdateOneWithoutSalesExpertTradingForNestedInput + } + + export type SalesExpertProfileUncheckedUpdateWithoutCityInput = { + id?: StringFieldUpdateOperationsInput | string + adminUserId?: StringFieldUpdateOperationsInput | string + gramLevel?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + phone?: NullableStringFieldUpdateOperationsInput | string | null + nationalCode?: NullableStringFieldUpdateOperationsInput | string | null + tradingCustomerId?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + } + + export type SalesExpertProfileUncheckedUpdateManyWithoutCityInput = { + id?: StringFieldUpdateOperationsInput | string + adminUserId?: StringFieldUpdateOperationsInput | string + gramLevel?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + phone?: NullableStringFieldUpdateOperationsInput | string | null + nationalCode?: NullableStringFieldUpdateOperationsInput | string | null + tradingCustomerId?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + } + export type CustomerCreateManyReferredByAgentInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -60837,6 +66326,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -60935,6 +66425,8 @@ export namespace Prisma { export type CustomerUpdateWithoutReferredByAgentInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -60948,6 +66440,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -60958,6 +66451,7 @@ export namespace Prisma { createdAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string city?: CityUpdateOneWithoutCustomersNestedInput + salesExpertTradingFor?: SalesExpertProfileUpdateOneWithoutTradingCustomerNestedInput avatarFile?: UploadedFileUpdateOneWithoutCustomerAvatarsNestedInput addresses?: CustomerAddressUpdateManyWithoutCustomerNestedInput orders?: OrderUpdateManyWithoutCustomerNestedInput @@ -60968,11 +66462,14 @@ export namespace Prisma { promotions?: PromotionUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUpdateManyWithoutCustomerNestedInput } export type CustomerUncheckedUpdateWithoutReferredByAgentInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -60987,6 +66484,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -60997,6 +66495,7 @@ export namespace Prisma { isArchived?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedUpdateOneWithoutTradingCustomerNestedInput addresses?: CustomerAddressUncheckedUpdateManyWithoutCustomerNestedInput orders?: OrderUncheckedUpdateManyWithoutCustomerNestedInput preOrders?: PreOrderUncheckedUpdateManyWithoutCustomerNestedInput @@ -61006,11 +66505,14 @@ export namespace Prisma { promotions?: PromotionUncheckedUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUncheckedUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUncheckedUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUncheckedUpdateManyWithoutCustomerNestedInput } export type CustomerUncheckedUpdateManyWithoutReferredByAgentInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -61025,6 +66527,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -61271,6 +66774,7 @@ export namespace Prisma { purpose?: NullableStringFieldUpdateOperationsInput | string | null createdAt?: DateTimeFieldUpdateOperationsInput | Date | string customerAvatars?: CustomerUpdateManyWithoutAvatarFileNestedInput + adminUserAvatars?: AdminUserUpdateManyWithoutAvatarFileNestedInput } export type UploadedFileUncheckedUpdateWithoutUploadedByAdminUserInput = { @@ -61282,6 +66786,7 @@ export namespace Prisma { purpose?: NullableStringFieldUpdateOperationsInput | string | null createdAt?: DateTimeFieldUpdateOperationsInput | Date | string customerAvatars?: CustomerUncheckedUpdateManyWithoutAvatarFileNestedInput + adminUserAvatars?: AdminUserUncheckedUpdateManyWithoutAvatarFileNestedInput } export type UploadedFileUncheckedUpdateManyWithoutUploadedByAdminUserInput = { @@ -61409,6 +66914,17 @@ export namespace Prisma { createdAt?: Date | string } + export type CustomerOtpCreateManyCustomerInput = { + id?: string + mobile: string + codeHash: string + expiresAt: Date | string + attempts?: number + consumedAt?: Date | string | null + requestIp?: string | null + createdAt?: Date | string + } + export type CustomerAddressUpdateWithoutCustomerInput = { id?: StringFieldUpdateOperationsInput | string addressLine?: StringFieldUpdateOperationsInput | string @@ -61766,9 +67282,44 @@ export namespace Prisma { createdAt?: DateTimeFieldUpdateOperationsInput | Date | string } + export type CustomerOtpUpdateWithoutCustomerInput = { + id?: StringFieldUpdateOperationsInput | string + mobile?: StringFieldUpdateOperationsInput | string + codeHash?: StringFieldUpdateOperationsInput | string + expiresAt?: DateTimeFieldUpdateOperationsInput | Date | string + attempts?: IntFieldUpdateOperationsInput | number + consumedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null + requestIp?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + } + + export type CustomerOtpUncheckedUpdateWithoutCustomerInput = { + id?: StringFieldUpdateOperationsInput | string + mobile?: StringFieldUpdateOperationsInput | string + codeHash?: StringFieldUpdateOperationsInput | string + expiresAt?: DateTimeFieldUpdateOperationsInput | Date | string + attempts?: IntFieldUpdateOperationsInput | number + consumedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null + requestIp?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + } + + export type CustomerOtpUncheckedUpdateManyWithoutCustomerInput = { + id?: StringFieldUpdateOperationsInput | string + mobile?: StringFieldUpdateOperationsInput | string + codeHash?: StringFieldUpdateOperationsInput | string + expiresAt?: DateTimeFieldUpdateOperationsInput | Date | string + attempts?: IntFieldUpdateOperationsInput | number + consumedAt?: NullableDateTimeFieldUpdateOperationsInput | Date | string | null + requestIp?: NullableStringFieldUpdateOperationsInput | string | null + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + } + export type AdminUserCreateManyBranchInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole @@ -61776,6 +67327,11 @@ export namespace Prisma { wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + avatarFileId?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null } export type OrderCreateManyBranchInput = { @@ -61803,13 +67359,20 @@ export namespace Prisma { export type AdminUserUpdateWithoutBranchInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null userType?: UserTypeUpdateOneWithoutAdminUsersNestedInput + avatarFile?: UploadedFileUpdateOneWithoutAdminUserAvatarsNestedInput referredCustomers?: CustomerUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUpdateManyWithoutAllocatorAdminUserNestedInput @@ -61819,11 +67382,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUpdateOneWithoutAdminUserNestedInput } export type AdminUserUncheckedUpdateWithoutBranchInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole @@ -61831,6 +67397,11 @@ export namespace Prisma { wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null referredCustomers?: CustomerUncheckedUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUncheckedUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUncheckedUpdateManyWithoutAllocatorAdminUserNestedInput @@ -61840,11 +67411,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUncheckedUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUncheckedUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUncheckedUpdateOneWithoutAdminUserNestedInput } export type AdminUserUncheckedUpdateManyWithoutBranchInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole @@ -61852,6 +67426,11 @@ export namespace Prisma { wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null } export type OrderUpdateWithoutBranchInput = { @@ -62331,6 +67910,7 @@ export namespace Prisma { qty?: number isPreOrder?: boolean isActive?: boolean + imageId?: string | null } export type ProductImageCreateManyProductInput = { @@ -62387,6 +67967,7 @@ export namespace Prisma { qty?: IntFieldUpdateOperationsInput | number isPreOrder?: BoolFieldUpdateOperationsInput | boolean isActive?: BoolFieldUpdateOperationsInput | boolean + image?: ProductImageUpdateOneWithoutVariantsNestedInput orderItems?: OrderItemUpdateManyWithoutProductVariantNestedInput } @@ -62399,6 +67980,7 @@ export namespace Prisma { qty?: IntFieldUpdateOperationsInput | number isPreOrder?: BoolFieldUpdateOperationsInput | boolean isActive?: BoolFieldUpdateOperationsInput | boolean + imageId?: NullableStringFieldUpdateOperationsInput | string | null orderItems?: OrderItemUncheckedUpdateManyWithoutProductVariantNestedInput } @@ -62411,6 +67993,7 @@ export namespace Prisma { qty?: IntFieldUpdateOperationsInput | number isPreOrder?: BoolFieldUpdateOperationsInput | boolean isActive?: BoolFieldUpdateOperationsInput | boolean + imageId?: NullableStringFieldUpdateOperationsInput | string | null } export type ProductImageUpdateWithoutProductInput = { @@ -62418,6 +68001,7 @@ export namespace Prisma { url?: StringFieldUpdateOperationsInput | string sortOrder?: IntFieldUpdateOperationsInput | number role?: EnumProductImageRoleFieldUpdateOperationsInput | $Enums.ProductImageRole + variants?: ProductVariantUpdateManyWithoutImageNestedInput } export type ProductImageUncheckedUpdateWithoutProductInput = { @@ -62425,6 +68009,7 @@ export namespace Prisma { url?: StringFieldUpdateOperationsInput | string sortOrder?: IntFieldUpdateOperationsInput | number role?: EnumProductImageRoleFieldUpdateOperationsInput | $Enums.ProductImageRole + variants?: ProductVariantUncheckedUpdateManyWithoutImageNestedInput } export type ProductImageUncheckedUpdateManyWithoutProductInput = { @@ -62550,6 +68135,56 @@ export namespace Prisma { weightGrams?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null } + export type ProductVariantCreateManyImageInput = { + id?: string + productId: string + title: string + sku?: string | null + rashWeightGrams: Decimal | DecimalJsLike | number | string + defaultWagePercent: Decimal | DecimalJsLike | number | string + qty?: number + isPreOrder?: boolean + isActive?: boolean + } + + export type ProductVariantUpdateWithoutImageInput = { + id?: StringFieldUpdateOperationsInput | string + title?: StringFieldUpdateOperationsInput | string + sku?: NullableStringFieldUpdateOperationsInput | string | null + rashWeightGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + defaultWagePercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + qty?: IntFieldUpdateOperationsInput | number + isPreOrder?: BoolFieldUpdateOperationsInput | boolean + isActive?: BoolFieldUpdateOperationsInput | boolean + product?: ProductUpdateOneRequiredWithoutVariantsNestedInput + orderItems?: OrderItemUpdateManyWithoutProductVariantNestedInput + } + + export type ProductVariantUncheckedUpdateWithoutImageInput = { + id?: StringFieldUpdateOperationsInput | string + productId?: StringFieldUpdateOperationsInput | string + title?: StringFieldUpdateOperationsInput | string + sku?: NullableStringFieldUpdateOperationsInput | string | null + rashWeightGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + defaultWagePercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + qty?: IntFieldUpdateOperationsInput | number + isPreOrder?: BoolFieldUpdateOperationsInput | boolean + isActive?: BoolFieldUpdateOperationsInput | boolean + orderItems?: OrderItemUncheckedUpdateManyWithoutProductVariantNestedInput + } + + export type ProductVariantUncheckedUpdateManyWithoutImageInput = { + id?: StringFieldUpdateOperationsInput | string + productId?: StringFieldUpdateOperationsInput | string + title?: StringFieldUpdateOperationsInput | string + sku?: NullableStringFieldUpdateOperationsInput | string | null + rashWeightGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + defaultWagePercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + qty?: IntFieldUpdateOperationsInput | number + isPreOrder?: BoolFieldUpdateOperationsInput | boolean + isActive?: BoolFieldUpdateOperationsInput | boolean + } + export type OrderItemCreateManyProductVariantInput = { id?: string orderId: string @@ -62809,6 +68444,8 @@ export namespace Prisma { export type AdminUserCreateManyUserTypeInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string passwordHash: string role: $Enums.AdminRole @@ -62816,6 +68453,11 @@ export namespace Prisma { wagePercent?: Decimal | DecimalJsLike | number | string | null isActive?: boolean createdAt?: Date | string + avatarFileId?: string | null + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null } export type PermissionUpdateWithoutUserTypeInput = { @@ -62833,13 +68475,20 @@ export namespace Prisma { export type AdminUserUpdateWithoutUserTypeInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null branch?: BranchUpdateOneWithoutAdminUsersNestedInput + avatarFile?: UploadedFileUpdateOneWithoutAdminUserAvatarsNestedInput referredCustomers?: CustomerUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUpdateManyWithoutAllocatorAdminUserNestedInput @@ -62849,11 +68498,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUpdateOneWithoutAdminUserNestedInput } export type AdminUserUncheckedUpdateWithoutUserTypeInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole @@ -62861,6 +68513,11 @@ export namespace Prisma { wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null referredCustomers?: CustomerUncheckedUpdateManyWithoutReferredByAgentNestedInput supervisedWorkshops?: WorkshopUncheckedUpdateManyWithoutSupervisorNestedInput branchAllocations?: BranchAllocationUncheckedUpdateManyWithoutAllocatorAdminUserNestedInput @@ -62870,11 +68527,14 @@ export namespace Prisma { sentSmsMessages?: SmsMessageUncheckedUpdateManyWithoutSentByAdminUserNestedInput sentNotifications?: NotificationUncheckedUpdateManyWithoutSentByAdminUserNestedInput uploadedFiles?: UploadedFileUncheckedUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUncheckedUpdateOneWithoutAdminUserNestedInput } export type AdminUserUncheckedUpdateManyWithoutUserTypeInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole @@ -62882,6 +68542,11 @@ export namespace Prisma { wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null isActive?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + avatarFileId?: NullableStringFieldUpdateOperationsInput | string | null + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null } export type CommentCreateManyReplyToInput = { @@ -62977,6 +68642,8 @@ export namespace Prisma { export type CustomerCreateManyAvatarFileInput = { id?: string fullName: string + firstName?: string | null + lastName?: string | null mobile: string nationalCode: string passwordHash: string @@ -62992,6 +68659,7 @@ export namespace Prisma { goldCreditLimitGrams?: Decimal | DecimalJsLike | number | string goldDebtGrams?: Decimal | DecimalJsLike | number | string fiatDebtRial?: Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: Decimal | DecimalJsLike | number | string verificationStep?: $Enums.VerificationStep nationalIdImageUrl?: string | null businessLicenseUrl?: string | null @@ -63003,9 +68671,30 @@ export namespace Prisma { updatedAt?: Date | string } + export type AdminUserCreateManyAvatarFileInput = { + id?: string + fullName: string + firstName?: string | null + lastName?: string | null + mobile: string + passwordHash: string + role: $Enums.AdminRole + userTypeId?: number | null + branchId?: string | null + wagePercent?: Decimal | DecimalJsLike | number | string | null + isActive?: boolean + createdAt?: Date | string + address?: string | null + storeName?: string | null + pasazhName?: string | null + memberCode?: string | null + } + export type CustomerUpdateWithoutAvatarFileInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -63019,6 +68708,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -63030,6 +68720,7 @@ export namespace Prisma { updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string city?: CityUpdateOneWithoutCustomersNestedInput referredByAgent?: AdminUserUpdateOneWithoutReferredCustomersNestedInput + salesExpertTradingFor?: SalesExpertProfileUpdateOneWithoutTradingCustomerNestedInput addresses?: CustomerAddressUpdateManyWithoutCustomerNestedInput orders?: OrderUpdateManyWithoutCustomerNestedInput preOrders?: PreOrderUpdateManyWithoutCustomerNestedInput @@ -63039,11 +68730,14 @@ export namespace Prisma { promotions?: PromotionUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUpdateManyWithoutCustomerNestedInput } export type CustomerUncheckedUpdateWithoutAvatarFileInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -63059,6 +68753,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -63068,6 +68763,7 @@ export namespace Prisma { isArchived?: BoolFieldUpdateOperationsInput | boolean createdAt?: DateTimeFieldUpdateOperationsInput | Date | string updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string + salesExpertTradingFor?: SalesExpertProfileUncheckedUpdateOneWithoutTradingCustomerNestedInput addresses?: CustomerAddressUncheckedUpdateManyWithoutCustomerNestedInput orders?: OrderUncheckedUpdateManyWithoutCustomerNestedInput preOrders?: PreOrderUncheckedUpdateManyWithoutCustomerNestedInput @@ -63077,11 +68773,14 @@ export namespace Prisma { promotions?: PromotionUncheckedUpdateManyWithoutCustomerNestedInput notifications?: NotificationRecipientUncheckedUpdateManyWithoutCustomerNestedInput activityLogs?: CustomerActivityLogUncheckedUpdateManyWithoutCustomerNestedInput + otpChallenges?: CustomerOtpUncheckedUpdateManyWithoutCustomerNestedInput } export type CustomerUncheckedUpdateManyWithoutAvatarFileInput = { id?: StringFieldUpdateOperationsInput | string fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null mobile?: StringFieldUpdateOperationsInput | string nationalCode?: StringFieldUpdateOperationsInput | string passwordHash?: StringFieldUpdateOperationsInput | string @@ -63097,6 +68796,7 @@ export namespace Prisma { goldCreditLimitGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string goldDebtGrams?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string fiatDebtRial?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string + makingChargeOffsetPercent?: DecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string verificationStep?: EnumVerificationStepFieldUpdateOperationsInput | $Enums.VerificationStep nationalIdImageUrl?: NullableStringFieldUpdateOperationsInput | string | null businessLicenseUrl?: NullableStringFieldUpdateOperationsInput | string | null @@ -63108,6 +68808,83 @@ export namespace Prisma { updatedAt?: DateTimeFieldUpdateOperationsInput | Date | string } + export type AdminUserUpdateWithoutAvatarFileInput = { + id?: StringFieldUpdateOperationsInput | string + fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null + mobile?: StringFieldUpdateOperationsInput | string + passwordHash?: StringFieldUpdateOperationsInput | string + role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole + wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null + isActive?: BoolFieldUpdateOperationsInput | boolean + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null + userType?: UserTypeUpdateOneWithoutAdminUsersNestedInput + branch?: BranchUpdateOneWithoutAdminUsersNestedInput + referredCustomers?: CustomerUpdateManyWithoutReferredByAgentNestedInput + supervisedWorkshops?: WorkshopUpdateManyWithoutSupervisorNestedInput + branchAllocations?: BranchAllocationUpdateManyWithoutAllocatorAdminUserNestedInput + postedLedgerEntries?: LedgerEntryUpdateManyWithoutPostedByAdminUserNestedInput + orderStatusEvents?: OrderStatusEventUpdateManyWithoutActorAdminUserNestedInput + preOrderStatusEvents?: PreOrderStatusEventUpdateManyWithoutActorAdminUserNestedInput + sentSmsMessages?: SmsMessageUpdateManyWithoutSentByAdminUserNestedInput + sentNotifications?: NotificationUpdateManyWithoutSentByAdminUserNestedInput + uploadedFiles?: UploadedFileUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUpdateOneWithoutAdminUserNestedInput + } + + export type AdminUserUncheckedUpdateWithoutAvatarFileInput = { + id?: StringFieldUpdateOperationsInput | string + fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null + mobile?: StringFieldUpdateOperationsInput | string + passwordHash?: StringFieldUpdateOperationsInput | string + role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole + userTypeId?: NullableIntFieldUpdateOperationsInput | number | null + branchId?: NullableStringFieldUpdateOperationsInput | string | null + wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null + isActive?: BoolFieldUpdateOperationsInput | boolean + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null + referredCustomers?: CustomerUncheckedUpdateManyWithoutReferredByAgentNestedInput + supervisedWorkshops?: WorkshopUncheckedUpdateManyWithoutSupervisorNestedInput + branchAllocations?: BranchAllocationUncheckedUpdateManyWithoutAllocatorAdminUserNestedInput + postedLedgerEntries?: LedgerEntryUncheckedUpdateManyWithoutPostedByAdminUserNestedInput + orderStatusEvents?: OrderStatusEventUncheckedUpdateManyWithoutActorAdminUserNestedInput + preOrderStatusEvents?: PreOrderStatusEventUncheckedUpdateManyWithoutActorAdminUserNestedInput + sentSmsMessages?: SmsMessageUncheckedUpdateManyWithoutSentByAdminUserNestedInput + sentNotifications?: NotificationUncheckedUpdateManyWithoutSentByAdminUserNestedInput + uploadedFiles?: UploadedFileUncheckedUpdateManyWithoutUploadedByAdminUserNestedInput + salesExpertProfile?: SalesExpertProfileUncheckedUpdateOneWithoutAdminUserNestedInput + } + + export type AdminUserUncheckedUpdateManyWithoutAvatarFileInput = { + id?: StringFieldUpdateOperationsInput | string + fullName?: StringFieldUpdateOperationsInput | string + firstName?: NullableStringFieldUpdateOperationsInput | string | null + lastName?: NullableStringFieldUpdateOperationsInput | string | null + mobile?: StringFieldUpdateOperationsInput | string + passwordHash?: StringFieldUpdateOperationsInput | string + role?: EnumAdminRoleFieldUpdateOperationsInput | $Enums.AdminRole + userTypeId?: NullableIntFieldUpdateOperationsInput | number | null + branchId?: NullableStringFieldUpdateOperationsInput | string | null + wagePercent?: NullableDecimalFieldUpdateOperationsInput | Decimal | DecimalJsLike | number | string | null + isActive?: BoolFieldUpdateOperationsInput | boolean + createdAt?: DateTimeFieldUpdateOperationsInput | Date | string + address?: NullableStringFieldUpdateOperationsInput | string | null + storeName?: NullableStringFieldUpdateOperationsInput | string | null + pasazhName?: NullableStringFieldUpdateOperationsInput | string | null + memberCode?: NullableStringFieldUpdateOperationsInput | string | null + } + /** @@ -63149,6 +68926,10 @@ export namespace Prisma { * @deprecated Use ProductCountOutputTypeDefaultArgs instead */ export type ProductCountOutputTypeArgs = ProductCountOutputTypeDefaultArgs + /** + * @deprecated Use ProductImageCountOutputTypeDefaultArgs instead + */ + export type ProductImageCountOutputTypeArgs = ProductImageCountOutputTypeDefaultArgs /** * @deprecated Use ProductVariantCountOutputTypeDefaultArgs instead */ @@ -63189,6 +68970,10 @@ export namespace Prisma { * @deprecated Use AdminUserDefaultArgs instead */ export type AdminUserArgs = AdminUserDefaultArgs + /** + * @deprecated Use SalesExpertProfileDefaultArgs instead + */ + export type SalesExpertProfileArgs = SalesExpertProfileDefaultArgs /** * @deprecated Use CustomerDefaultArgs instead */ @@ -63321,6 +69106,10 @@ export namespace Prisma { * @deprecated Use UploadedFileDefaultArgs instead */ export type UploadedFileArgs = UploadedFileDefaultArgs + /** + * @deprecated Use CustomerOtpDefaultArgs instead + */ + export type CustomerOtpArgs = CustomerOtpDefaultArgs /** * Batch Payload for updateMany & deleteMany & createMany diff --git a/apps/api/release/vendor/.prisma/client/index.js b/apps/api/release/vendor/.prisma/client/index.js index 7d9a854..fe17744 100644 --- a/apps/api/release/vendor/.prisma/client/index.js +++ b/apps/api/release/vendor/.prisma/client/index.js @@ -102,6 +102,8 @@ exports.Prisma.CityScalarFieldEnum = { exports.Prisma.AdminUserScalarFieldEnum = { id: 'id', fullName: 'fullName', + firstName: 'firstName', + lastName: 'lastName', mobile: 'mobile', passwordHash: 'passwordHash', role: 'role', @@ -109,12 +111,31 @@ exports.Prisma.AdminUserScalarFieldEnum = { branchId: 'branchId', wagePercent: 'wagePercent', isActive: 'isActive', - createdAt: 'createdAt' + createdAt: 'createdAt', + avatarFileId: 'avatarFileId', + address: 'address', + storeName: 'storeName', + pasazhName: 'pasazhName', + memberCode: 'memberCode' +}; + +exports.Prisma.SalesExpertProfileScalarFieldEnum = { + id: 'id', + adminUserId: 'adminUserId', + cityId: 'cityId', + gramLevel: 'gramLevel', + phone: 'phone', + nationalCode: 'nationalCode', + tradingCustomerId: 'tradingCustomerId', + createdAt: 'createdAt', + updatedAt: 'updatedAt' }; exports.Prisma.CustomerScalarFieldEnum = { id: 'id', fullName: 'fullName', + firstName: 'firstName', + lastName: 'lastName', mobile: 'mobile', nationalCode: 'nationalCode', passwordHash: 'passwordHash', @@ -130,6 +151,7 @@ exports.Prisma.CustomerScalarFieldEnum = { goldCreditLimitGrams: 'goldCreditLimitGrams', goldDebtGrams: 'goldDebtGrams', fiatDebtRial: 'fiatDebtRial', + makingChargeOffsetPercent: 'makingChargeOffsetPercent', verificationStep: 'verificationStep', nationalIdImageUrl: 'nationalIdImageUrl', businessLicenseUrl: 'businessLicenseUrl', @@ -225,7 +247,8 @@ exports.Prisma.ProductVariantScalarFieldEnum = { defaultWagePercent: 'defaultWagePercent', qty: 'qty', isPreOrder: 'isPreOrder', - isActive: 'isActive' + isActive: 'isActive', + imageId: 'imageId' }; exports.Prisma.OrderScalarFieldEnum = { @@ -460,6 +483,18 @@ exports.Prisma.UploadedFileScalarFieldEnum = { createdAt: 'createdAt' }; +exports.Prisma.CustomerOtpScalarFieldEnum = { + id: 'id', + mobile: 'mobile', + customerId: 'customerId', + codeHash: 'codeHash', + expiresAt: 'expiresAt', + attempts: 'attempts', + consumedAt: 'consumedAt', + requestIp: 'requestIp', + createdAt: 'createdAt' +}; + exports.Prisma.SortOrder = { asc: 'asc', desc: 'desc' @@ -497,7 +532,8 @@ exports.CustomerKind = exports.$Enums.CustomerKind = { APP_SHOPKEEPER: 'APP_SHOPKEEPER', WALK_IN_SHOPKEEPER: 'WALK_IN_SHOPKEEPER', BANKER: 'BANKER', - WORKSHOP_SUPERVISOR: 'WORKSHOP_SUPERVISOR' + WORKSHOP_SUPERVISOR: 'WORKSHOP_SUPERVISOR', + RETAIL: 'RETAIL' }; exports.VerificationStep = exports.$Enums.VerificationStep = { @@ -590,6 +626,7 @@ exports.MessageAudience = exports.$Enums.MessageAudience = { exports.Prisma.ModelName = { City: 'City', AdminUser: 'AdminUser', + SalesExpertProfile: 'SalesExpertProfile', Customer: 'Customer', CustomerAddress: 'CustomerAddress', Branch: 'Branch', @@ -622,7 +659,8 @@ exports.Prisma.ModelName = { NotificationRecipient: 'NotificationRecipient', NewsletterSubscriber: 'NewsletterSubscriber', CustomerActivityLog: 'CustomerActivityLog', - UploadedFile: 'UploadedFile' + UploadedFile: 'UploadedFile', + CustomerOtp: 'CustomerOtp' }; /** * Create the Client @@ -635,7 +673,7 @@ const config = { "value": "prisma-client-js" }, "output": { - "value": "D:\\APP\\gold-bank\\node_modules\\.pnpm\\@prisma+client@5.22.0_prisma@5.22.0\\node_modules\\@prisma\\client", + "value": "E:\\gold bank\\gold-banking-monorepo\\node_modules\\.pnpm\\@prisma+client@5.22.0_prisma@5.22.0\\node_modules\\@prisma\\client", "fromEnvVar": null }, "config": { @@ -653,10 +691,11 @@ const config = { } ], "previewFeatures": [], - "sourceFilePath": "D:\\APP\\gold-bank\\apps\\api\\prisma\\schema.prisma" + "sourceFilePath": "E:\\gold bank\\gold-banking-monorepo\\apps\\api\\prisma\\schema.prisma" }, "relativeEnvPaths": { - "rootEnvPath": null + "rootEnvPath": null, + "schemaEnvPath": "../../../../../../apps/api/.env" }, "relativePath": "../../../../../../apps/api/prisma", "clientVersion": "5.22.0", @@ -674,8 +713,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; 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\n/// Legacy Customer.type — what kind of wholesale customer this is. Values and\n/// labels taken verbatim from bulkCustomer.vue's userTypes list.\nenum CustomerKind {\n SALES_AGENT // 1 کارشناس فروش\n APP_SHOPKEEPER // 2 مغازه دار اپلیکیشن\n WALK_IN_SHOPKEEPER // 3 مغازه دار حضوری\n BANKER // 4 بنکدار\n WORKSHOP_SUPERVISOR // 5 سرپرست کارگاه\n}\n\nenum VerificationStep {\n UNVERIFIED\n STEP_ONE_SUBMITTED\n STEP_TWO_SUBMITTED\n VERIFIED\n}\n\n/// Legacy kept retail and wholesale orders in two SEPARATE tables\n/// (orders_orders vs orders). The redesigned schema has one Order model, so the\n/// distinction lives here instead.\nenum OrderChannel {\n RETAIL // تک فروشی\n WHOLESALE // عمده فروشی\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 /// Legacy cities.percent / percent_aux — per-city wage surcharges the city\n /// screen edits through GET cities/get-percent/{id} and get-percent-aux/{id}.\n percent Decimal? @db.Decimal(6, 3)\n percentAux Decimal? @db.Decimal(6, 3)\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 /// Legacy `users.ut`. Granular ACL lives here (Permission rows hang off UserType);\n /// the coarse `role` enum above is retained for the business rules that already\n /// switch on it. Nullable so existing seeded admins remain valid.\n userTypeId Int?\n userType UserType? @relation(fields: [userTypeId], references: [id])\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 supervisedWorkshops Workshop[] @relation(\"WorkshopSupervisor\")\n branchAllocations BranchAllocation[] @relation(\"AllocationAuthor\")\n postedLedgerEntries LedgerEntry[]\n orderStatusEvents OrderStatusEvent[]\n preOrderStatusEvents PreOrderStatusEvent[]\n sentSmsMessages SmsMessage[] @relation(\"SmsAuthor\")\n sentNotifications Notification[] @relation(\"NotificationAuthor\")\n uploadedFiles UploadedFile[] @relation(\"FileUploader\")\n\n @@index([role])\n @@index([userTypeId])\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 /// Legacy Customer.type — distinguishes بنکدار from مغازه دار, which the\n /// BankerList / ShopkeeperList screens filter on.\n kind CustomerKind @default(APP_SHOPKEEPER)\n\n /// Legacy contact + shop columns, shown on the wholesale customer form and as\n /// (hidden-by-default) columns in its grid.\n phone String?\n address String?\n storeName String?\n pasazhName String?\n zone String?\n /// Legacy mcode — the operator-assigned customer code.\n memberCode String?\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 /// Legacy `image_id` — the profile photo (عکس پروفایل) on the sales-agent\n /// form. Legacy stored an FK to its Image table and read it back as a nested\n /// `image: { id, image }`, so this is a relation, not a bare URL string.\n avatarFileId String?\n avatarFile UploadedFile? @relation(\"CustomerAvatar\", fields: [avatarFileId], references: [id])\n\n /// Legacy had TWO independent switches on a customer:\n /// sts -> may they place orders (this field)\n /// active -> is the account enabled (isActive, below)\n /// The port previously drove both from isActive; they are separate again.\n canPlaceOrders Boolean @default(true)\n\n isActive Boolean @default(true)\n /// Legacy archive flag — the archived list filtered on this, not on isActive.\n isArchived Boolean @default(false)\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 comments Comment[]\n promotions Promotion[]\n notifications NotificationRecipient[]\n activityLogs CustomerActivityLog[]\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 /// Legacy Branch.address / phone — both are columns in the branch grid.\n address String?\n phone String?\n /// Legacy GET branch/active/{id} toggles this.\n isActive Boolean @default(true)\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n adminUsers AdminUser[]\n orders Order[]\n allocations BranchAllocation[]\n}\n\nmodel Workshop {\n id String @id @default(cuid())\n name String\n /// Legacy Workshop.address / phone — both are columns in the workshop grid.\n address String?\n phone String?\n /// The supervisor created alongside the workshop by the legacy Add form.\n supervisorId String?\n supervisor AdminUser? @relation(\"WorkshopSupervisor\", fields: [supervisorId], references: [id])\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 /// Legacy Category.type — which storefront the category belongs to:\n /// 1 سایت | 2 اپلیکشن موبایل. Drives the \"type_str\" grid column.\n type Int @default(1)\n /// Legacy Category.image / icon — the grid renders the image, the storefront the icon.\n imageUrl String?\n iconUrl String?\n /// Legacy meta tag + description, used for SEO on the site categories.\n metaTag String?\n description String?\n parentId String?\n parent Category? @relation(\"CategoryTree\", fields: [parentId], references: [id])\n children Category[] @relation(\"CategoryTree\")\n products Product[]\n promotions Promotion[]\n}\n\nmodel Brand {\n id String @id @default(cuid())\n title String\n products Product[]\n promotions Promotion[]\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 /// Legacy Product.product_code — the human-facing SKU shown in the grid.\n productCode String? @unique\n /// Legacy Product.show_type: 1 site, 2 mobile, 3 both.\n showType Int @default(3)\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n variants ProductVariant[]\n images ProductImage[]\n collections CollectionProduct[]\n comments Comment[]\n promotions Promotion[]\n allocationItems BranchAllocationItem[]\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 /// Legacy had 4 distinct upload slots (تصویر شاخص / تصویر شاخص موبایل /\n /// گالری تصاویر سایت / گالری تصاویر موبایل) — GALLERY is the default so\n /// existing rows and any generic \"just attach an image\" caller keep working.\n role ProductImageRole @default(GALLERY)\n}\n\nenum ProductImageRole {\n FEATURED\n FEATURED_MOBILE\n GALLERY\n GALLERY_MOBILE\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 /// Legacy ProductVariant.qty — on-hand stock. The cart refuses a variant with\n /// qty 0 unless it is flagged pre-order, so both fields are required for that\n /// guard to work (see apps/admin-panel/src/stores/cart.ts).\n qty Int @default(0)\n /// Legacy is_pre_order: orderable even at zero stock.\n isPreOrder Boolean @default(false)\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 /// Which sales channel placed this order — see OrderChannel.\n channel OrderChannel @default(RETAIL)\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 /// Instrument metadata from the legacy \"cash\" document form. Which fields are\n /// filled depends on sourceType: a melted-gold receipt carries lab/hallmark and\n /// the pre-normalization weight, a cheque carries the account and bank, and a\n /// transfer carries its destination.\n /// Legacy columns: lab, ang, account_name, bank_name, to, original_value, recive_date.\n labName String?\n hallmark String?\n accountName String?\n bankName String?\n transferTo String?\n /// Weight as handed over, BEFORE karat normalization (legacy original_value).\n originalValue Decimal? @db.Decimal(14, 4)\n /// Rial amount for cash/cheque documents (legacy price).\n priceRial Decimal? @db.Decimal(20, 2)\n receivedAt DateTime?\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\n// ─────────────────────────────────────────────────────────────────────────\n// Admin ACL — faithful port of the legacy granular permission model.\n//\n// Legacy (henza_server):\n// Schema::create('permission', function (Blueprint $table) {\n// $table->string('rid');\n// $table->integer('ut');\n// $table->primary(['rid' , 'ut']);\n// });\n//\n// Permissions attach to the USER TYPE (`ut`), never to an individual user, so\n// every admin of a given type shares one permission set. `ut` pointed at a row\n// in the legacy `config_static` lookup table; UserType is that table, narrowed\n// to the admin-user-type rows it was actually used for.\n//\n// A `rid` is a dotted permission id such as \"persons.list.client.omde\". The\n// frontend expands each stored rid into its ancestors at runtime via\n// checkParent() — see apps/admin-panel/src/stores/users.ts. Only the 58 leaf\n// rids the routers actually gate are seeded; ancestors are derived, not stored.\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel UserType {\n /// Legacy `permission.ut` / `users.ut` — an integer, kept as the PK so seeded\n /// rows keep their legacy identity rather than getting new cuids.\n id Int @id\n title String\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n permissions Permission[]\n adminUsers AdminUser[]\n\n @@map(\"user_type\")\n}\n\nmodel Permission {\n /// Dotted permission id, e.g. \"order.list\", \"persons.list.client.omde\".\n rid String\n userTypeId Int\n userType UserType @relation(fields: [userTypeId], references: [id], onDelete: Cascade)\n\n /// Legacy's composite primary key (rid, ut), preserved exactly.\n @@id([rid, userTypeId])\n @@index([userTypeId])\n @@map(\"permission\")\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Product comments / reviews — legacy `comment` table (App\\Models\\Comment),\n// moderated from views/customer/Comments/Comments.vue.\n//\n// Legacy fillable: product_id, comment, reply_to, sts, customer_id\n// Legacy Comment::$STS = [1 => 'در انتظار تایید', 2 => 'تایید شده', 3 => 'رد شده']\n// which is represented here as an enum rather than a magic integer.\n// ─────────────────────────────────────────────────────────────────────────\n\nenum CommentStatus {\n PENDING // 1 — در انتظار تایید\n APPROVED // 2 — تایید شده\n REJECTED // 3 — رد شده\n}\n\nmodel Comment {\n id String @id @default(cuid())\n productId String\n product Product @relation(fields: [productId], references: [id])\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n body String\n status CommentStatus @default(PENDING)\n\n /// Legacy `reply_to`: a staff reply threads under the comment it answers.\n replyToId String?\n replyTo Comment? @relation(\"CommentReplies\", fields: [replyToId], references: [id])\n replies Comment[] @relation(\"CommentReplies\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([productId])\n @@index([customerId])\n @@index([status])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Settings — replaces legacy `config_static`, a key/value table the admin\n// panel read and wrote through endpoints like /wallet/config and\n// /const/__list__ut. Values are stored as JSON so a setting can hold a scalar,\n// an object, or the dynamic option lists legacy kept under `__list__*` keys.\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel Setting {\n key String @id\n value Json\n updatedAt DateTime @updatedAt\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Promotions / discounts — legacy views/promotion/*, served by\n// PromotionController (`product/variant/promotion`). The redesigned schema had\n// no promotion concept at all; this models what the legacy forms actually\n// collect.\n// ─────────────────────────────────────────────────────────────────────────\n\n/// Legacy Promotion.type — what the discount is scoped to.\nenum PromotionScope {\n PRODUCT // 1 محصولات\n CATEGORY // 2 دسته بندی\n CUSTOMER // 3 مشتری\n BRAND // 4 برند\n}\n\n/// Legacy Promotion.use_only — how often a customer may redeem it.\nenum PromotionUsage {\n ONCE // 1 فقط یکبار\n UNTIL_END_DATE // 2 تا تاریخ پایان\n}\n\nmodel Promotion {\n id String @id @default(cuid())\n title String\n description String?\n scope PromotionScope @default(PRODUCT)\n usage PromotionUsage @default(ONCE)\n /// Percentage off, 0-100 (legacy discount_percent).\n discountPercent Decimal @db.Decimal(6, 3)\n isActive Boolean @default(true)\n /// Which storefront it shows on, mirroring Product.showType (legacy platform).\n platform Int @default(3)\n\n startsAt DateTime?\n endsAt DateTime?\n\n /// Exactly one of these is populated, per `scope`.\n productId String?\n product Product? @relation(fields: [productId], references: [id])\n categoryId String?\n category Category? @relation(fields: [categoryId], references: [id])\n customerId String?\n customer Customer? @relation(fields: [customerId], references: [id])\n brandId String?\n brand Brand? @relation(fields: [brandId], references: [id])\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([scope, isActive])\n}\n\n/// Legacy \"ignore\" table (views/offersIgnore) — specific products, categories,\n/// customers or brands excluded from the automatic discount engine.\n/// Legacy posts { type, item_id[] }: \"type\" reuses the promotion scope codes\n/// (1 محصولات | 2 دسته بندی | 3 مشتری | 4 برند) and \"itemId\" is the excluded row.\nmodel PromotionExclusion {\n id String @id @default(cuid())\n type Int\n itemId String\n createdAt DateTime @default(now())\n\n @@unique([type, itemId])\n @@index([type])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Branch stock allocation — legacy views/invoice/* (\"فاکتور انتقال\"), served by\n// BranchController as `branch/product`. Despite the screen being called an\n// invoice, it is not a sales document: it records which products head office\n// allocated to a branch, and who allocated them.\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel BranchAllocation {\n id String @id @default(cuid())\n branchId String\n branch Branch @relation(fields: [branchId], references: [id])\n\n /// Legacy `allocator_user` — the admin who filed the allocation.\n allocatorAdminUserId String?\n allocatorAdminUser AdminUser? @relation(\"AllocationAuthor\", fields: [allocatorAdminUserId], references: [id])\n\n note String?\n createdAt DateTime @default(now())\n\n items BranchAllocationItem[]\n\n @@index([branchId])\n}\n\nmodel BranchAllocationItem {\n id String @id @default(cuid())\n allocationId String\n allocation BranchAllocation @relation(fields: [allocationId], references: [id], onDelete: Cascade)\n productId String\n product Product @relation(fields: [productId], references: [id])\n /// Weight snapshot at allocation time, so later catalog edits do not rewrite history.\n weightGrams Decimal? @db.Decimal(14, 4)\n\n @@unique([allocationId, productId])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Messaging + audit — legacy views/sms, views/notify, views/newsletters and\n// views/statics. None of these had a model in the redesigned schema.\n// ─────────────────────────────────────────────────────────────────────────\n\n/// Who an SMS or notification was aimed at. Legacy's sms/create offered these\n/// audiences, each gated by its own rid (sms.create.all, .omde, .single, ...).\nenum MessageAudience {\n SINGLE_NUMBER // یک شماره\n MULTIPLE_NUMBERS // چند شماره\n ALL_CUSTOMERS // همه\n WHOLESALE_CUSTOMERS // عمده\n RETAIL_CUSTOMERS // تک\n SALES_AGENTS // کارشناسان فروش\n}\n\nmodel SmsMessage {\n id String @id @default(cuid())\n audience MessageAudience\n /// Comma-separated recipients for the SINGLE/MULTIPLE audiences.\n recipients String?\n body String\n /// How many numbers the send actually resolved to.\n sentCount Int @default(0)\n\n sentByAdminUserId String?\n sentByAdminUser AdminUser? @relation(\"SmsAuthor\", fields: [sentByAdminUserId], references: [id])\n\n createdAt DateTime @default(now())\n\n @@index([createdAt])\n}\n\nmodel Notification {\n id String @id @default(cuid())\n title String\n body String\n audience MessageAudience @default(ALL_CUSTOMERS)\n\n sentByAdminUserId String?\n sentByAdminUser AdminUser? @relation(\"NotificationAuthor\", fields: [sentByAdminUserId], references: [id])\n\n createdAt DateTime @default(now())\n\n recipients NotificationRecipient[]\n\n @@index([createdAt])\n}\n\n/// Per-customer delivery + read state — legacy `GET notify/read/{id}`.\nmodel NotificationRecipient {\n id String @id @default(cuid())\n notificationId String\n notification Notification @relation(fields: [notificationId], references: [id], onDelete: Cascade)\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n readAt DateTime?\n\n @@unique([notificationId, customerId])\n @@index([customerId, readAt])\n}\n\n/// Legacy views/newsletters — the mailing-list signups collected by the site.\nmodel NewsletterSubscriber {\n id String @id @default(cuid())\n email String?\n mobile String?\n createdAt DateTime @default(now())\n\n @@unique([email])\n @@unique([mobile])\n}\n\n/// Legacy views/statics — the customer activity log the \"آمار\" screen reads\n/// (`log/list/customer`, `log/ago/{customerId}`).\nmodel CustomerActivityLog {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n /// Free-form action key, e.g. 'login', 'order.placed', 'profile.updated'.\n action String\n detail String?\n createdAt DateTime @default(now())\n\n @@index([customerId, createdAt])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Uploads — replaces the legacy MyUpload / SingleUploadInput widgets, which\n// POSTed to a file endpoint and stored the returned URL on the owning record.\n// Ports of those forms currently take a URL by hand; this gives them a real\n// destination to upload to.\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel UploadedFile {\n id String @id @default(cuid())\n /// Public URL the stored file is served from.\n url String\n originalName String\n mimeType String\n sizeBytes Int\n /// Free-form tag for what the file belongs to, e.g. 'product', 'category',\n /// 'customer.nationalId'. Kept loose so a new form needs no schema change.\n purpose String?\n\n uploadedByAdminUserId String?\n uploadedByAdminUser AdminUser? @relation(\"FileUploader\", fields: [uploadedByAdminUserId], references: [id])\n\n createdAt DateTime @default(now())\n customerAvatars Customer[] @relation(\"CustomerAvatar\")\n\n @@index([purpose])\n}\n", - "inlineSchemaHash": "2a0cac26ecbad0f3dec2805682be8f2a8b95feeb6b57cd369f94c84ce9997c84", + "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\n/// Legacy Customer.type — what kind of wholesale customer this is. Values and\n/// labels taken verbatim from bulkCustomer.vue's userTypes list.\n///\n/// SALES_AGENT is a LEGACY value only: legacy stored a sales agent as a\n/// Customer row with type=1, but the redesigned system models a sales agent\n/// as an AdminUser (role SALES_EXPERT) instead — see admin-users.service.ts\n/// and admin-customers.service.ts's listReagents()/create(). No Customer row\n/// should be created with kind SALES_AGENT going forward; the value is kept\n/// only so legacyTypeToKind()/kindToLegacyType() stay total functions and any\n/// already-migrated data keeps a readable label.\n///\n/// RETAIL marks a \"تک\" (single/retail) customer — legacy kept these in an\n/// entirely separate table (SingleCustomer/`login`) that could never contain\n/// a wholesale/agent/shopkeeper/banker row. The redesigned schema folded both\n/// into one Customer model, so this value is what listSingle() now filters on\n/// to reproduce that same separation.\nenum CustomerKind {\n SALES_AGENT // legacy 1 کارشناس فروش — superseded by AdminRole.SALES_EXPERT, do not use for new rows\n APP_SHOPKEEPER // 2 مغازه دار اپلیکیشن\n WALK_IN_SHOPKEEPER // 3 مغازه دار حضوری\n BANKER // 4 بنکدار\n WORKSHOP_SUPERVISOR // 5 سرپرست کارگاه\n RETAIL // مشتری تک — legacy SingleCustomer\n}\n\nenum VerificationStep {\n UNVERIFIED\n STEP_ONE_SUBMITTED\n STEP_TWO_SUBMITTED\n VERIFIED\n}\n\n/// Legacy kept retail and wholesale orders in two SEPARATE tables\n/// (orders_orders vs orders). The redesigned schema has one Order model, so the\n/// distinction lives here instead.\nenum OrderChannel {\n RETAIL // تک فروشی\n WHOLESALE // عمده فروشی\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 /// Legacy cities.percent / percent_aux — per-city wage surcharges the city\n /// screen edits through GET cities/get-percent/{id} and get-percent-aux/{id}.\n percent Decimal? @db.Decimal(6, 3)\n percentAux Decimal? @db.Decimal(6, 3)\n customers Customer[]\n addresses CustomerAddress[]\n branches Branch[]\n preOrders PreOrder[]\n salesExpertProfiles SalesExpertProfile[]\n}\n\nmodel AdminUser {\n id String @id @default(cuid())\n\n /// ⚠️ Kept in sync BY A DATABASE TRIGGER — `admin_user_sync_full_name`, from\n /// migration 20260922180000. Prisma cannot see it, so a write here may store\n /// something other than what you passed:\n ///\n /// * write the parts -> fullName is rebuilt as \"first last\"\n /// * write fullName only -> the parts are re-derived from it\n /// * write BOTH in one update -> THE PARTS WIN; the submitted fullName is\n /// discarded\n ///\n /// Re-read after writing if you need the stored value. Same arrangement as\n /// Customer (D0 requirement 7 — the split applies to ALL admins, not only\n /// sales experts).\n fullName String\n firstName String?\n lastName String?\n\n mobile String @unique\n passwordHash String\n role AdminRole\n /// Legacy `users.ut`. Granular ACL lives here (Permission rows hang off UserType);\n /// the coarse `role` enum above is retained for the business rules that already\n /// switch on it. Nullable so existing seeded admins remain valid.\n userTypeId Int?\n userType UserType? @relation(fields: [userTypeId], references: [id])\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 /// Legacy stored a sales agent as a `customers` row with type=1, so\n /// ReagentCustomer.vue's shared form collected these alongside the\n /// wholesale-customer fields. A sales agent is now an AdminUser\n /// (role=SALES_EXPERT), so these live here instead — only ever set for that\n /// role; every other AdminUser leaves them null.\n avatarFileId String?\n avatarFile UploadedFile? @relation(\"AdminUserAvatar\", fields: [avatarFileId], references: [id])\n address String?\n storeName String? // legacy store_name\n pasazhName String? // legacy pasazh_name\n memberCode String? // legacy mcode\n\n referredCustomers Customer[] @relation(\"ReferralAgent\")\n supervisedWorkshops Workshop[] @relation(\"WorkshopSupervisor\")\n branchAllocations BranchAllocation[] @relation(\"AllocationAuthor\")\n postedLedgerEntries LedgerEntry[]\n orderStatusEvents OrderStatusEvent[]\n preOrderStatusEvents PreOrderStatusEvent[]\n sentSmsMessages SmsMessage[] @relation(\"SmsAuthor\")\n sentNotifications Notification[] @relation(\"NotificationAuthor\")\n uploadedFiles UploadedFile[] @relation(\"FileUploader\")\n\n /// Only ever set for role = SALES_EXPERT (D17).\n salesExpertProfile SalesExpertProfile?\n\n @@index([role])\n @@index([userTypeId])\n @@index([avatarFileId])\n @@index([firstName])\n @@index([lastName])\n}\n\n/// The sales-expert fields legacy kept on the `customers` row (type=1) and that\n/// were lost when a sales expert became an AdminUser (D17).\n///\n/// A separate table rather than columns on AdminUser: these are meaningful only\n/// for role = SALES_EXPERT and would be permanently null for every other admin.\n/// A row is created for every SALES_EXPERT by the migration, and upserted by the\n/// service whenever one is created or a user's role becomes SALES_EXPERT — but\n/// readers still left-join, so a missing profile degrades to nulls rather than\n/// dropping the agent from the grid.\n///\n/// `reagent_id` is deliberately NOT here — see D17: in the reference it was a\n/// side effect of the shared customers form, and filling it removed that expert\n/// from the selectable-agent list (queryReagent, AdminController.php:420).\nmodel SalesExpertProfile {\n id String @id @default(cuid())\n adminUserId String @unique\n adminUser AdminUser @relation(fields: [adminUserId], references: [id], onDelete: Cascade)\n\n /// Legacy customers.city_id — a filterable grid column\n /// (ReagentListModel.js:14) and the source of the commission autofill\n /// (ReagentCustomer.vue:215-228).\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n\n /// Legacy customers.gram_level — the «سطح اعتبار» column, filterable.\n /// Decimal(14,4) matches Customer.goldCreditLimitGrams exactly so the same\n /// credit limit has identical precision on both tables.\n gramLevel Decimal @default(0) @db.Decimal(14, 4)\n\n /// Legacy customers.phone (varchar 15) and ncode (varchar 20). The reference\n /// stores both and reads neither back; kept so the form matches it. Neither\n /// is unique and neither has a validation rule in the reference — inventing\n /// one would reject data the reference accepts.\n phone String? @db.VarChar(15)\n nationalCode String? @db.VarChar(20)\n\n /// The Customer row carrying this expert's OWN orders, debt and credit limit.\n ///\n /// The reference stored a sales agent AS a Customer (type=1), so an order the\n /// agent placed from the app was simply their own, against their own\n /// gram_level (ApiMobileController.php:534-535). Here the expert is an\n /// AdminUser (D17), which has no ledger and cannot be an Order's subject, so\n /// this one linked Customer plays that part.\n ///\n /// Distinct from the expert's REFERRED customers, which are ordinary Customer\n /// rows pointing at the AdminUser through Customer.referredByAgentId.\n ///\n /// Null until the expert first signs into the app, when it is created from\n /// this profile. See D21.\n tradingCustomerId String? @unique\n tradingCustomer Customer? @relation(\"SalesExpertTradingAccount\", fields: [tradingCustomerId], references: [id], onDelete: SetNull)\n\n createdAt DateTime @default(now())\n updatedAt DateTime @default(now()) @updatedAt\n\n @@index([cityId])\n}\n\nmodel Customer {\n id String @id @default(cuid())\n\n /// ⚠️ `fullName`, `firstName` and `lastName` are kept in sync BY A DATABASE\n /// TRIGGER — `customer_sync_full_name`, created in migration\n /// 20260922120000_customer_name_parts. Prisma does not know about it, so a\n /// write here is NOT the whole story:\n ///\n /// * write the parts -> fullName is rebuilt as \"first last\"\n /// * write fullName only -> the parts are re-derived from it\n /// * write BOTH in one update -> THE PARTS WIN; fullName is overwritten\n /// with \"first last\" and whatever you passed for fullName is discarded\n ///\n /// So a value you wrote can differ from the value that lands in the row.\n /// Re-read after writing if you need the stored value. Prefer writing the\n /// parts; `fullName` exists for the legacy readers that predate D0.\n ///\n /// Reference has these as two columns from the start\n /// (henza_server 2020_10_01_120430_customers_table.php:18-19 fname/lname);\n /// the port collapsed them, which is the D0 defect.\n fullName String\n firstName String?\n lastName String?\n\n mobile String @unique\n nationalCode String @unique\n passwordHash String\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n\n /// Legacy Customer.type — distinguishes بنکدار from مغازه دار, which the\n /// BankerList / ShopkeeperList screens filter on.\n kind CustomerKind @default(APP_SHOPKEEPER)\n\n /// Legacy contact + shop columns, shown on the wholesale customer form and as\n /// (hidden-by-default) columns in its grid.\n phone String?\n address String?\n storeName String?\n pasazhName String?\n zone String?\n /// Legacy mcode — the operator-assigned customer code.\n memberCode String?\n\n referredByAgentId String?\n referredByAgent AdminUser? @relation(\"ReferralAgent\", fields: [referredByAgentId], references: [id])\n\n /// Set only on the one Customer row that acts as a sales expert's own trading\n /// account (see SalesExpertProfile.tradingCustomerId). Empty for every\n /// ordinary customer.\n salesExpertTradingFor SalesExpertProfile? @relation(\"SalesExpertTradingAccount\")\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 /// Legacy `customers.percent` — the per-customer making-charge offset. It is\n /// ADDED to the variant's making charge when pricing an order line\n /// (henza_pwa src/Mixins/product/index.ts:69-75:\n /// getWage(v) = state.percent + v.percentage_pay)\n /// and it shifts the catalogue making-charge filter bounds\n /// (ApiMobileController.php:391-396,76-77). It is NOT part of the price\n /// formula itself — that extra percent is ConfigStatic['product_price_extra_percent'].\n ///\n /// NOT the wallet cashback rate (storefront ConfigStatic['percent'], D13) and\n /// NOT the wholesale per-line charge (OrderItem.wagePercentSnapshot).\n ///\n /// DB default 0 keeps every existing customer's price unchanged. The\n /// reference's application-layer default for NEW customers is 3\n /// (AdminController.php:338) and the field is required on create (:354).\n makingChargeOffsetPercent Decimal @default(0) @db.Decimal(6, 3)\n\n verificationStep VerificationStep @default(UNVERIFIED)\n nationalIdImageUrl String?\n businessLicenseUrl String?\n shopImageUrl String?\n\n /// Legacy `image_id` — the profile photo (عکس پروفایل) on the sales-agent\n /// form. Legacy stored an FK to its Image table and read it back as a nested\n /// `image: { id, image }`, so this is a relation, not a bare URL string.\n avatarFileId String?\n avatarFile UploadedFile? @relation(\"CustomerAvatar\", fields: [avatarFileId], references: [id])\n\n /// Legacy had TWO independent switches on a customer:\n /// sts -> may they place orders (this field)\n /// active -> is the account enabled (isActive, below)\n /// The port previously drove both from isActive; they are separate again.\n canPlaceOrders Boolean @default(true)\n\n isActive Boolean @default(true)\n /// Legacy archive flag — the archived list filtered on this, not on isActive.\n isArchived Boolean @default(false)\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 comments Comment[]\n promotions Promotion[]\n notifications NotificationRecipient[]\n activityLogs CustomerActivityLog[]\n otpChallenges CustomerOtp[]\n\n @@index([referredByAgentId])\n @@index([firstName])\n @@index([lastName])\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 /// Legacy Branch.address / phone — both are columns in the branch grid.\n address String?\n phone String?\n /// Legacy GET branch/active/{id} toggles this.\n isActive Boolean @default(true)\n cityId String?\n city City? @relation(fields: [cityId], references: [id])\n adminUsers AdminUser[]\n orders Order[]\n allocations BranchAllocation[]\n}\n\nmodel Workshop {\n id String @id @default(cuid())\n name String\n /// Legacy Workshop.address / phone — both are columns in the workshop grid.\n address String?\n phone String?\n /// The supervisor created alongside the workshop by the legacy Add form.\n supervisorId String?\n supervisor AdminUser? @relation(\"WorkshopSupervisor\", fields: [supervisorId], references: [id])\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 /// Legacy Category.type — which storefront the category belongs to:\n /// 1 سایت | 2 اپلیکشن موبایل. Drives the \"type_str\" grid column.\n type Int @default(1)\n /// Legacy Category.image / icon — the grid renders the image, the storefront the icon.\n imageUrl String?\n iconUrl String?\n /// Legacy meta tag + description, used for SEO on the site categories.\n metaTag String?\n description String?\n parentId String?\n parent Category? @relation(\"CategoryTree\", fields: [parentId], references: [id])\n children Category[] @relation(\"CategoryTree\")\n products Product[]\n promotions Promotion[]\n}\n\nmodel Brand {\n id String @id @default(cuid())\n title String\n products Product[]\n promotions Promotion[]\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 /// Legacy Product.product_code — the human-facing SKU shown in the grid.\n productCode String? @unique\n /// Legacy Product.show_type: 1 site, 2 mobile, 3 both.\n showType Int @default(3)\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n variants ProductVariant[]\n images ProductImage[]\n collections CollectionProduct[]\n comments Comment[]\n promotions Promotion[]\n allocationItems BranchAllocationItem[]\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 /// Legacy had 4 distinct upload slots (تصویر شاخص / تصویر شاخص موبایل /\n /// گالری تصاویر سایت / گالری تصاویر موبایل) — GALLERY is the default so\n /// existing rows and any generic \"just attach an image\" caller keep working.\n role ProductImageRole @default(GALLERY)\n\n /// Variants that display this image. A gallery image may back several\n /// variants (one photo, several weights), which is why this is a plain\n /// one-to-many and not a join table.\n variants ProductVariant[]\n}\n\nenum ProductImageRole {\n FEATURED\n FEATURED_MOBILE\n GALLERY\n GALLERY_MOBILE\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 /// Legacy ProductVariant.qty — on-hand stock. The cart refuses a variant with\n /// qty 0 unless it is flagged pre-order, so both fields are required for that\n /// guard to work (see apps/admin-panel/src/stores/cart.ts).\n qty Int @default(0)\n /// Legacy is_pre_order: orderable even at zero stock.\n isPreOrder Boolean @default(false)\n isActive Boolean @default(true)\n\n /// The gallery image shown for this variant, picked from its own product's\n /// images — never a re-uploaded copy. onDelete: SetNull because dropping an\n /// image must never cascade into the variant: OrderItem rows reference\n /// variants, so losing one would break order history.\n imageId String?\n image ProductImage? @relation(fields: [imageId], references: [id], onDelete: SetNull)\n\n orderItems OrderItem[]\n\n @@index([imageId])\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 /// Which sales channel placed this order — see OrderChannel.\n channel OrderChannel @default(RETAIL)\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 /// Instrument metadata from the legacy \"cash\" document form. Which fields are\n /// filled depends on sourceType: a melted-gold receipt carries lab/hallmark and\n /// the pre-normalization weight, a cheque carries the account and bank, and a\n /// transfer carries its destination.\n /// Legacy columns: lab, ang, account_name, bank_name, to, original_value, recive_date.\n labName String?\n hallmark String?\n accountName String?\n bankName String?\n transferTo String?\n /// Weight as handed over, BEFORE karat normalization (legacy original_value).\n originalValue Decimal? @db.Decimal(14, 4)\n /// Rial amount for cash/cheque documents (legacy price).\n priceRial Decimal? @db.Decimal(20, 2)\n receivedAt DateTime?\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\n// ─────────────────────────────────────────────────────────────────────────\n// Admin ACL — faithful port of the legacy granular permission model.\n//\n// Legacy (henza_server):\n// Schema::create('permission', function (Blueprint $table) {\n// $table->string('rid');\n// $table->integer('ut');\n// $table->primary(['rid' , 'ut']);\n// });\n//\n// Permissions attach to the USER TYPE (`ut`), never to an individual user, so\n// every admin of a given type shares one permission set. `ut` pointed at a row\n// in the legacy `config_static` lookup table; UserType is that table, narrowed\n// to the admin-user-type rows it was actually used for.\n//\n// A `rid` is a dotted permission id such as \"persons.list.client.omde\". The\n// frontend expands each stored rid into its ancestors at runtime via\n// checkParent() — see apps/admin-panel/src/stores/users.ts. Only the 58 leaf\n// rids the routers actually gate are seeded; ancestors are derived, not stored.\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel UserType {\n /// Legacy `permission.ut` / `users.ut` — an integer, kept as the PK so seeded\n /// rows keep their legacy identity rather than getting new cuids.\n id Int @id\n title String\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n permissions Permission[]\n adminUsers AdminUser[]\n\n @@map(\"user_type\")\n}\n\nmodel Permission {\n /// Dotted permission id, e.g. \"order.list\", \"persons.list.client.omde\".\n rid String\n userTypeId Int\n userType UserType @relation(fields: [userTypeId], references: [id], onDelete: Cascade)\n\n /// Legacy's composite primary key (rid, ut), preserved exactly.\n @@id([rid, userTypeId])\n @@index([userTypeId])\n @@map(\"permission\")\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Product comments / reviews — legacy `comment` table (App\\Models\\Comment),\n// moderated from views/customer/Comments/Comments.vue.\n//\n// Legacy fillable: product_id, comment, reply_to, sts, customer_id\n// Legacy Comment::$STS = [1 => 'در انتظار تایید', 2 => 'تایید شده', 3 => 'رد شده']\n// which is represented here as an enum rather than a magic integer.\n// ─────────────────────────────────────────────────────────────────────────\n\nenum CommentStatus {\n PENDING // 1 — در انتظار تایید\n APPROVED // 2 — تایید شده\n REJECTED // 3 — رد شده\n}\n\nmodel Comment {\n id String @id @default(cuid())\n productId String\n product Product @relation(fields: [productId], references: [id])\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n body String\n status CommentStatus @default(PENDING)\n\n /// Legacy `reply_to`: a staff reply threads under the comment it answers.\n replyToId String?\n replyTo Comment? @relation(\"CommentReplies\", fields: [replyToId], references: [id])\n replies Comment[] @relation(\"CommentReplies\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([productId])\n @@index([customerId])\n @@index([status])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Settings — replaces legacy `config_static`, a key/value table the admin\n// panel read and wrote through endpoints like /wallet/config and\n// /const/__list__ut. Values are stored as JSON so a setting can hold a scalar,\n// an object, or the dynamic option lists legacy kept under `__list__*` keys.\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel Setting {\n key String @id\n value Json\n updatedAt DateTime @updatedAt\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Promotions / discounts — legacy views/promotion/*, served by\n// PromotionController (`product/variant/promotion`). The redesigned schema had\n// no promotion concept at all; this models what the legacy forms actually\n// collect.\n// ─────────────────────────────────────────────────────────────────────────\n\n/// Legacy Promotion.type — what the discount is scoped to.\nenum PromotionScope {\n PRODUCT // 1 محصولات\n CATEGORY // 2 دسته بندی\n CUSTOMER // 3 مشتری\n BRAND // 4 برند\n}\n\n/// Legacy Promotion.use_only — how often a customer may redeem it.\nenum PromotionUsage {\n ONCE // 1 فقط یکبار\n UNTIL_END_DATE // 2 تا تاریخ پایان\n}\n\nmodel Promotion {\n id String @id @default(cuid())\n title String\n description String?\n scope PromotionScope @default(PRODUCT)\n usage PromotionUsage @default(ONCE)\n /// Percentage off, 0-100 (legacy discount_percent).\n discountPercent Decimal @db.Decimal(6, 3)\n isActive Boolean @default(true)\n /// Which storefront it shows on, mirroring Product.showType (legacy platform).\n platform Int @default(3)\n\n startsAt DateTime?\n endsAt DateTime?\n\n /// Exactly one of these is populated, per `scope`.\n productId String?\n product Product? @relation(fields: [productId], references: [id])\n categoryId String?\n category Category? @relation(fields: [categoryId], references: [id])\n customerId String?\n customer Customer? @relation(fields: [customerId], references: [id])\n brandId String?\n brand Brand? @relation(fields: [brandId], references: [id])\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([scope, isActive])\n}\n\n/// Legacy \"ignore\" table (views/offersIgnore) — specific products, categories,\n/// customers or brands excluded from the automatic discount engine.\n/// Legacy posts { type, item_id[] }: \"type\" reuses the promotion scope codes\n/// (1 محصولات | 2 دسته بندی | 3 مشتری | 4 برند) and \"itemId\" is the excluded row.\nmodel PromotionExclusion {\n id String @id @default(cuid())\n type Int\n itemId String\n createdAt DateTime @default(now())\n\n @@unique([type, itemId])\n @@index([type])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Branch stock allocation — legacy views/invoice/* (\"فاکتور انتقال\"), served by\n// BranchController as `branch/product`. Despite the screen being called an\n// invoice, it is not a sales document: it records which products head office\n// allocated to a branch, and who allocated them.\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel BranchAllocation {\n id String @id @default(cuid())\n branchId String\n branch Branch @relation(fields: [branchId], references: [id])\n\n /// Legacy `allocator_user` — the admin who filed the allocation.\n allocatorAdminUserId String?\n allocatorAdminUser AdminUser? @relation(\"AllocationAuthor\", fields: [allocatorAdminUserId], references: [id])\n\n note String?\n createdAt DateTime @default(now())\n\n items BranchAllocationItem[]\n\n @@index([branchId])\n}\n\nmodel BranchAllocationItem {\n id String @id @default(cuid())\n allocationId String\n allocation BranchAllocation @relation(fields: [allocationId], references: [id], onDelete: Cascade)\n productId String\n product Product @relation(fields: [productId], references: [id])\n /// Weight snapshot at allocation time, so later catalog edits do not rewrite history.\n weightGrams Decimal? @db.Decimal(14, 4)\n\n @@unique([allocationId, productId])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Messaging + audit — legacy views/sms, views/notify, views/newsletters and\n// views/statics. None of these had a model in the redesigned schema.\n// ─────────────────────────────────────────────────────────────────────────\n\n/// Who an SMS or notification was aimed at. Legacy's sms/create offered these\n/// audiences, each gated by its own rid (sms.create.all, .omde, .single, ...).\nenum MessageAudience {\n SINGLE_NUMBER // یک شماره\n MULTIPLE_NUMBERS // چند شماره\n ALL_CUSTOMERS // همه\n WHOLESALE_CUSTOMERS // عمده\n RETAIL_CUSTOMERS // تک\n SALES_AGENTS // کارشناسان فروش\n}\n\nmodel SmsMessage {\n id String @id @default(cuid())\n audience MessageAudience\n /// Comma-separated recipients for the SINGLE/MULTIPLE audiences.\n recipients String?\n body String\n /// How many numbers the send actually resolved to.\n sentCount Int @default(0)\n\n sentByAdminUserId String?\n sentByAdminUser AdminUser? @relation(\"SmsAuthor\", fields: [sentByAdminUserId], references: [id])\n\n createdAt DateTime @default(now())\n\n @@index([createdAt])\n}\n\nmodel Notification {\n id String @id @default(cuid())\n title String\n body String\n audience MessageAudience @default(ALL_CUSTOMERS)\n\n sentByAdminUserId String?\n sentByAdminUser AdminUser? @relation(\"NotificationAuthor\", fields: [sentByAdminUserId], references: [id])\n\n createdAt DateTime @default(now())\n\n recipients NotificationRecipient[]\n\n @@index([createdAt])\n}\n\n/// Per-customer delivery + read state — legacy `GET notify/read/{id}`.\nmodel NotificationRecipient {\n id String @id @default(cuid())\n notificationId String\n notification Notification @relation(fields: [notificationId], references: [id], onDelete: Cascade)\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n readAt DateTime?\n\n @@unique([notificationId, customerId])\n @@index([customerId, readAt])\n}\n\n/// Legacy views/newsletters — the mailing-list signups collected by the site.\nmodel NewsletterSubscriber {\n id String @id @default(cuid())\n email String?\n mobile String?\n createdAt DateTime @default(now())\n\n @@unique([email])\n @@unique([mobile])\n}\n\n/// Legacy views/statics — the customer activity log the \"آمار\" screen reads\n/// (`log/list/customer`, `log/ago/{customerId}`).\nmodel CustomerActivityLog {\n id String @id @default(cuid())\n customerId String\n customer Customer @relation(fields: [customerId], references: [id])\n /// Free-form action key, e.g. 'login', 'order.placed', 'profile.updated'.\n action String\n detail String?\n createdAt DateTime @default(now())\n\n @@index([customerId, createdAt])\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Uploads — replaces the legacy MyUpload / SingleUploadInput widgets, which\n// POSTed to a file endpoint and stored the returned URL on the owning record.\n// Ports of those forms currently take a URL by hand; this gives them a real\n// destination to upload to.\n// ─────────────────────────────────────────────────────────────────────────\n\nmodel UploadedFile {\n id String @id @default(cuid())\n /// Public URL the stored file is served from.\n url String\n originalName String\n mimeType String\n sizeBytes Int\n /// Free-form tag for what the file belongs to, e.g. 'product', 'category',\n /// 'customer.nationalId'. Kept loose so a new form needs no schema change.\n purpose String?\n\n uploadedByAdminUserId String?\n uploadedByAdminUser AdminUser? @relation(\"FileUploader\", fields: [uploadedByAdminUserId], references: [id])\n\n createdAt DateTime @default(now())\n customerAvatars Customer[] @relation(\"CustomerAvatar\")\n adminUserAvatars AdminUser[] @relation(\"AdminUserAvatar\")\n\n @@index([purpose])\n}\n\n/// One OTP login challenge for a customer.\n///\n/// Ports henza_server's flow (ApiMobileController.php:39-78), where userAuth\n/// generated rand(1000,9999), stored it on the customer row as `send_password`\n/// and sent it by SMS, and userVerfiy compared the submitted code against it\n/// (mismatch -> HTTP 422).\n///\n/// Three deliberate improvements over the reference, approved by the user\n/// (docs/parity/DECISIONS.md D6 and T1):\n/// 1. the code is stored as a bcrypt hash, never in plain text — the\n/// reference kept it readable in a customer column;\n/// 2. it expires (`expiresAt`) — the reference's code stayed valid forever;\n/// 3. attempts are counted and the challenge locks out — the reference had\n/// no limit at all.\n/// The parity behaviour itself is the code comparison and the rejection.\nmodel CustomerOtp {\n id String @id @default(cuid())\n /// Normalised to English digits before storing, as the reference does with\n /// Utility::faTOen() on both send and verify.\n mobile String\n customerId String\n customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)\n\n /// bcrypt hash of the 4-digit code. Never the code itself.\n codeHash String\n expiresAt DateTime\n /// Failed verify attempts against THIS challenge.\n attempts Int @default(0)\n /// Set when the code is accepted, so one challenge cannot be reused.\n consumedAt DateTime?\n\n /// Kept for the per-IP send throttle.\n requestIp String?\n createdAt DateTime @default(now())\n\n @@index([mobile, createdAt])\n @@index([customerId])\n @@index([expiresAt])\n}\n\n/// Bespoke migration ledger left by the EARLIER, pre-Prisma port.\n///\n/// It predates Prisma Migrate in this repo and holds 12 historical rows\n/// (`0001_init.sql`, `0002_admin_schema.sql`, `seed:initial`, and the early\n/// `2026091…` migrations). It is the only record of how that port was applied,\n/// so it is **kept and never written to or deleted from** (DEAD-07).\n///\n/// It is declared here purely so `prisma migrate diff` stops proposing\n/// `DROP TABLE \"_goldbank_migrations\"` — an undeclared table reads as drift.\n/// `@@ignore` keeps it out of Prisma Client: nothing in the application should\n/// ever read or write it, and Prisma Migrate will not manage its shape either.\n///\n/// Prisma's own ledger is `_prisma_migrations`, which is separate and is never\n/// edited by hand (D14).\nmodel goldbank_migrations {\n name String @id\n appliedAt DateTime @default(now()) @map(\"applied_at\") @db.Timestamptz(6)\n\n @@map(\"_goldbank_migrations\")\n @@ignore\n}\n", + "inlineSchemaHash": "e290014d6a3917ba9e61ab09b945e2d1aca56498b54593681f437b770dcb227c", "copyEngine": true } @@ -696,7 +735,7 @@ if (!fs.existsSync(path.join(__dirname, 'schema.prisma'))) { config.isBundled = true } -config.runtimeDataModel = JSON.parse("{\"models\":{\"City\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"province\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"percent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy cities.percent / percent_aux — per-city wage surcharges the city\\\\nscreen edits through GET cities/get-percent/{id} and get-percent-aux/{id}.\"},{\"name\":\"percentAux\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customers\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CityToCustomer\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"addresses\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CustomerAddress\",\"relationName\":\"CityToCustomerAddress\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branches\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Branch\",\"relationName\":\"BranchToCity\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"CityToPreOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"AdminUser\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fullName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mobile\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"passwordHash\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"role\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminRole\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"userTypeId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `users.ut`. Granular ACL lives here (Permission rows hang off UserType);\\\\nthe coarse `role` enum above is retained for the business rules that already\\\\nswitch on it. Nullable so existing seeded admins remain valid.\"},{\"name\":\"userType\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UserType\",\"relationName\":\"AdminUserToUserType\",\"relationFromFields\":[\"userTypeId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branchId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branch\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Branch\",\"relationName\":\"AdminUserToBranch\",\"relationFromFields\":[\"branchId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"wagePercent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"referredCustomers\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"ReferralAgent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"supervisedWorkshops\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Workshop\",\"relationName\":\"WorkshopSupervisor\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branchAllocations\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocation\",\"relationName\":\"AllocationAuthor\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"postedLedgerEntries\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerEntry\",\"relationName\":\"AdminUserToLedgerEntry\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderStatusEvents\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderStatusEvent\",\"relationName\":\"AdminUserToOrderStatusEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrderStatusEvents\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrderStatusEvent\",\"relationName\":\"AdminUserToPreOrderStatusEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentSmsMessages\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"SmsMessage\",\"relationName\":\"SmsAuthor\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentNotifications\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Notification\",\"relationName\":\"NotificationAuthor\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"uploadedFiles\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UploadedFile\",\"relationName\":\"FileUploader\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Customer\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fullName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mobile\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"nationalCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"passwordHash\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"CityToCustomer\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"kind\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"CustomerKind\",\"default\":\"APP_SHOPKEEPER\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Customer.type — distinguishes بنکدار from مغازه دار, which the\\\\nBankerList / ShopkeeperList screens filter on.\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy contact + shop columns, shown on the wholesale customer form and as\\\\n(hidden-by-default) columns in its grid.\"},{\"name\":\"address\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"storeName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"pasazhName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"zone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"memberCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy mcode — the operator-assigned customer code.\"},{\"name\":\"referredByAgentId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"referredByAgent\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"ReferralAgent\",\"relationFromFields\":[\"referredByAgentId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"goldCreditLimitGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"goldDebtGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fiatDebtRial\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"verificationStep\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"VerificationStep\",\"default\":\"UNVERIFIED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"nationalIdImageUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"businessLicenseUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"shopImageUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"avatarFileId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `image_id` — the profile photo (عکس پروفایل) on the sales-agent\\\\nform. Legacy stored an FK to its Image table and read it back as a nested\\\\n`image: { id, image }`, so this is a relation, not a bare URL string.\"},{\"name\":\"avatarFile\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UploadedFile\",\"relationName\":\"CustomerAvatar\",\"relationFromFields\":[\"avatarFileId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"canPlaceOrders\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy had TWO independent switches on a customer:\\\\nsts -> may they place orders (this field)\\\\nactive -> is the account enabled (isActive, below)\\\\nThe port previously drove both from isActive; they are separate again.\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isArchived\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy archive flag — the archived list filtered on this, not on isActive.\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"addresses\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CustomerAddress\",\"relationName\":\"CustomerToCustomerAddress\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"CustomerToOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"CustomerToPreOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ledgerEntries\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerEntry\",\"relationName\":\"CustomerToLedgerEntry\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"walletBonuses\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"WalletBonus\",\"relationName\":\"CustomerToWalletBonus\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"comments\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Comment\",\"relationName\":\"CommentToCustomer\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"promotions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Promotion\",\"relationName\":\"CustomerToPromotion\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"notifications\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"NotificationRecipient\",\"relationName\":\"CustomerToNotificationRecipient\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"activityLogs\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CustomerActivityLog\",\"relationName\":\"CustomerToCustomerActivityLog\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"CustomerAddress\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToCustomerAddress\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"CityToCustomerAddress\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"addressLine\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isDefault\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Branch\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"address\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Branch.address / phone — both are columns in the branch grid.\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy GET branch/active/{id} toggles this.\"},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"BranchToCity\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"adminUsers\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToBranch\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"BranchToOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocations\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocation\",\"relationName\":\"BranchToBranchAllocation\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Workshop\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"address\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Workshop.address / phone — both are columns in the workshop grid.\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"supervisorId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"The supervisor created alongside the workshop by the legacy Add form.\"},{\"name\":\"supervisor\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"WorkshopSupervisor\",\"relationFromFields\":[\"supervisorId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"PreOrderToWorkshop\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Category\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"type\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":1,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Category.type — which storefront the category belongs to:\\\\n1 سایت | 2 اپلیکشن موبایل. Drives the \\\"type_str\\\" grid column.\"},{\"name\":\"imageUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Category.image / icon — the grid renders the image, the storefront the icon.\"},{\"name\":\"iconUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"metaTag\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy meta tag + description, used for SEO on the site categories.\"},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"parentId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"parent\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Category\",\"relationName\":\"CategoryTree\",\"relationFromFields\":[\"parentId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"children\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Category\",\"relationName\":\"CategoryTree\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"products\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"CategoryToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"promotions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Promotion\",\"relationName\":\"CategoryToPromotion\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Brand\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"products\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"BrandToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"promotions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Promotion\",\"relationName\":\"BrandToPromotion\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Collection\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"products\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CollectionProduct\",\"relationName\":\"CollectionToCollectionProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"Curated product groupings — replaces legacy \\\"Ciny/CinyProduct\\\" (a tagging\\\\nfeature, NOT a gold-price snapshot mechanism, despite the misleading legacy name).\"},\"CollectionProduct\":{\"dbName\":null,\"fields\":[{\"name\":\"collectionId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"collection\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Collection\",\"relationName\":\"CollectionToCollectionProduct\",\"relationFromFields\":[\"collectionId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"CollectionProductToProduct\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":{\"name\":null,\"fields\":[\"collectionId\",\"productId\"]},\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Product\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"categoryId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"category\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Category\",\"relationName\":\"CategoryToProduct\",\"relationFromFields\":[\"categoryId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"brandId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"brand\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Brand\",\"relationName\":\"BrandToProduct\",\"relationFromFields\":[\"brandId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Product.product_code — the human-facing SKU shown in the grid.\"},{\"name\":\"showType\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":3,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Product.show_type: 1 site, 2 mobile, 3 both.\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"variants\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ProductVariant\",\"relationName\":\"ProductToProductVariant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"images\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ProductImage\",\"relationName\":\"ProductToProductImage\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"collections\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CollectionProduct\",\"relationName\":\"CollectionProductToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"comments\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Comment\",\"relationName\":\"CommentToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"promotions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Promotion\",\"relationName\":\"ProductToPromotion\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocationItems\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocationItem\",\"relationName\":\"BranchAllocationItemToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"ProductImage\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"ProductToProductImage\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"url\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sortOrder\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"role\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"ProductImageRole\",\"default\":\"GALLERY\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy had 4 distinct upload slots (تصویر شاخص / تصویر شاخص موبایل /\\\\nگالری تصاویر سایت / گالری تصاویر موبایل) — GALLERY is the default so\\\\nexisting rows and any generic \\\"just attach an image\\\" caller keep working.\"}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"ProductVariant\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"ProductToProductVariant\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sku\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rashWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Base/raw weight before making charge (\\\"rash weight\\\").\"},{\"name\":\"defaultWagePercent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Default making-charge percentage applied on top of rash weight.\"},{\"name\":\"qty\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy ProductVariant.qty — on-hand stock. The cart refuses a variant with\\\\nqty 0 unless it is flagged pre-order, so both fields are required for that\\\\nguard to work (see apps/admin-panel/src/stores/cart.ts).\"},{\"name\":\"isPreOrder\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy is_pre_order: orderable even at zero stock.\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderItems\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderItem\",\"relationName\":\"OrderItemToProductVariant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Order\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderNumber\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToOrder\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branchId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branch\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Branch\",\"relationName\":\"BranchToOrder\",\"relationFromFields\":[\"branchId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"channel\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"OrderChannel\",\"default\":\"RETAIL\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Which sales channel placed this order — see OrderChannel.\"},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"OrderStatus\",\"default\":\"PLACED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"totalGoldWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Total gold weight incl. wage across all items (\\\"allGram\\\" / order.w).\"},{\"name\":\"wageWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Pure making-charge weight component (totalGoldWeightGrams minus raw weight sum).\"},{\"name\":\"flatRialCharge\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Flat Rial charge independent of gold price (e.g. stones) — legacy rial_input.\"},{\"name\":\"isSuspendedOverCreditLimit\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"deliveredAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"items\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderItem\",\"relationName\":\"OrderToOrderItem\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"statusEvents\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderStatusEvent\",\"relationName\":\"OrderToOrderStatusEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ledgerEntries\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerEntry\",\"relationName\":\"LedgerEntryToOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"convertedFromPreOrder\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"OrderToPreOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"OrderItem\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"order\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"OrderToOrderItem\",\"relationFromFields\":[\"orderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productVariantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productVariant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ProductVariant\",\"relationName\":\"OrderItemToProductVariant\",\"relationFromFields\":[\"productVariantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"quantity\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rashWeightGramsSnapshot\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"wagePercentSnapshot\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"discountPercentSnapshot\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"computedWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"computed: rash*(1 + (wagePercent-discount)/100)\"},{\"name\":\"flatRialCharge\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"OrderStatusEvent\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"order\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"OrderToOrderStatusEvent\",\"relationFromFields\":[\"orderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fromStatus\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderStatus\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"toStatus\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderStatus\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"note\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"actorAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"actorAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToOrderStatusEvent\",\"relationFromFields\":[\"actorAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"PreOrder\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToPreOrder\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"CityToPreOrder\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"workshopId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"workshop\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Workshop\",\"relationName\":\"PreOrderToWorkshop\",\"relationFromFields\":[\"workshopId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"PreOrderStatus\",\"default\":\"PLACED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"designImageUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"colorName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sizeSpec\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"stoneColor\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"minaColor\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"estimatedWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"estimatedPriceRial\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"convertedOrderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Once priced, an admin converts this into a real (debt-bearing) Order.\"},{\"name\":\"convertedOrder\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"OrderToPreOrder\",\"relationFromFields\":[\"convertedOrderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"statusEvents\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrderStatusEvent\",\"relationName\":\"PreOrderToPreOrderStatusEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"PreOrderStatusEvent\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrder\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"PreOrderToPreOrderStatusEvent\",\"relationFromFields\":[\"preOrderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"changes\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Json\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Field-level diff, e.g. {\\\"status\\\": {\\\"old\\\": \\\"PLACED\\\", \\\"new\\\": \\\"ADMIN_APPROVED\\\"}}\"},{\"name\":\"actorAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"actorAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToPreOrderStatusEvent\",\"relationFromFields\":[\"actorAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ipAddress\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"LedgerEntry\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToLedgerEntry\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"currency\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerCurrency\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"direction\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerDirection\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sourceType\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerSourceType\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"amount\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Always positive; direction determines the sign applied to the customer balance.\"},{\"name\":\"karatPerMille\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Purity of the physical instrument, for REPAYMENT_MELTED_GOLD (e.g. 750, 916).\"},{\"name\":\"pricePerGramRialAtEntry\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Gold price/gram at conversion time, for REPAYMENT_CASH.\"},{\"name\":\"orderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"order\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"LedgerEntryToOrder\",\"relationFromFields\":[\"orderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"confirmed\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Pending entries don't count toward the cached customer balance until confirmed.\"},{\"name\":\"confirmedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"postedByAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"postedByAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToLedgerEntry\",\"relationFromFields\":[\"postedByAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"labName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Instrument metadata from the legacy \\\"cash\\\" document form. Which fields are\\\\nfilled depends on sourceType: a melted-gold receipt carries lab/hallmark and\\\\nthe pre-normalization weight, a cheque carries the account and bank, and a\\\\ntransfer carries its destination.\\\\nLegacy columns: lab, ang, account_name, bank_name, to, original_value, recive_date.\"},{\"name\":\"hallmark\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"accountName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"bankName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"transferTo\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"originalValue\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Weight as handed over, BEFORE karat normalization (legacy original_value).\"},{\"name\":\"priceRial\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Rial amount for cash/cheque documents (legacy price).\"},{\"name\":\"receivedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"note\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"GoldPriceQuote\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"karatPerMille\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"pricePerGramRial\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"source\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"quotedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"Replaces legacy WebServiceGold / ConfigStatic.gold_price — timestamped price feed.\"},\"WalletBonus\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToWalletBonus\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"goldGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"grantedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"consumedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"Time-limited gold cashback bonus — replaces the legacy Rooyesh\\\\Wallet gold credit.\"},\"UserType\":{\"dbName\":\"user_type\",\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `permission.ut` / `users.ut` — an integer, kept as the PK so seeded\\\\nrows keep their legacy identity rather than getting new cuids.\"},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"permissions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Permission\",\"relationName\":\"PermissionToUserType\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"adminUsers\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToUserType\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Permission\":{\"dbName\":\"permission\",\"fields\":[{\"name\":\"rid\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Dotted permission id, e.g. \\\"order.list\\\", \\\"persons.list.client.omde\\\".\"},{\"name\":\"userTypeId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"userType\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UserType\",\"relationName\":\"PermissionToUserType\",\"relationFromFields\":[\"userTypeId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"Cascade\",\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":{\"name\":null,\"fields\":[\"rid\",\"userTypeId\"]},\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Comment\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"CommentToProduct\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CommentToCustomer\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"body\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"CommentStatus\",\"default\":\"PENDING\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"replyToId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `reply_to`: a staff reply threads under the comment it answers.\"},{\"name\":\"replyTo\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Comment\",\"relationName\":\"CommentReplies\",\"relationFromFields\":[\"replyToId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"replies\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Comment\",\"relationName\":\"CommentReplies\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Setting\":{\"dbName\":null,\"fields\":[{\"name\":\"key\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"value\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Json\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Promotion\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"scope\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"PromotionScope\",\"default\":\"PRODUCT\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"usage\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"PromotionUsage\",\"default\":\"ONCE\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"discountPercent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Percentage off, 0-100 (legacy discount_percent).\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"platform\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":3,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Which storefront it shows on, mirroring Product.showType (legacy platform).\"},{\"name\":\"startsAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"endsAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Exactly one of these is populated, per `scope`.\"},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"ProductToPromotion\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"categoryId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"category\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Category\",\"relationName\":\"CategoryToPromotion\",\"relationFromFields\":[\"categoryId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToPromotion\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"brandId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"brand\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Brand\",\"relationName\":\"BrandToPromotion\",\"relationFromFields\":[\"brandId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"PromotionExclusion\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"type\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"itemId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[[\"type\",\"itemId\"]],\"uniqueIndexes\":[{\"name\":null,\"fields\":[\"type\",\"itemId\"]}],\"isGenerated\":false,\"documentation\":\"Legacy \\\"ignore\\\" table (views/offersIgnore) — specific products, categories,\\\\ncustomers or brands excluded from the automatic discount engine.\\\\nLegacy posts { type, item_id[] }: \\\"type\\\" reuses the promotion scope codes\\\\n(1 محصولات | 2 دسته بندی | 3 مشتری | 4 برند) and \\\"itemId\\\" is the excluded row.\"},\"BranchAllocation\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branchId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branch\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Branch\",\"relationName\":\"BranchToBranchAllocation\",\"relationFromFields\":[\"branchId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocatorAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `allocator_user` — the admin who filed the allocation.\"},{\"name\":\"allocatorAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AllocationAuthor\",\"relationFromFields\":[\"allocatorAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"note\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"items\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocationItem\",\"relationName\":\"BranchAllocationToBranchAllocationItem\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"BranchAllocationItem\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocationId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocation\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocation\",\"relationName\":\"BranchAllocationToBranchAllocationItem\",\"relationFromFields\":[\"allocationId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"Cascade\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"BranchAllocationItemToProduct\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"weightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Weight snapshot at allocation time, so later catalog edits do not rewrite history.\"}],\"primaryKey\":null,\"uniqueFields\":[[\"allocationId\",\"productId\"]],\"uniqueIndexes\":[{\"name\":null,\"fields\":[\"allocationId\",\"productId\"]}],\"isGenerated\":false},\"SmsMessage\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"audience\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MessageAudience\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"recipients\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Comma-separated recipients for the SINGLE/MULTIPLE audiences.\"},{\"name\":\"body\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentCount\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"How many numbers the send actually resolved to.\"},{\"name\":\"sentByAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentByAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"SmsAuthor\",\"relationFromFields\":[\"sentByAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Notification\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"body\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"audience\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"MessageAudience\",\"default\":\"ALL_CUSTOMERS\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentByAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentByAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"NotificationAuthor\",\"relationFromFields\":[\"sentByAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"recipients\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"NotificationRecipient\",\"relationName\":\"NotificationToNotificationRecipient\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"NotificationRecipient\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"notificationId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"notification\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Notification\",\"relationName\":\"NotificationToNotificationRecipient\",\"relationFromFields\":[\"notificationId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"Cascade\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToNotificationRecipient\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"readAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[[\"notificationId\",\"customerId\"]],\"uniqueIndexes\":[{\"name\":null,\"fields\":[\"notificationId\",\"customerId\"]}],\"isGenerated\":false,\"documentation\":\"Per-customer delivery + read state — legacy `GET notify/read/{id}`.\"},\"NewsletterSubscriber\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"email\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mobile\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[[\"email\"],[\"mobile\"]],\"uniqueIndexes\":[{\"name\":null,\"fields\":[\"email\"]},{\"name\":null,\"fields\":[\"mobile\"]}],\"isGenerated\":false,\"documentation\":\"Legacy views/newsletters — the mailing-list signups collected by the site.\"},\"CustomerActivityLog\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToCustomerActivityLog\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"action\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Free-form action key, e.g. 'login', 'order.placed', 'profile.updated'.\"},{\"name\":\"detail\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"Legacy views/statics — the customer activity log the \\\"آمار\\\" screen reads\\\\n(`log/list/customer`, `log/ago/{customerId}`).\"},\"UploadedFile\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"url\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Public URL the stored file is served from.\"},{\"name\":\"originalName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mimeType\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sizeBytes\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"purpose\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Free-form tag for what the file belongs to, e.g. 'product', 'category',\\\\n'customer.nationalId'. Kept loose so a new form needs no schema change.\"},{\"name\":\"uploadedByAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"uploadedByAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"FileUploader\",\"relationFromFields\":[\"uploadedByAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerAvatars\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerAvatar\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false}},\"enums\":{\"AdminRole\":{\"values\":[{\"name\":\"SUPER_ADMIN\",\"dbName\":null},{\"name\":\"SYSTEM_APPROVER\",\"dbName\":null},{\"name\":\"SALES_EXPERT\",\"dbName\":null},{\"name\":\"WORKSHOP_SUPERVISOR\",\"dbName\":null},{\"name\":\"ACCOUNTANT\",\"dbName\":null}],\"dbName\":null},\"CustomerKind\":{\"values\":[{\"name\":\"SALES_AGENT\",\"dbName\":null},{\"name\":\"APP_SHOPKEEPER\",\"dbName\":null},{\"name\":\"WALK_IN_SHOPKEEPER\",\"dbName\":null},{\"name\":\"BANKER\",\"dbName\":null},{\"name\":\"WORKSHOP_SUPERVISOR\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Legacy Customer.type — what kind of wholesale customer this is. Values and\\\\nlabels taken verbatim from bulkCustomer.vue's userTypes list.\"},\"VerificationStep\":{\"values\":[{\"name\":\"UNVERIFIED\",\"dbName\":null},{\"name\":\"STEP_ONE_SUBMITTED\",\"dbName\":null},{\"name\":\"STEP_TWO_SUBMITTED\",\"dbName\":null},{\"name\":\"VERIFIED\",\"dbName\":null}],\"dbName\":null},\"OrderChannel\":{\"values\":[{\"name\":\"RETAIL\",\"dbName\":null},{\"name\":\"WHOLESALE\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Legacy kept retail and wholesale orders in two SEPARATE tables\\\\n(orders_orders vs orders). The redesigned schema has one Order model, so the\\\\ndistinction lives here instead.\"},\"OrderStatus\":{\"values\":[{\"name\":\"SUSPENDED\",\"dbName\":null},{\"name\":\"PLACED\",\"dbName\":null},{\"name\":\"ADMIN_APPROVED\",\"dbName\":null},{\"name\":\"SOURCING\",\"dbName\":null},{\"name\":\"PREPARING\",\"dbName\":null},{\"name\":\"DELIVERED_TO_AGENT\",\"dbName\":null},{\"name\":\"DELIVERED_TO_CUSTOMER\",\"dbName\":null},{\"name\":\"CANCELLED\",\"dbName\":null}],\"dbName\":null},\"PreOrderStatus\":{\"values\":[{\"name\":\"PLACED\",\"dbName\":null},{\"name\":\"ADMIN_APPROVED\",\"dbName\":null},{\"name\":\"WORKSHOP_APPROVED\",\"dbName\":null},{\"name\":\"READY_TO_SHIP\",\"dbName\":null},{\"name\":\"DELIVERED\",\"dbName\":null},{\"name\":\"CANCELLED\",\"dbName\":null}],\"dbName\":null},\"LedgerCurrency\":{\"values\":[{\"name\":\"GOLD_GRAM\",\"dbName\":null},{\"name\":\"RIAL\",\"dbName\":null}],\"dbName\":null},\"LedgerDirection\":{\"values\":[{\"name\":\"DEBIT\",\"dbName\":null},{\"name\":\"CREDIT\",\"dbName\":null}],\"dbName\":null},\"LedgerSourceType\":{\"values\":[{\"name\":\"ORDER_STOCK\",\"dbName\":null},{\"name\":\"ORDER_PRE_ORDER\",\"dbName\":null},{\"name\":\"REPAYMENT_MELTED_GOLD\",\"dbName\":null},{\"name\":\"REPAYMENT_TRANSFER\",\"dbName\":null},{\"name\":\"REPAYMENT_CHEQUE\",\"dbName\":null},{\"name\":\"REPAYMENT_CASH\",\"dbName\":null},{\"name\":\"WALLET_BONUS\",\"dbName\":null},{\"name\":\"ADJUSTMENT\",\"dbName\":null}],\"dbName\":null},\"ProductImageRole\":{\"values\":[{\"name\":\"FEATURED\",\"dbName\":null},{\"name\":\"FEATURED_MOBILE\",\"dbName\":null},{\"name\":\"GALLERY\",\"dbName\":null},{\"name\":\"GALLERY_MOBILE\",\"dbName\":null}],\"dbName\":null},\"CommentStatus\":{\"values\":[{\"name\":\"PENDING\",\"dbName\":null},{\"name\":\"APPROVED\",\"dbName\":null},{\"name\":\"REJECTED\",\"dbName\":null}],\"dbName\":null},\"PromotionScope\":{\"values\":[{\"name\":\"PRODUCT\",\"dbName\":null},{\"name\":\"CATEGORY\",\"dbName\":null},{\"name\":\"CUSTOMER\",\"dbName\":null},{\"name\":\"BRAND\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Legacy Promotion.type — what the discount is scoped to.\"},\"PromotionUsage\":{\"values\":[{\"name\":\"ONCE\",\"dbName\":null},{\"name\":\"UNTIL_END_DATE\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Legacy Promotion.use_only — how often a customer may redeem it.\"},\"MessageAudience\":{\"values\":[{\"name\":\"SINGLE_NUMBER\",\"dbName\":null},{\"name\":\"MULTIPLE_NUMBERS\",\"dbName\":null},{\"name\":\"ALL_CUSTOMERS\",\"dbName\":null},{\"name\":\"WHOLESALE_CUSTOMERS\",\"dbName\":null},{\"name\":\"RETAIL_CUSTOMERS\",\"dbName\":null},{\"name\":\"SALES_AGENTS\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Who an SMS or notification was aimed at. Legacy's sms/create offered these\\\\naudiences, each gated by its own rid (sms.create.all, .omde, .single, ...).\"}},\"types\":{}}") +config.runtimeDataModel = JSON.parse("{\"models\":{\"City\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"province\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"percent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy cities.percent / percent_aux — per-city wage surcharges the city\\\\nscreen edits through GET cities/get-percent/{id} and get-percent-aux/{id}.\"},{\"name\":\"percentAux\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customers\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CityToCustomer\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"addresses\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CustomerAddress\",\"relationName\":\"CityToCustomerAddress\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branches\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Branch\",\"relationName\":\"BranchToCity\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"CityToPreOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"salesExpertProfiles\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"SalesExpertProfile\",\"relationName\":\"CityToSalesExpertProfile\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"AdminUser\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fullName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"⚠️ Kept in sync BY A DATABASE TRIGGER — `admin_user_sync_full_name`, from\\\\nmigration 20260922180000. Prisma cannot see it, so a write here may store\\\\nsomething other than what you passed:\\\\n\\\\n* write the parts -> fullName is rebuilt as \\\"first last\\\"\\\\n* write fullName only -> the parts are re-derived from it\\\\n* write BOTH in one update -> THE PARTS WIN; the submitted fullName is\\\\ndiscarded\\\\n\\\\nRe-read after writing if you need the stored value. Same arrangement as\\\\nCustomer (D0 requirement 7 — the split applies to ALL admins, not only\\\\nsales experts).\"},{\"name\":\"firstName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"lastName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mobile\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"passwordHash\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"role\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminRole\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"userTypeId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `users.ut`. Granular ACL lives here (Permission rows hang off UserType);\\\\nthe coarse `role` enum above is retained for the business rules that already\\\\nswitch on it. Nullable so existing seeded admins remain valid.\"},{\"name\":\"userType\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UserType\",\"relationName\":\"AdminUserToUserType\",\"relationFromFields\":[\"userTypeId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branchId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branch\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Branch\",\"relationName\":\"AdminUserToBranch\",\"relationFromFields\":[\"branchId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"wagePercent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"avatarFileId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy stored a sales agent as a `customers` row with type=1, so\\\\nReagentCustomer.vue's shared form collected these alongside the\\\\nwholesale-customer fields. A sales agent is now an AdminUser\\\\n(role=SALES_EXPERT), so these live here instead — only ever set for that\\\\nrole; every other AdminUser leaves them null.\"},{\"name\":\"avatarFile\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UploadedFile\",\"relationName\":\"AdminUserAvatar\",\"relationFromFields\":[\"avatarFileId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"address\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"storeName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"pasazhName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"memberCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"referredCustomers\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"ReferralAgent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"supervisedWorkshops\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Workshop\",\"relationName\":\"WorkshopSupervisor\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branchAllocations\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocation\",\"relationName\":\"AllocationAuthor\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"postedLedgerEntries\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerEntry\",\"relationName\":\"AdminUserToLedgerEntry\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderStatusEvents\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderStatusEvent\",\"relationName\":\"AdminUserToOrderStatusEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrderStatusEvents\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrderStatusEvent\",\"relationName\":\"AdminUserToPreOrderStatusEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentSmsMessages\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"SmsMessage\",\"relationName\":\"SmsAuthor\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentNotifications\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Notification\",\"relationName\":\"NotificationAuthor\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"uploadedFiles\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UploadedFile\",\"relationName\":\"FileUploader\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"salesExpertProfile\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"SalesExpertProfile\",\"relationName\":\"AdminUserToSalesExpertProfile\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Only ever set for role = SALES_EXPERT (D17).\"}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"SalesExpertProfile\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"adminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"adminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToSalesExpertProfile\",\"relationFromFields\":[\"adminUserId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"Cascade\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy customers.city_id — a filterable grid column\\\\n(ReagentListModel.js:14) and the source of the commission autofill\\\\n(ReagentCustomer.vue:215-228).\"},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"CityToSalesExpertProfile\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"gramLevel\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy customers.gram_level — the «سطح اعتبار» column, filterable.\\\\nDecimal(14,4) matches Customer.goldCreditLimitGrams exactly so the same\\\\ncredit limit has identical precision on both tables.\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy customers.phone (varchar 15) and ncode (varchar 20). The reference\\\\nstores both and reads neither back; kept so the form matches it. Neither\\\\nis unique and neither has a validation rule in the reference — inventing\\\\none would reject data the reference accepts.\"},{\"name\":\"nationalCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"tradingCustomerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"The Customer row carrying this expert's OWN orders, debt and credit limit.\\\\n\\\\nThe reference stored a sales agent AS a Customer (type=1), so an order the\\\\nagent placed from the app was simply their own, against their own\\\\ngram_level (ApiMobileController.php:534-535). Here the expert is an\\\\nAdminUser (D17), which has no ledger and cannot be an Order's subject, so\\\\nthis one linked Customer plays that part.\\\\n\\\\nDistinct from the expert's REFERRED customers, which are ordinary Customer\\\\nrows pointing at the AdminUser through Customer.referredByAgentId.\\\\n\\\\nNull until the expert first signs into the app, when it is created from\\\\nthis profile. See D21.\"},{\"name\":\"tradingCustomer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"SalesExpertTradingAccount\",\"relationFromFields\":[\"tradingCustomerId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"SetNull\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"The sales-expert fields legacy kept on the `customers` row (type=1) and that\\\\nwere lost when a sales expert became an AdminUser (D17).\\\\n\\\\nA separate table rather than columns on AdminUser: these are meaningful only\\\\nfor role = SALES_EXPERT and would be permanently null for every other admin.\\\\nA row is created for every SALES_EXPERT by the migration, and upserted by the\\\\nservice whenever one is created or a user's role becomes SALES_EXPERT — but\\\\nreaders still left-join, so a missing profile degrades to nulls rather than\\\\ndropping the agent from the grid.\\\\n\\\\n`reagent_id` is deliberately NOT here — see D17: in the reference it was a\\\\nside effect of the shared customers form, and filling it removed that expert\\\\nfrom the selectable-agent list (queryReagent, AdminController.php:420).\"},\"Customer\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fullName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"⚠️ `fullName`, `firstName` and `lastName` are kept in sync BY A DATABASE\\\\nTRIGGER — `customer_sync_full_name`, created in migration\\\\n20260922120000_customer_name_parts. Prisma does not know about it, so a\\\\nwrite here is NOT the whole story:\\\\n\\\\n* write the parts -> fullName is rebuilt as \\\"first last\\\"\\\\n* write fullName only -> the parts are re-derived from it\\\\n* write BOTH in one update -> THE PARTS WIN; fullName is overwritten\\\\nwith \\\"first last\\\" and whatever you passed for fullName is discarded\\\\n\\\\nSo a value you wrote can differ from the value that lands in the row.\\\\nRe-read after writing if you need the stored value. Prefer writing the\\\\nparts; `fullName` exists for the legacy readers that predate D0.\\\\n\\\\nReference has these as two columns from the start\\\\n(henza_server 2020_10_01_120430_customers_table.php:18-19 fname/lname);\\\\nthe port collapsed them, which is the D0 defect.\"},{\"name\":\"firstName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"lastName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mobile\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"nationalCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"passwordHash\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"CityToCustomer\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"kind\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"CustomerKind\",\"default\":\"APP_SHOPKEEPER\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Customer.type — distinguishes بنکدار from مغازه دار, which the\\\\nBankerList / ShopkeeperList screens filter on.\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy contact + shop columns, shown on the wholesale customer form and as\\\\n(hidden-by-default) columns in its grid.\"},{\"name\":\"address\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"storeName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"pasazhName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"zone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"memberCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy mcode — the operator-assigned customer code.\"},{\"name\":\"referredByAgentId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"referredByAgent\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"ReferralAgent\",\"relationFromFields\":[\"referredByAgentId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"salesExpertTradingFor\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"SalesExpertProfile\",\"relationName\":\"SalesExpertTradingAccount\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Set only on the one Customer row that acts as a sales expert's own trading\\\\naccount (see SalesExpertProfile.tradingCustomerId). Empty for every\\\\nordinary customer.\"},{\"name\":\"goldCreditLimitGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"goldDebtGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fiatDebtRial\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"makingChargeOffsetPercent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `customers.percent` — the per-customer making-charge offset. It is\\\\nADDED to the variant's making charge when pricing an order line\\\\n(henza_pwa src/Mixins/product/index.ts:69-75:\\\\ngetWage(v) = state.percent + v.percentage_pay)\\\\nand it shifts the catalogue making-charge filter bounds\\\\n(ApiMobileController.php:391-396,76-77). It is NOT part of the price\\\\nformula itself — that extra percent is ConfigStatic['product_price_extra_percent'].\\\\n\\\\nNOT the wallet cashback rate (storefront ConfigStatic['percent'], D13) and\\\\nNOT the wholesale per-line charge (OrderItem.wagePercentSnapshot).\\\\n\\\\nDB default 0 keeps every existing customer's price unchanged. The\\\\nreference's application-layer default for NEW customers is 3\\\\n(AdminController.php:338) and the field is required on create (:354).\"},{\"name\":\"verificationStep\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"VerificationStep\",\"default\":\"UNVERIFIED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"nationalIdImageUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"businessLicenseUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"shopImageUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"avatarFileId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `image_id` — the profile photo (عکس پروفایل) on the sales-agent\\\\nform. Legacy stored an FK to its Image table and read it back as a nested\\\\n`image: { id, image }`, so this is a relation, not a bare URL string.\"},{\"name\":\"avatarFile\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UploadedFile\",\"relationName\":\"CustomerAvatar\",\"relationFromFields\":[\"avatarFileId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"canPlaceOrders\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy had TWO independent switches on a customer:\\\\nsts -> may they place orders (this field)\\\\nactive -> is the account enabled (isActive, below)\\\\nThe port previously drove both from isActive; they are separate again.\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isArchived\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy archive flag — the archived list filtered on this, not on isActive.\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"addresses\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CustomerAddress\",\"relationName\":\"CustomerToCustomerAddress\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"CustomerToOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"CustomerToPreOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ledgerEntries\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerEntry\",\"relationName\":\"CustomerToLedgerEntry\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"walletBonuses\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"WalletBonus\",\"relationName\":\"CustomerToWalletBonus\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"comments\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Comment\",\"relationName\":\"CommentToCustomer\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"promotions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Promotion\",\"relationName\":\"CustomerToPromotion\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"notifications\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"NotificationRecipient\",\"relationName\":\"CustomerToNotificationRecipient\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"activityLogs\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CustomerActivityLog\",\"relationName\":\"CustomerToCustomerActivityLog\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"otpChallenges\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CustomerOtp\",\"relationName\":\"CustomerToCustomerOtp\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"CustomerAddress\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToCustomerAddress\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"CityToCustomerAddress\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"addressLine\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isDefault\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Branch\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"address\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Branch.address / phone — both are columns in the branch grid.\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy GET branch/active/{id} toggles this.\"},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"BranchToCity\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"adminUsers\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToBranch\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"BranchToOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocations\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocation\",\"relationName\":\"BranchToBranchAllocation\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Workshop\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"address\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Workshop.address / phone — both are columns in the workshop grid.\"},{\"name\":\"phone\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"supervisorId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"The supervisor created alongside the workshop by the legacy Add form.\"},{\"name\":\"supervisor\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"WorkshopSupervisor\",\"relationFromFields\":[\"supervisorId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrders\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"PreOrderToWorkshop\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Category\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"type\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":1,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Category.type — which storefront the category belongs to:\\\\n1 سایت | 2 اپلیکشن موبایل. Drives the \\\"type_str\\\" grid column.\"},{\"name\":\"imageUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Category.image / icon — the grid renders the image, the storefront the icon.\"},{\"name\":\"iconUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"metaTag\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy meta tag + description, used for SEO on the site categories.\"},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"parentId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"parent\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Category\",\"relationName\":\"CategoryTree\",\"relationFromFields\":[\"parentId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"children\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Category\",\"relationName\":\"CategoryTree\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"products\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"CategoryToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"promotions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Promotion\",\"relationName\":\"CategoryToPromotion\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Brand\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"products\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"BrandToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"promotions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Promotion\",\"relationName\":\"BrandToPromotion\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Collection\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"products\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CollectionProduct\",\"relationName\":\"CollectionToCollectionProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"Curated product groupings — replaces legacy \\\"Ciny/CinyProduct\\\" (a tagging\\\\nfeature, NOT a gold-price snapshot mechanism, despite the misleading legacy name).\"},\"CollectionProduct\":{\"dbName\":null,\"fields\":[{\"name\":\"collectionId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"collection\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Collection\",\"relationName\":\"CollectionToCollectionProduct\",\"relationFromFields\":[\"collectionId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"CollectionProductToProduct\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":{\"name\":null,\"fields\":[\"collectionId\",\"productId\"]},\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Product\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"categoryId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"category\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Category\",\"relationName\":\"CategoryToProduct\",\"relationFromFields\":[\"categoryId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"brandId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"brand\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Brand\",\"relationName\":\"BrandToProduct\",\"relationFromFields\":[\"brandId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productCode\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Product.product_code — the human-facing SKU shown in the grid.\"},{\"name\":\"showType\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":3,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy Product.show_type: 1 site, 2 mobile, 3 both.\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"variants\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ProductVariant\",\"relationName\":\"ProductToProductVariant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"images\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ProductImage\",\"relationName\":\"ProductToProductImage\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"collections\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"CollectionProduct\",\"relationName\":\"CollectionProductToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"comments\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Comment\",\"relationName\":\"CommentToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"promotions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Promotion\",\"relationName\":\"ProductToPromotion\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocationItems\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocationItem\",\"relationName\":\"BranchAllocationItemToProduct\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"ProductImage\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"ProductToProductImage\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"url\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sortOrder\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"role\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"ProductImageRole\",\"default\":\"GALLERY\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy had 4 distinct upload slots (تصویر شاخص / تصویر شاخص موبایل /\\\\nگالری تصاویر سایت / گالری تصاویر موبایل) — GALLERY is the default so\\\\nexisting rows and any generic \\\"just attach an image\\\" caller keep working.\"},{\"name\":\"variants\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ProductVariant\",\"relationName\":\"ProductImageToProductVariant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Variants that display this image. A gallery image may back several\\\\nvariants (one photo, several weights), which is why this is a plain\\\\none-to-many and not a join table.\"}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"ProductVariant\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"ProductToProductVariant\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sku\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rashWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Base/raw weight before making charge (\\\"rash weight\\\").\"},{\"name\":\"defaultWagePercent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Default making-charge percentage applied on top of rash weight.\"},{\"name\":\"qty\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy ProductVariant.qty — on-hand stock. The cart refuses a variant with\\\\nqty 0 unless it is flagged pre-order, so both fields are required for that\\\\nguard to work (see apps/admin-panel/src/stores/cart.ts).\"},{\"name\":\"isPreOrder\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy is_pre_order: orderable even at zero stock.\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"imageId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"The gallery image shown for this variant, picked from its own product's\\\\nimages — never a re-uploaded copy. onDelete: SetNull because dropping an\\\\nimage must never cascade into the variant: OrderItem rows reference\\\\nvariants, so losing one would break order history.\"},{\"name\":\"image\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ProductImage\",\"relationName\":\"ProductImageToProductVariant\",\"relationFromFields\":[\"imageId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"SetNull\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderItems\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderItem\",\"relationName\":\"OrderItemToProductVariant\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Order\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderNumber\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToOrder\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branchId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branch\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Branch\",\"relationName\":\"BranchToOrder\",\"relationFromFields\":[\"branchId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"channel\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"OrderChannel\",\"default\":\"RETAIL\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Which sales channel placed this order — see OrderChannel.\"},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"OrderStatus\",\"default\":\"PLACED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"totalGoldWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Total gold weight incl. wage across all items (\\\"allGram\\\" / order.w).\"},{\"name\":\"wageWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Pure making-charge weight component (totalGoldWeightGrams minus raw weight sum).\"},{\"name\":\"flatRialCharge\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Flat Rial charge independent of gold price (e.g. stones) — legacy rial_input.\"},{\"name\":\"isSuspendedOverCreditLimit\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"deliveredAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"items\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderItem\",\"relationName\":\"OrderToOrderItem\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"statusEvents\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderStatusEvent\",\"relationName\":\"OrderToOrderStatusEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ledgerEntries\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerEntry\",\"relationName\":\"LedgerEntryToOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"convertedFromPreOrder\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"OrderToPreOrder\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"OrderItem\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"order\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"OrderToOrderItem\",\"relationFromFields\":[\"orderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productVariantId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productVariant\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"ProductVariant\",\"relationName\":\"OrderItemToProductVariant\",\"relationFromFields\":[\"productVariantId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"quantity\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rashWeightGramsSnapshot\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"wagePercentSnapshot\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"discountPercentSnapshot\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"computedWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"computed: rash*(1 + (wagePercent-discount)/100)\"},{\"name\":\"flatRialCharge\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Decimal\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"OrderStatusEvent\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"order\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"OrderToOrderStatusEvent\",\"relationFromFields\":[\"orderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fromStatus\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderStatus\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"toStatus\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"OrderStatus\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"note\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"actorAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"actorAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToOrderStatusEvent\",\"relationFromFields\":[\"actorAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"PreOrder\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToPreOrder\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"cityId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"city\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"City\",\"relationName\":\"CityToPreOrder\",\"relationFromFields\":[\"cityId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"workshopId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"workshop\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Workshop\",\"relationName\":\"PreOrderToWorkshop\",\"relationFromFields\":[\"workshopId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"PreOrderStatus\",\"default\":\"PLACED\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"designImageUrl\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"colorName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sizeSpec\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"stoneColor\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"minaColor\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"estimatedWeightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"estimatedPriceRial\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"convertedOrderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Once priced, an admin converts this into a real (debt-bearing) Order.\"},{\"name\":\"convertedOrder\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"OrderToPreOrder\",\"relationFromFields\":[\"convertedOrderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true},{\"name\":\"statusEvents\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrderStatusEvent\",\"relationName\":\"PreOrderToPreOrderStatusEvent\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"PreOrderStatusEvent\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"preOrder\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"PreOrder\",\"relationName\":\"PreOrderToPreOrderStatusEvent\",\"relationFromFields\":[\"preOrderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"changes\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Json\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Field-level diff, e.g. {\\\"status\\\": {\\\"old\\\": \\\"PLACED\\\", \\\"new\\\": \\\"ADMIN_APPROVED\\\"}}\"},{\"name\":\"actorAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"actorAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToPreOrderStatusEvent\",\"relationFromFields\":[\"actorAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"ipAddress\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"LedgerEntry\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToLedgerEntry\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"currency\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerCurrency\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"direction\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerDirection\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sourceType\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"LedgerSourceType\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"amount\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Always positive; direction determines the sign applied to the customer balance.\"},{\"name\":\"karatPerMille\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Purity of the physical instrument, for REPAYMENT_MELTED_GOLD (e.g. 750, 916).\"},{\"name\":\"pricePerGramRialAtEntry\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Gold price/gram at conversion time, for REPAYMENT_CASH.\"},{\"name\":\"orderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"order\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Order\",\"relationName\":\"LedgerEntryToOrder\",\"relationFromFields\":[\"orderId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"confirmed\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":false,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Pending entries don't count toward the cached customer balance until confirmed.\"},{\"name\":\"confirmedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"postedByAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"postedByAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToLedgerEntry\",\"relationFromFields\":[\"postedByAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"labName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Instrument metadata from the legacy \\\"cash\\\" document form. Which fields are\\\\nfilled depends on sourceType: a melted-gold receipt carries lab/hallmark and\\\\nthe pre-normalization weight, a cheque carries the account and bank, and a\\\\ntransfer carries its destination.\\\\nLegacy columns: lab, ang, account_name, bank_name, to, original_value, recive_date.\"},{\"name\":\"hallmark\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"accountName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"bankName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"transferTo\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"originalValue\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Weight as handed over, BEFORE karat normalization (legacy original_value).\"},{\"name\":\"priceRial\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Rial amount for cash/cheque documents (legacy price).\"},{\"name\":\"receivedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"note\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"GoldPriceQuote\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"karatPerMille\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"pricePerGramRial\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"source\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"quotedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"Replaces legacy WebServiceGold / ConfigStatic.gold_price — timestamped price feed.\"},\"WalletBonus\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToWalletBonus\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"orderId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"goldGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"grantedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"consumedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"Time-limited gold cashback bonus — replaces the legacy Rooyesh\\\\Wallet gold credit.\"},\"UserType\":{\"dbName\":\"user_type\",\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `permission.ut` / `users.ut` — an integer, kept as the PK so seeded\\\\nrows keep their legacy identity rather than getting new cuids.\"},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"permissions\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Permission\",\"relationName\":\"PermissionToUserType\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"adminUsers\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserToUserType\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Permission\":{\"dbName\":\"permission\",\"fields\":[{\"name\":\"rid\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Dotted permission id, e.g. \\\"order.list\\\", \\\"persons.list.client.omde\\\".\"},{\"name\":\"userTypeId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"userType\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"UserType\",\"relationName\":\"PermissionToUserType\",\"relationFromFields\":[\"userTypeId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"Cascade\",\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":{\"name\":null,\"fields\":[\"rid\",\"userTypeId\"]},\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Comment\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"CommentToProduct\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CommentToCustomer\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"body\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"status\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"CommentStatus\",\"default\":\"PENDING\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"replyToId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `reply_to`: a staff reply threads under the comment it answers.\"},{\"name\":\"replyTo\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Comment\",\"relationName\":\"CommentReplies\",\"relationFromFields\":[\"replyToId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"replies\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Comment\",\"relationName\":\"CommentReplies\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Setting\":{\"dbName\":null,\"fields\":[{\"name\":\"key\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"value\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Json\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Promotion\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"scope\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"PromotionScope\",\"default\":\"PRODUCT\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"usage\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"PromotionUsage\",\"default\":\"ONCE\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"discountPercent\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Percentage off, 0-100 (legacy discount_percent).\"},{\"name\":\"isActive\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Boolean\",\"default\":true,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"platform\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":3,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Which storefront it shows on, mirroring Product.showType (legacy platform).\"},{\"name\":\"startsAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"endsAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Exactly one of these is populated, per `scope`.\"},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"ProductToPromotion\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"categoryId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"category\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Category\",\"relationName\":\"CategoryToPromotion\",\"relationFromFields\":[\"categoryId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToPromotion\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"brandId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"brand\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Brand\",\"relationName\":\"BrandToPromotion\",\"relationFromFields\":[\"brandId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"PromotionExclusion\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"type\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"itemId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[[\"type\",\"itemId\"]],\"uniqueIndexes\":[{\"name\":null,\"fields\":[\"type\",\"itemId\"]}],\"isGenerated\":false,\"documentation\":\"Legacy \\\"ignore\\\" table (views/offersIgnore) — specific products, categories,\\\\ncustomers or brands excluded from the automatic discount engine.\\\\nLegacy posts { type, item_id[] }: \\\"type\\\" reuses the promotion scope codes\\\\n(1 محصولات | 2 دسته بندی | 3 مشتری | 4 برند) and \\\"itemId\\\" is the excluded row.\"},\"BranchAllocation\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branchId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"branch\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Branch\",\"relationName\":\"BranchToBranchAllocation\",\"relationFromFields\":[\"branchId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocatorAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Legacy `allocator_user` — the admin who filed the allocation.\"},{\"name\":\"allocatorAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AllocationAuthor\",\"relationFromFields\":[\"allocatorAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"note\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"items\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocationItem\",\"relationName\":\"BranchAllocationToBranchAllocationItem\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"BranchAllocationItem\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocationId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"allocation\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BranchAllocation\",\"relationName\":\"BranchAllocationToBranchAllocationItem\",\"relationFromFields\":[\"allocationId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"Cascade\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"productId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"product\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Product\",\"relationName\":\"BranchAllocationItemToProduct\",\"relationFromFields\":[\"productId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"weightGrams\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Decimal\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Weight snapshot at allocation time, so later catalog edits do not rewrite history.\"}],\"primaryKey\":null,\"uniqueFields\":[[\"allocationId\",\"productId\"]],\"uniqueIndexes\":[{\"name\":null,\"fields\":[\"allocationId\",\"productId\"]}],\"isGenerated\":false},\"SmsMessage\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"audience\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"MessageAudience\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"recipients\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Comma-separated recipients for the SINGLE/MULTIPLE audiences.\"},{\"name\":\"body\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentCount\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"How many numbers the send actually resolved to.\"},{\"name\":\"sentByAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentByAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"SmsAuthor\",\"relationFromFields\":[\"sentByAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"Notification\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"title\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"body\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"audience\",\"kind\":\"enum\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"MessageAudience\",\"default\":\"ALL_CUSTOMERS\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentByAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sentByAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"NotificationAuthor\",\"relationFromFields\":[\"sentByAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"recipients\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"NotificationRecipient\",\"relationName\":\"NotificationToNotificationRecipient\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"NotificationRecipient\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"notificationId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"notification\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Notification\",\"relationName\":\"NotificationToNotificationRecipient\",\"relationFromFields\":[\"notificationId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"Cascade\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToNotificationRecipient\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"readAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[[\"notificationId\",\"customerId\"]],\"uniqueIndexes\":[{\"name\":null,\"fields\":[\"notificationId\",\"customerId\"]}],\"isGenerated\":false,\"documentation\":\"Per-customer delivery + read state — legacy `GET notify/read/{id}`.\"},\"NewsletterSubscriber\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"email\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mobile\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[[\"email\"],[\"mobile\"]],\"uniqueIndexes\":[{\"name\":null,\"fields\":[\"email\"]},{\"name\":null,\"fields\":[\"mobile\"]}],\"isGenerated\":false,\"documentation\":\"Legacy views/newsletters — the mailing-list signups collected by the site.\"},\"CustomerActivityLog\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToCustomerActivityLog\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"action\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Free-form action key, e.g. 'login', 'order.placed', 'profile.updated'.\"},{\"name\":\"detail\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"Legacy views/statics — the customer activity log the \\\"آمار\\\" screen reads\\\\n(`log/list/customer`, `log/ago/{customerId}`).\"},\"UploadedFile\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"url\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Public URL the stored file is served from.\"},{\"name\":\"originalName\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mimeType\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"sizeBytes\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Int\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"purpose\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Free-form tag for what the file belongs to, e.g. 'product', 'category',\\\\n'customer.nationalId'. Kept loose so a new form needs no schema change.\"},{\"name\":\"uploadedByAdminUserId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"uploadedByAdminUser\",\"kind\":\"object\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"FileUploader\",\"relationFromFields\":[\"uploadedByAdminUserId\"],\"relationToFields\":[\"id\"],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customerAvatars\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerAvatar\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"adminUserAvatars\",\"kind\":\"object\",\"isList\":true,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"AdminUser\",\"relationName\":\"AdminUserAvatar\",\"relationFromFields\":[],\"relationToFields\":[],\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"CustomerOtp\":{\"dbName\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"String\",\"default\":{\"name\":\"cuid\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mobile\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Normalised to English digits before storing, as the reference does with\\\\nUtility::faTOen() on both send and verify.\"},{\"name\":\"customerId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":true,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"customer\",\"kind\":\"object\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Customer\",\"relationName\":\"CustomerToCustomerOtp\",\"relationFromFields\":[\"customerId\"],\"relationToFields\":[\"id\"],\"relationOnDelete\":\"Cascade\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"codeHash\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"bcrypt hash of the 4-digit code. Never the code itself.\"},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"attempts\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"default\":0,\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Failed verify attempts against THIS challenge.\"},{\"name\":\"consumedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Set when the code is accepted, so one challenge cannot be reused.\"},{\"name\":\"requestIp\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"isGenerated\":false,\"isUpdatedAt\":false,\"documentation\":\"Kept for the per-IP send throttle.\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false,\"documentation\":\"One OTP login challenge for a customer.\\\\n\\\\nPorts henza_server's flow (ApiMobileController.php:39-78), where userAuth\\\\ngenerated rand(1000,9999), stored it on the customer row as `send_password`\\\\nand sent it by SMS, and userVerfiy compared the submitted code against it\\\\n(mismatch -> HTTP 422).\\\\n\\\\nThree deliberate improvements over the reference, approved by the user\\\\n(docs/parity/DECISIONS.md D6 and T1):\\\\n1. the code is stored as a bcrypt hash, never in plain text — the\\\\nreference kept it readable in a customer column;\\\\n2. it expires (`expiresAt`) — the reference's code stayed valid forever;\\\\n3. attempts are counted and the challenge locks out — the reference had\\\\nno limit at all.\\\\nThe parity behaviour itself is the code comparison and the rejection.\"}},\"enums\":{\"AdminRole\":{\"values\":[{\"name\":\"SUPER_ADMIN\",\"dbName\":null},{\"name\":\"SYSTEM_APPROVER\",\"dbName\":null},{\"name\":\"SALES_EXPERT\",\"dbName\":null},{\"name\":\"WORKSHOP_SUPERVISOR\",\"dbName\":null},{\"name\":\"ACCOUNTANT\",\"dbName\":null}],\"dbName\":null},\"CustomerKind\":{\"values\":[{\"name\":\"SALES_AGENT\",\"dbName\":null},{\"name\":\"APP_SHOPKEEPER\",\"dbName\":null},{\"name\":\"WALK_IN_SHOPKEEPER\",\"dbName\":null},{\"name\":\"BANKER\",\"dbName\":null},{\"name\":\"WORKSHOP_SUPERVISOR\",\"dbName\":null},{\"name\":\"RETAIL\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Legacy Customer.type — what kind of wholesale customer this is. Values and\\\\nlabels taken verbatim from bulkCustomer.vue's userTypes list.\\\\n\\\\nSALES_AGENT is a LEGACY value only: legacy stored a sales agent as a\\\\nCustomer row with type=1, but the redesigned system models a sales agent\\\\nas an AdminUser (role SALES_EXPERT) instead — see admin-users.service.ts\\\\nand admin-customers.service.ts's listReagents()/create(). No Customer row\\\\nshould be created with kind SALES_AGENT going forward; the value is kept\\\\nonly so legacyTypeToKind()/kindToLegacyType() stay total functions and any\\\\nalready-migrated data keeps a readable label.\\\\n\\\\nRETAIL marks a \\\"تک\\\" (single/retail) customer — legacy kept these in an\\\\nentirely separate table (SingleCustomer/`login`) that could never contain\\\\na wholesale/agent/shopkeeper/banker row. The redesigned schema folded both\\\\ninto one Customer model, so this value is what listSingle() now filters on\\\\nto reproduce that same separation.\"},\"VerificationStep\":{\"values\":[{\"name\":\"UNVERIFIED\",\"dbName\":null},{\"name\":\"STEP_ONE_SUBMITTED\",\"dbName\":null},{\"name\":\"STEP_TWO_SUBMITTED\",\"dbName\":null},{\"name\":\"VERIFIED\",\"dbName\":null}],\"dbName\":null},\"OrderChannel\":{\"values\":[{\"name\":\"RETAIL\",\"dbName\":null},{\"name\":\"WHOLESALE\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Legacy kept retail and wholesale orders in two SEPARATE tables\\\\n(orders_orders vs orders). The redesigned schema has one Order model, so the\\\\ndistinction lives here instead.\"},\"OrderStatus\":{\"values\":[{\"name\":\"SUSPENDED\",\"dbName\":null},{\"name\":\"PLACED\",\"dbName\":null},{\"name\":\"ADMIN_APPROVED\",\"dbName\":null},{\"name\":\"SOURCING\",\"dbName\":null},{\"name\":\"PREPARING\",\"dbName\":null},{\"name\":\"DELIVERED_TO_AGENT\",\"dbName\":null},{\"name\":\"DELIVERED_TO_CUSTOMER\",\"dbName\":null},{\"name\":\"CANCELLED\",\"dbName\":null}],\"dbName\":null},\"PreOrderStatus\":{\"values\":[{\"name\":\"PLACED\",\"dbName\":null},{\"name\":\"ADMIN_APPROVED\",\"dbName\":null},{\"name\":\"WORKSHOP_APPROVED\",\"dbName\":null},{\"name\":\"READY_TO_SHIP\",\"dbName\":null},{\"name\":\"DELIVERED\",\"dbName\":null},{\"name\":\"CANCELLED\",\"dbName\":null}],\"dbName\":null},\"LedgerCurrency\":{\"values\":[{\"name\":\"GOLD_GRAM\",\"dbName\":null},{\"name\":\"RIAL\",\"dbName\":null}],\"dbName\":null},\"LedgerDirection\":{\"values\":[{\"name\":\"DEBIT\",\"dbName\":null},{\"name\":\"CREDIT\",\"dbName\":null}],\"dbName\":null},\"LedgerSourceType\":{\"values\":[{\"name\":\"ORDER_STOCK\",\"dbName\":null},{\"name\":\"ORDER_PRE_ORDER\",\"dbName\":null},{\"name\":\"REPAYMENT_MELTED_GOLD\",\"dbName\":null},{\"name\":\"REPAYMENT_TRANSFER\",\"dbName\":null},{\"name\":\"REPAYMENT_CHEQUE\",\"dbName\":null},{\"name\":\"REPAYMENT_CASH\",\"dbName\":null},{\"name\":\"WALLET_BONUS\",\"dbName\":null},{\"name\":\"ADJUSTMENT\",\"dbName\":null}],\"dbName\":null},\"ProductImageRole\":{\"values\":[{\"name\":\"FEATURED\",\"dbName\":null},{\"name\":\"FEATURED_MOBILE\",\"dbName\":null},{\"name\":\"GALLERY\",\"dbName\":null},{\"name\":\"GALLERY_MOBILE\",\"dbName\":null}],\"dbName\":null},\"CommentStatus\":{\"values\":[{\"name\":\"PENDING\",\"dbName\":null},{\"name\":\"APPROVED\",\"dbName\":null},{\"name\":\"REJECTED\",\"dbName\":null}],\"dbName\":null},\"PromotionScope\":{\"values\":[{\"name\":\"PRODUCT\",\"dbName\":null},{\"name\":\"CATEGORY\",\"dbName\":null},{\"name\":\"CUSTOMER\",\"dbName\":null},{\"name\":\"BRAND\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Legacy Promotion.type — what the discount is scoped to.\"},\"PromotionUsage\":{\"values\":[{\"name\":\"ONCE\",\"dbName\":null},{\"name\":\"UNTIL_END_DATE\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Legacy Promotion.use_only — how often a customer may redeem it.\"},\"MessageAudience\":{\"values\":[{\"name\":\"SINGLE_NUMBER\",\"dbName\":null},{\"name\":\"MULTIPLE_NUMBERS\",\"dbName\":null},{\"name\":\"ALL_CUSTOMERS\",\"dbName\":null},{\"name\":\"WHOLESALE_CUSTOMERS\",\"dbName\":null},{\"name\":\"RETAIL_CUSTOMERS\",\"dbName\":null},{\"name\":\"SALES_AGENTS\",\"dbName\":null}],\"dbName\":null,\"documentation\":\"Who an SMS or notification was aimed at. Legacy's sms/create offered these\\\\naudiences, each gated by its own rid (sms.create.all, .omde, .single, ...).\"}},\"types\":{}}") defineDmmfProperty(exports.Prisma, config.runtimeDataModel) config.engineWasm = undefined diff --git a/apps/api/release/vendor/.prisma/client/package.json b/apps/api/release/vendor/.prisma/client/package.json index e2f03a9..94132ee 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-ed6338840581098d1a6f6e7ebfa38995b890cf67ba58cec8ad302fd997d92a01", + "name": "prisma-client-4b01ea5ca305ee68796b97b40684b681824ae0788fb6c2d4d029c05f8b265ccc", "main": "index.js", "types": "index.d.ts", "browser": "index-browser.js", diff --git a/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp10336 b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp10336 new file mode 100644 index 0000000..d46657b Binary files /dev/null and b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp10336 differ diff --git a/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp18164 b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp18164 new file mode 100644 index 0000000..d46657b Binary files /dev/null and b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp18164 differ diff --git a/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp20396 b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp20396 new file mode 100644 index 0000000..d46657b Binary files /dev/null and b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp20396 differ diff --git a/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp20748 b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp20748 new file mode 100644 index 0000000..d46657b Binary files /dev/null and b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp20748 differ diff --git a/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp23140 b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp23140 new file mode 100644 index 0000000..d46657b Binary files /dev/null and b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp23140 differ diff --git a/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp24020 b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp24020 new file mode 100644 index 0000000..d46657b Binary files /dev/null and b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp24020 differ diff --git a/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp25856 b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp25856 new file mode 100644 index 0000000..d46657b Binary files /dev/null and b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp25856 differ diff --git a/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp27868 b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp27868 new file mode 100644 index 0000000..d46657b Binary files /dev/null and b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp27868 differ diff --git a/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp28148 b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp28148 new file mode 100644 index 0000000..d46657b Binary files /dev/null and b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp28148 differ diff --git a/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp29452 b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp29452 new file mode 100644 index 0000000..d46657b Binary files /dev/null and b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp29452 differ diff --git a/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp31932 b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp31932 new file mode 100644 index 0000000..d46657b Binary files /dev/null and b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp31932 differ diff --git a/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp432 b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp432 new file mode 100644 index 0000000..d46657b Binary files /dev/null and b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp432 differ diff --git a/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp4624 b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp4624 new file mode 100644 index 0000000..d46657b Binary files /dev/null and b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp4624 differ diff --git a/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp8796 b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp8796 new file mode 100644 index 0000000..d46657b Binary files /dev/null and b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp8796 differ diff --git a/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp9732 b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp9732 new file mode 100644 index 0000000..d46657b Binary files /dev/null and b/apps/api/release/vendor/.prisma/client/query_engine-windows.dll.node.tmp9732 differ diff --git a/apps/api/release/vendor/.prisma/client/schema.prisma b/apps/api/release/vendor/.prisma/client/schema.prisma index c81b913..fcda982 100644 --- a/apps/api/release/vendor/.prisma/client/schema.prisma +++ b/apps/api/release/vendor/.prisma/client/schema.prisma @@ -28,12 +28,27 @@ enum AdminRole { /// Legacy Customer.type — what kind of wholesale customer this is. Values and /// labels taken verbatim from bulkCustomer.vue's userTypes list. +/// +/// SALES_AGENT is a LEGACY value only: legacy stored a sales agent as a +/// Customer row with type=1, but the redesigned system models a sales agent +/// as an AdminUser (role SALES_EXPERT) instead — see admin-users.service.ts +/// and admin-customers.service.ts's listReagents()/create(). No Customer row +/// should be created with kind SALES_AGENT going forward; the value is kept +/// only so legacyTypeToKind()/kindToLegacyType() stay total functions and any +/// already-migrated data keeps a readable label. +/// +/// RETAIL marks a "تک" (single/retail) customer — legacy kept these in an +/// entirely separate table (SingleCustomer/`login`) that could never contain +/// a wholesale/agent/shopkeeper/banker row. The redesigned schema folded both +/// into one Customer model, so this value is what listSingle() now filters on +/// to reproduce that same separation. enum CustomerKind { - SALES_AGENT // 1 کارشناس فروش + SALES_AGENT // legacy 1 کارشناس فروش — superseded by AdminRole.SALES_EXPERT, do not use for new rows APP_SHOPKEEPER // 2 مغازه دار اپلیکیشن WALK_IN_SHOPKEEPER // 3 مغازه دار حضوری BANKER // 4 بنکدار WORKSHOP_SUPERVISOR // 5 سرپرست کارگاه + RETAIL // مشتری تک — legacy SingleCustomer } enum VerificationStep { @@ -97,22 +112,39 @@ enum LedgerSourceType { // ───────────────────────────────────────────────────────────────────────── model City { - id String @id @default(cuid()) - name String - province String? + id String @id @default(cuid()) + name String + province String? /// Legacy cities.percent / percent_aux — per-city wage surcharges the city /// screen edits through GET cities/get-percent/{id} and get-percent-aux/{id}. - percent Decimal? @db.Decimal(6, 3) - percentAux Decimal? @db.Decimal(6, 3) - customers Customer[] - addresses CustomerAddress[] - branches Branch[] - preOrders PreOrder[] + percent Decimal? @db.Decimal(6, 3) + percentAux Decimal? @db.Decimal(6, 3) + customers Customer[] + addresses CustomerAddress[] + branches Branch[] + preOrders PreOrder[] + salesExpertProfiles SalesExpertProfile[] } model AdminUser { - id String @id @default(cuid()) - fullName String + id String @id @default(cuid()) + + /// ⚠️ Kept in sync BY A DATABASE TRIGGER — `admin_user_sync_full_name`, from + /// migration 20260922180000. Prisma cannot see it, so a write here may store + /// something other than what you passed: + /// + /// * write the parts -> fullName is rebuilt as "first last" + /// * write fullName only -> the parts are re-derived from it + /// * write BOTH in one update -> THE PARTS WIN; the submitted fullName is + /// discarded + /// + /// Re-read after writing if you need the stored value. Same arrangement as + /// Customer (D0 requirement 7 — the split applies to ALL admins, not only + /// sales experts). + fullName String + firstName String? + lastName String? + mobile String @unique passwordHash String role AdminRole @@ -127,6 +159,18 @@ model AdminUser { isActive Boolean @default(true) createdAt DateTime @default(now()) + /// Legacy stored a sales agent as a `customers` row with type=1, so + /// ReagentCustomer.vue's shared form collected these alongside the + /// wholesale-customer fields. A sales agent is now an AdminUser + /// (role=SALES_EXPERT), so these live here instead — only ever set for that + /// role; every other AdminUser leaves them null. + avatarFileId String? + avatarFile UploadedFile? @relation("AdminUserAvatar", fields: [avatarFileId], references: [id]) + address String? + storeName String? // legacy store_name + pasazhName String? // legacy pasazh_name + memberCode String? // legacy mcode + referredCustomers Customer[] @relation("ReferralAgent") supervisedWorkshops Workshop[] @relation("WorkshopSupervisor") branchAllocations BranchAllocation[] @relation("AllocationAuthor") @@ -137,13 +181,98 @@ model AdminUser { sentNotifications Notification[] @relation("NotificationAuthor") uploadedFiles UploadedFile[] @relation("FileUploader") + /// Only ever set for role = SALES_EXPERT (D17). + salesExpertProfile SalesExpertProfile? + @@index([role]) @@index([userTypeId]) + @@index([avatarFileId]) + @@index([firstName]) + @@index([lastName]) +} + +/// The sales-expert fields legacy kept on the `customers` row (type=1) and that +/// were lost when a sales expert became an AdminUser (D17). +/// +/// A separate table rather than columns on AdminUser: these are meaningful only +/// for role = SALES_EXPERT and would be permanently null for every other admin. +/// A row is created for every SALES_EXPERT by the migration, and upserted by the +/// service whenever one is created or a user's role becomes SALES_EXPERT — but +/// readers still left-join, so a missing profile degrades to nulls rather than +/// dropping the agent from the grid. +/// +/// `reagent_id` is deliberately NOT here — see D17: in the reference it was a +/// side effect of the shared customers form, and filling it removed that expert +/// from the selectable-agent list (queryReagent, AdminController.php:420). +model SalesExpertProfile { + id String @id @default(cuid()) + adminUserId String @unique + adminUser AdminUser @relation(fields: [adminUserId], references: [id], onDelete: Cascade) + + /// Legacy customers.city_id — a filterable grid column + /// (ReagentListModel.js:14) and the source of the commission autofill + /// (ReagentCustomer.vue:215-228). + cityId String? + city City? @relation(fields: [cityId], references: [id]) + + /// Legacy customers.gram_level — the «سطح اعتبار» column, filterable. + /// Decimal(14,4) matches Customer.goldCreditLimitGrams exactly so the same + /// credit limit has identical precision on both tables. + gramLevel Decimal @default(0) @db.Decimal(14, 4) + + /// Legacy customers.phone (varchar 15) and ncode (varchar 20). The reference + /// stores both and reads neither back; kept so the form matches it. Neither + /// is unique and neither has a validation rule in the reference — inventing + /// one would reject data the reference accepts. + phone String? @db.VarChar(15) + nationalCode String? @db.VarChar(20) + + /// The Customer row carrying this expert's OWN orders, debt and credit limit. + /// + /// The reference stored a sales agent AS a Customer (type=1), so an order the + /// agent placed from the app was simply their own, against their own + /// gram_level (ApiMobileController.php:534-535). Here the expert is an + /// AdminUser (D17), which has no ledger and cannot be an Order's subject, so + /// this one linked Customer plays that part. + /// + /// Distinct from the expert's REFERRED customers, which are ordinary Customer + /// rows pointing at the AdminUser through Customer.referredByAgentId. + /// + /// Null until the expert first signs into the app, when it is created from + /// this profile. See D21. + tradingCustomerId String? @unique + tradingCustomer Customer? @relation("SalesExpertTradingAccount", fields: [tradingCustomerId], references: [id], onDelete: SetNull) + + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + @@index([cityId]) } model Customer { - id String @id @default(cuid()) - fullName String + id String @id @default(cuid()) + + /// ⚠️ `fullName`, `firstName` and `lastName` are kept in sync BY A DATABASE + /// TRIGGER — `customer_sync_full_name`, created in migration + /// 20260922120000_customer_name_parts. Prisma does not know about it, so a + /// write here is NOT the whole story: + /// + /// * write the parts -> fullName is rebuilt as "first last" + /// * write fullName only -> the parts are re-derived from it + /// * write BOTH in one update -> THE PARTS WIN; fullName is overwritten + /// with "first last" and whatever you passed for fullName is discarded + /// + /// So a value you wrote can differ from the value that lands in the row. + /// Re-read after writing if you need the stored value. Prefer writing the + /// parts; `fullName` exists for the legacy readers that predate D0. + /// + /// Reference has these as two columns from the start + /// (henza_server 2020_10_01_120430_customers_table.php:18-19 fname/lname); + /// the port collapsed them, which is the D0 defect. + fullName String + firstName String? + lastName String? + mobile String @unique nationalCode String @unique passwordHash String @@ -167,12 +296,33 @@ model Customer { referredByAgentId String? referredByAgent AdminUser? @relation("ReferralAgent", fields: [referredByAgentId], references: [id]) + /// Set only on the one Customer row that acts as a sales expert's own trading + /// account (see SalesExpertProfile.tradingCustomerId). Empty for every + /// ordinary customer. + salesExpertTradingFor SalesExpertProfile? @relation("SalesExpertTradingAccount") + // Credit limit + cached ledger balances (source of truth is LedgerEntry; these // are denormalized for fast reads and reconciled by a scheduled job). goldCreditLimitGrams Decimal @default(0) @db.Decimal(14, 4) // legacy gram_level goldDebtGrams Decimal @default(0) @db.Decimal(14, 4) // legacy remain fiatDebtRial Decimal @default(0) @db.Decimal(20, 2) // legacy rial_remain + /// Legacy `customers.percent` — the per-customer making-charge offset. It is + /// ADDED to the variant's making charge when pricing an order line + /// (henza_pwa src/Mixins/product/index.ts:69-75: + /// getWage(v) = state.percent + v.percentage_pay) + /// and it shifts the catalogue making-charge filter bounds + /// (ApiMobileController.php:391-396,76-77). It is NOT part of the price + /// formula itself — that extra percent is ConfigStatic['product_price_extra_percent']. + /// + /// NOT the wallet cashback rate (storefront ConfigStatic['percent'], D13) and + /// NOT the wholesale per-line charge (OrderItem.wagePercentSnapshot). + /// + /// DB default 0 keeps every existing customer's price unchanged. The + /// reference's application-layer default for NEW customers is 3 + /// (AdminController.php:338) and the field is required on create (:354). + makingChargeOffsetPercent Decimal @default(0) @db.Decimal(6, 3) + verificationStep VerificationStep @default(UNVERIFIED) nationalIdImageUrl String? businessLicenseUrl String? @@ -205,8 +355,11 @@ model Customer { promotions Promotion[] notifications NotificationRecipient[] activityLogs CustomerActivityLog[] + otpChallenges CustomerOtp[] @@index([referredByAgentId]) + @@index([firstName]) + @@index([lastName]) } model CustomerAddress { @@ -329,6 +482,11 @@ model ProductImage { /// گالری تصاویر سایت / گالری تصاویر موبایل) — GALLERY is the default so /// existing rows and any generic "just attach an image" caller keep working. role ProductImageRole @default(GALLERY) + + /// Variants that display this image. A gallery image may back several + /// variants (one photo, several weights), which is why this is a plain + /// one-to-many and not a join table. + variants ProductVariant[] } enum ProductImageRole { @@ -356,7 +514,16 @@ model ProductVariant { isPreOrder Boolean @default(false) isActive Boolean @default(true) + /// The gallery image shown for this variant, picked from its own product's + /// images — never a re-uploaded copy. onDelete: SetNull because dropping an + /// image must never cascade into the variant: OrderItem rows reference + /// variants, so losing one would break order history. + imageId String? + image ProductImage? @relation(fields: [imageId], references: [id], onDelete: SetNull) + orderItems OrderItem[] + + @@index([imageId]) } // ───────────────────────────────────────────────────────────────────────── @@ -861,8 +1028,71 @@ model UploadedFile { uploadedByAdminUserId String? uploadedByAdminUser AdminUser? @relation("FileUploader", fields: [uploadedByAdminUserId], references: [id]) - createdAt DateTime @default(now()) - customerAvatars Customer[] @relation("CustomerAvatar") + createdAt DateTime @default(now()) + customerAvatars Customer[] @relation("CustomerAvatar") + adminUserAvatars AdminUser[] @relation("AdminUserAvatar") @@index([purpose]) } + +/// One OTP login challenge for a customer. +/// +/// Ports henza_server's flow (ApiMobileController.php:39-78), where userAuth +/// generated rand(1000,9999), stored it on the customer row as `send_password` +/// and sent it by SMS, and userVerfiy compared the submitted code against it +/// (mismatch -> HTTP 422). +/// +/// Three deliberate improvements over the reference, approved by the user +/// (docs/parity/DECISIONS.md D6 and T1): +/// 1. the code is stored as a bcrypt hash, never in plain text — the +/// reference kept it readable in a customer column; +/// 2. it expires (`expiresAt`) — the reference's code stayed valid forever; +/// 3. attempts are counted and the challenge locks out — the reference had +/// no limit at all. +/// The parity behaviour itself is the code comparison and the rejection. +model CustomerOtp { + id String @id @default(cuid()) + /// Normalised to English digits before storing, as the reference does with + /// Utility::faTOen() on both send and verify. + mobile String + customerId String + customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade) + + /// bcrypt hash of the 4-digit code. Never the code itself. + codeHash String + expiresAt DateTime + /// Failed verify attempts against THIS challenge. + attempts Int @default(0) + /// Set when the code is accepted, so one challenge cannot be reused. + consumedAt DateTime? + + /// Kept for the per-IP send throttle. + requestIp String? + createdAt DateTime @default(now()) + + @@index([mobile, createdAt]) + @@index([customerId]) + @@index([expiresAt]) +} + +/// Bespoke migration ledger left by the EARLIER, pre-Prisma port. +/// +/// It predates Prisma Migrate in this repo and holds 12 historical rows +/// (`0001_init.sql`, `0002_admin_schema.sql`, `seed:initial`, and the early +/// `2026091…` migrations). It is the only record of how that port was applied, +/// so it is **kept and never written to or deleted from** (DEAD-07). +/// +/// It is declared here purely so `prisma migrate diff` stops proposing +/// `DROP TABLE "_goldbank_migrations"` — an undeclared table reads as drift. +/// `@@ignore` keeps it out of Prisma Client: nothing in the application should +/// ever read or write it, and Prisma Migrate will not manage its shape either. +/// +/// Prisma's own ledger is `_prisma_migrations`, which is separate and is never +/// edited by hand (D14). +model goldbank_migrations { + name String @id + appliedAt DateTime @default(now()) @map("applied_at") @db.Timestamptz(6) + + @@map("_goldbank_migrations") + @@ignore +} diff --git a/apps/api/release/vendor/.prisma/client/wasm.js b/apps/api/release/vendor/.prisma/client/wasm.js index b5f4160..3979a6f 100644 --- a/apps/api/release/vendor/.prisma/client/wasm.js +++ b/apps/api/release/vendor/.prisma/client/wasm.js @@ -133,6 +133,8 @@ exports.Prisma.CityScalarFieldEnum = { exports.Prisma.AdminUserScalarFieldEnum = { id: 'id', fullName: 'fullName', + firstName: 'firstName', + lastName: 'lastName', mobile: 'mobile', passwordHash: 'passwordHash', role: 'role', @@ -140,12 +142,31 @@ exports.Prisma.AdminUserScalarFieldEnum = { branchId: 'branchId', wagePercent: 'wagePercent', isActive: 'isActive', - createdAt: 'createdAt' + createdAt: 'createdAt', + avatarFileId: 'avatarFileId', + address: 'address', + storeName: 'storeName', + pasazhName: 'pasazhName', + memberCode: 'memberCode' +}; + +exports.Prisma.SalesExpertProfileScalarFieldEnum = { + id: 'id', + adminUserId: 'adminUserId', + cityId: 'cityId', + gramLevel: 'gramLevel', + phone: 'phone', + nationalCode: 'nationalCode', + tradingCustomerId: 'tradingCustomerId', + createdAt: 'createdAt', + updatedAt: 'updatedAt' }; exports.Prisma.CustomerScalarFieldEnum = { id: 'id', fullName: 'fullName', + firstName: 'firstName', + lastName: 'lastName', mobile: 'mobile', nationalCode: 'nationalCode', passwordHash: 'passwordHash', @@ -161,6 +182,7 @@ exports.Prisma.CustomerScalarFieldEnum = { goldCreditLimitGrams: 'goldCreditLimitGrams', goldDebtGrams: 'goldDebtGrams', fiatDebtRial: 'fiatDebtRial', + makingChargeOffsetPercent: 'makingChargeOffsetPercent', verificationStep: 'verificationStep', nationalIdImageUrl: 'nationalIdImageUrl', businessLicenseUrl: 'businessLicenseUrl', @@ -256,7 +278,8 @@ exports.Prisma.ProductVariantScalarFieldEnum = { defaultWagePercent: 'defaultWagePercent', qty: 'qty', isPreOrder: 'isPreOrder', - isActive: 'isActive' + isActive: 'isActive', + imageId: 'imageId' }; exports.Prisma.OrderScalarFieldEnum = { @@ -491,6 +514,18 @@ exports.Prisma.UploadedFileScalarFieldEnum = { createdAt: 'createdAt' }; +exports.Prisma.CustomerOtpScalarFieldEnum = { + id: 'id', + mobile: 'mobile', + customerId: 'customerId', + codeHash: 'codeHash', + expiresAt: 'expiresAt', + attempts: 'attempts', + consumedAt: 'consumedAt', + requestIp: 'requestIp', + createdAt: 'createdAt' +}; + exports.Prisma.SortOrder = { asc: 'asc', desc: 'desc' @@ -528,7 +563,8 @@ exports.CustomerKind = exports.$Enums.CustomerKind = { APP_SHOPKEEPER: 'APP_SHOPKEEPER', WALK_IN_SHOPKEEPER: 'WALK_IN_SHOPKEEPER', BANKER: 'BANKER', - WORKSHOP_SUPERVISOR: 'WORKSHOP_SUPERVISOR' + WORKSHOP_SUPERVISOR: 'WORKSHOP_SUPERVISOR', + RETAIL: 'RETAIL' }; exports.VerificationStep = exports.$Enums.VerificationStep = { @@ -621,6 +657,7 @@ exports.MessageAudience = exports.$Enums.MessageAudience = { exports.Prisma.ModelName = { City: 'City', AdminUser: 'AdminUser', + SalesExpertProfile: 'SalesExpertProfile', Customer: 'Customer', CustomerAddress: 'CustomerAddress', Branch: 'Branch', @@ -653,7 +690,8 @@ exports.Prisma.ModelName = { NotificationRecipient: 'NotificationRecipient', NewsletterSubscriber: 'NewsletterSubscriber', CustomerActivityLog: 'CustomerActivityLog', - UploadedFile: 'UploadedFile' + UploadedFile: 'UploadedFile', + CustomerOtp: 'CustomerOtp' }; /** diff --git a/apps/api/scripts/test-compose-e2e.cjs b/apps/api/scripts/test-compose-e2e.cjs new file mode 100644 index 0000000..9093062 --- /dev/null +++ b/apps/api/scripts/test-compose-e2e.cjs @@ -0,0 +1,226 @@ +/** + * The whole delivery flow, against the DOCKER COMPOSE stack. + * + * What the owner asked to see proven inside compose: + * 1. a sales expert signs into the app with the master code + * 2. an ordinary customer signs into the app with the master code + * 3. BOTH place an order + * 4. an ADMIN sees both of those orders in the admin panel + * + * Unlike the other scripts this one talks ONLY over HTTP — it never opens a + * Prisma client — because the point is to exercise the deployed container as a + * black box, including its own boot-time migration run. Setup and teardown go + * through the admin API, the same way an operator would. + * + * Usage: node scripts/test-compose-e2e.cjs [apiBaseUrl] [masterCode] + * e.g. node scripts/test-compose-e2e.cjs http://localhost:3100/api 1379 + */ +const API = (process.argv[2] || 'http://localhost:3100/api').replace(/\/$/, ''); +const MASTER = process.argv[3] || '1379'; +const ADMIN_USER = process.argv[4] || '09128081392'; +const ADMIN_PASS = process.argv[5] || '09128081392'; + +let pass = 0, fail = 0; +const failures = []; +const chk = (c, m) => { + if (c) { pass++; console.log(' PASS ' + m); } + else { fail++; failures.push(m); console.log(' FAIL ' + m); } +}; +const step = (t) => console.log('\n' + t); + +const call = async (method, p, body, token) => { + const res = await fetch(`${API}/${p}`, { + method, + headers: { + 'Content-Type': 'application/json', + ...(token ? { Authorization: 'Bearer ' + token } : {}), + }, + body: body === undefined ? undefined : JSON.stringify(body), + }); + const text = await res.text(); + let payload = null; + try { payload = JSON.parse(text); } catch { /* empty */ } + return { status: res.status, ok: res.ok, body: payload, raw: text }; +}; + +const signIn = (mobile) => + call('POST', 'customer/auth/verify-otp', { username: mobile, password: MASTER }); + +const uniq = String(Date.now()).slice(-7); +const made = { customers: [], experts: [], orders: [] }; + +(async () => { + // ── ADMIN ─────────────────────────────────────────────────────────────── + step('0 — ADMIN signs into the panel'); + const adminLogin = await call('POST', 'admin/auth/login', { + phone: ADMIN_USER, + password: ADMIN_PASS, + }); + chk(adminLogin.ok, `POST admin/auth/login -> ${adminLogin.status}`); + const adminToken = adminLogin.body?.api_token; + if (!adminToken) { + console.log('\n Could not sign in as admin; pass the credentials as argv[4]/argv[5].'); + console.log(' Response: ' + adminLogin.raw.slice(0, 300)); + process.exit(1); + } + chk(Boolean(adminToken), 'an admin token came back'); + + // ── SETUP through the ADMIN API, as an operator would ─────────────────── + step('SETUP — create a sales expert and a customer through the admin panel'); + + const expertMobile = `0913${uniq}`; + const expert = await call('POST', 'admin/users', { + fname: 'کارشناس', lname: 'کامپوز', + mobile: expertMobile, password: 'Test12345', + role: 'SALES_EXPERT', percent: 3.5, gram_level: 150, + }, adminToken); + chk(expert.ok, `create the sales expert -> ${expert.status}` + (expert.ok ? '' : ' ' + expert.raw.slice(0, 160))); + if (expert.body?.id) made.experts.push(expert.body.id); + + const custMobile = `0914${uniq}`; + const cust = await call('POST', 'admin/customers-omde/create', { + fname: 'مشتری', lname: 'کامپوز', name: 'مشتری کامپوز', + mobile: custMobile, percent: 3, type: 2, + store_name: 'گالری کامپوز', gram_level: 200, + }, adminToken); + chk(cust.ok, `create the customer -> ${cust.status}` + (cust.ok ? '' : ' ' + cust.raw.slice(0, 160))); + const custId = cust.body?.id; + if (custId) made.customers.push(custId); + + // A product to order. + step('SETUP — find an orderable product variant'); + const products = await call('GET', 'catalog/products'); + chk(products.ok, `GET catalog/products -> ${products.status}`); + const variant = (products.body ?? []) + .flatMap((p) => p.variants ?? []) + .find((v) => v && v.id); + chk(Boolean(variant), `found a variant to order (${variant?.id ?? 'none'})`); + if (!variant) { + console.log(' No product variants in this database — cannot place orders.'); + process.exit(1); + } + + // ── 1. THE EXPERT SIGNS IN ────────────────────────────────────────────── + step('1 — THE SALES EXPERT signs into the app with the master code'); + const expReq = await call('POST', 'customer/auth/request-otp', { username: expertMobile }); + chk(expReq.ok, `request-otp -> ${expReq.status}`); + + const expLogin = await signIn(expertMobile); + chk(expLogin.ok, `verify-otp with the master code -> ${expLogin.status}`); + const expToken = expLogin.body?.token; + chk(Boolean(expToken), 'an app token came back'); + + const whoami = await call('GET', 'expert/whoami', undefined, expToken); + chk(whoami.body?.isSalesExpert === true, + `the app is told this session is an expert (${whoami.body?.isSalesExpert})`); + + // ── 2. THE CUSTOMER SIGNS IN ──────────────────────────────────────────── + step('2 — AN ORDINARY CUSTOMER signs into the app with the master code'); + await call('POST', 'customer/auth/request-otp', { username: custMobile }); + const custLogin = await signIn(custMobile); + chk(custLogin.ok, `verify-otp with the master code -> ${custLogin.status}`); + const custToken = custLogin.body?.token; + chk(Boolean(custToken), 'an app token came back'); + + const custWho = await call('GET', 'expert/whoami', undefined, custToken); + chk(custWho.body?.isSalesExpert === false, + `and is NOT flagged as an expert (${custWho.body?.isSalesExpert})`); + + // ── 3. BOTH PLACE AN ORDER ────────────────────────────────────────────── + step('3 — BOTH place an order from the app'); + + // The exact payload both apps send from the cart (CartView.vue:200-211): + // wagePercent comes from the variant the cart pinned. No customerId — the + // server takes the subject from the token (D21). + const placeOrder = async (token, who) => { + const r = await call('POST', 'orders', { + items: [ + { + productVariantId: variant.id, + quantity: 1, + wagePercent: parseFloat(variant.defaultWagePercent ?? '0'), + }, + ], + }, token); + chk(r.ok, `${who} places an order -> ${r.status}${r.ok ? ' (' + r.body?.orderNumber + ')' : ' ' + r.raw.slice(0, 160)}`); + if (r.body?.id) made.orders.push({ id: r.body.id, number: r.body.orderNumber, who }); + return r.body; + }; + + const expOrder = await placeOrder(expToken, 'the EXPERT'); + const custOrder = await placeOrder(custToken, 'the CUSTOMER'); + + chk(Boolean(expOrder?.orderNumber && custOrder?.orderNumber), + 'both orders came back with an order number'); + chk(expOrder?.orderNumber !== custOrder?.orderNumber, + 'and they are two distinct orders'); + + // Each sees their own order in their own history, and only their own. + const expHistory = await call('GET', 'customer/me/orders', undefined, expToken); + chk(expHistory.body?.some((o) => o.id === expOrder?.id), + "the expert's own order is in the expert's history"); + chk(!expHistory.body?.some((o) => o.id === custOrder?.id), + "and the customer's order is NOT"); + + const custHistory = await call('GET', 'customer/me/orders', undefined, custToken); + chk(custHistory.body?.some((o) => o.id === custOrder?.id), + "the customer's own order is in the customer's history"); + chk(!custHistory.body?.some((o) => o.id === expOrder?.id), + "and the expert's order is NOT"); + + // ── 4. THE ADMIN SEES BOTH ────────────────────────────────────────────── + step('4 — THE ADMIN sees both orders in the panel'); + const list = await call('POST', 'admin/orders/list', { page: 1, limit: 100 }, adminToken); + chk(list.ok, `POST admin/orders/list -> ${list.status}`); + + const rows = list.body?.rows ?? list.body?.data ?? list.body?.result ?? []; + chk(Array.isArray(rows) && rows.length > 0, `the grid returned ${rows.length} row(s)`); + + const findRow = (id, number) => + rows.find((r) => r.id === id || r.orderNumber === number); + + chk(Boolean(findRow(expOrder?.id, expOrder?.orderNumber)), + `the EXPERT's order ${expOrder?.orderNumber} appears in the admin grid`); + chk(Boolean(findRow(custOrder?.id, custOrder?.orderNumber)), + `the CUSTOMER's order ${custOrder?.orderNumber} appears in the admin grid`); + + // And the admin can open each one. + for (const o of made.orders) { + const detail = await call('GET', `orders/admin/${o.id}`, undefined, adminToken); + chk(detail.ok, `the admin can open ${o.who}'s order ${o.number} -> ${detail.status}`); + chk(detail.body?.items?.length > 0, `and it carries its items (${detail.body?.items?.length})`); + } + + // ── 5. TOKEN BOUNDARIES still hold inside compose ─────────────────────── + step('5 — the token boundaries hold in the deployed container too'); + const appOnAdmin = await call('POST', 'admin/orders/list', { page: 1 }, expToken); + chk(appOnAdmin.status === 401 || appOnAdmin.status === 403, + `an expert's APP token on an admin route -> ${appOnAdmin.status}`); + + const custOnExpert = await call('POST', 'expert/my-customers', { search: null }, custToken); + chk(custOnExpert.status === 403 || custOnExpert.status === 401, + `a customer's token on an expert route -> ${custOnExpert.status}`); + + const adminOnApp = await call('GET', 'customer/me', undefined, adminToken); + chk(adminOnApp.status === 401, `an admin token on an app route -> ${adminOnApp.status}`); + + // ── CLEANUP ───────────────────────────────────────────────────────────── + step('CLEANUP'); + console.log(' This run created, through the admin API:'); + for (const o of made.orders) console.log(` order ${o.number} (${o.who})`); + console.log(` customer ${custMobile}, sales expert ${expertMobile}`); + console.log(' They are LEFT IN PLACE deliberately: this is the compose stack, its'); + console.log(' database is separate from development, and the rows are what the'); + console.log(' owner will look at in the panel. Delete them from the admin panel'); + console.log(' after the demo if they are not wanted.'); + + console.log(`\n${'='.repeat(60)}`); + console.log(`COMPOSE END-TO-END: passed ${pass} failed ${fail}`); + if (fail) { console.log('\nFailures:'); failures.forEach((f) => console.log(' - ' + f)); } + console.log('='.repeat(60)); + + process.exit(fail ? 1 : 0); +})().catch((e) => { + console.error('\nABORTED:', e); + process.exit(1); +}); diff --git a/apps/api/src/common/env.spec.ts b/apps/api/src/common/env.spec.ts new file mode 100644 index 0000000..b1abd2d --- /dev/null +++ b/apps/api/src/common/env.spec.ts @@ -0,0 +1,104 @@ +import { envOr, envOrNull } from './env'; + +/** + * DEPLOY-02 — a blank environment variable must be treated as unset. + * + * `docker-compose.yml` passes every optional variable as `${X:-}`, so an + * unconfigured one arrives as `""`, not `undefined`. The codebase originally + * read these with `process.env.X ?? 'default'`, which only falls back on + * `undefined` — so the empty string won, and + * + * new JwtService().sign(payload, { expiresIn: '' }) + * + * threw `"expiresIn" should be a number of seconds or string representing a + * timespan` on EVERY login. In Docker only, which is why it survived every + * suite that ran against a local dev process. These tests pin the behaviour. + */ +describe('envOr / envOrNull (DEPLOY-02)', () => { + const saved = { ...process.env }; + const KEY = 'GOLDBANK_ENV_SPEC_KEY'; + + beforeEach(() => { + delete process.env[KEY]; + }); + + afterAll(() => { + process.env = { ...saved }; + }); + + describe('envOr', () => { + it('falls back when the variable is absent', () => { + expect(envOr(KEY, '12h')).toBe('12h'); + }); + + it('falls back when the variable is the EMPTY STRING — the compose case', () => { + process.env[KEY] = ''; + expect(envOr(KEY, '12h')).toBe('12h'); + }); + + it('falls back when the variable is only whitespace', () => { + process.env[KEY] = ' '; + expect(envOr(KEY, '12h')).toBe('12h'); + }); + + it('returns the value when one is set', () => { + process.env[KEY] = '30d'; + expect(envOr(KEY, '12h')).toBe('30d'); + }); + + it('trims a padded value rather than passing the padding through', () => { + process.env[KEY] = ' 30d '; + expect(envOr(KEY, '12h')).toBe('30d'); + }); + + it('never returns an empty string, whatever is set', () => { + for (const v of ['', ' ', '\t', '\n', ' \t\n ']) { + process.env[KEY] = v; + expect(envOr(KEY, 'fallback')).not.toBe(''); + } + }); + }); + + describe('envOrNull', () => { + it('is null when absent, empty or blank', () => { + expect(envOrNull(KEY)).toBeNull(); + process.env[KEY] = ''; + expect(envOrNull(KEY)).toBeNull(); + process.env[KEY] = ' '; + expect(envOrNull(KEY)).toBeNull(); + }); + + it('is the trimmed value when set', () => { + process.env[KEY] = ' secret '; + expect(envOrNull(KEY)).toBe('secret'); + }); + }); + + /** + * The specific failure: an empty expiresIn must never reach jsonwebtoken. + * Asserted against the real library so the test fails if the reader regresses. + */ + describe('the original failure', () => { + it('jsonwebtoken rejects an empty expiresIn, and envOr never produces one', () => { + // Required rather than imported: jsonwebtoken is a transitive dependency + // of @nestjs/jwt and ships no types here. Typed loosely on purpose — this + // test is about the VALUE reaching it, not about its signature. + // eslint-disable-next-line @typescript-eslint/no-var-requires + const jwt = require('jsonwebtoken') as { + sign: (payload: object, secret: string, opts: { expiresIn: string }) => string; + }; + + // The bug: this is exactly what `process.env.JWT_EXPIRES_IN ?? '12h'` + // produced under compose, and it threw on every login. + expect(() => jwt.sign({ sub: 'x' }, 'secret', { expiresIn: '' })).toThrow( + /expiresIn/, + ); + + // The fix: envOr turns that same blank into the default, which signs. + process.env[KEY] = ''; + const token = jwt.sign({ sub: 'x' }, 'secret', { expiresIn: envOr(KEY, '12h') }); + expect(typeof token).toBe('string'); + expect(token.split('.')).toHaveLength(3); + }); + }); +}); diff --git a/apps/api/src/common/env.ts b/apps/api/src/common/env.ts new file mode 100644 index 0000000..4a8668d --- /dev/null +++ b/apps/api/src/common/env.ts @@ -0,0 +1,28 @@ +/** + * Environment readers that treat a BLANK value as unset. + * + * `process.env.X ?? 'default'` only falls back when the variable is absent, so + * an empty string sails straight through. That matters here because + * `docker-compose.yml` passes every optional variable as `${X:-}`, which sets + * it to `""` when the operator has not configured it. jsonwebtoken then throws + * + * "expiresIn" should be a number of seconds or string representing a timespan + * + * and EVERY login 500s — in Docker only, which is why it survived every test + * that ran against a local dev process. Found by the pre-delivery compose run; + * recorded as DEPLOY-02. + */ + +/** The variable's trimmed value, or `fallback` when it is unset, empty or blank. */ +export function envOr(name: string, fallback: string): string { + const raw = process.env[name]; + const trimmed = typeof raw === 'string' ? raw.trim() : ''; + return trimmed === '' ? fallback : trimmed; +} + +/** The variable's trimmed value, or null when it is unset, empty or blank. */ +export function envOrNull(name: string): string | null { + const raw = process.env[name]; + const trimmed = typeof raw === 'string' ? raw.trim() : ''; + return trimmed === '' ? null : trimmed; +} diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index 3170aff..45e89a9 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -8,6 +8,7 @@ import { AppModule } from './app.module'; import { prepareDatabase } from './prisma/bootstrap'; import { PrismaExceptionFilter } from './prisma/prisma-exception.filter'; import { assertSmsConfigured } from './modules/sms/sms.service'; +import { envOr, envOrNull } from './common/env'; import { masterOtpCode, masterOtpEnabled } from './modules/customer-auth/customer-auth.service'; // serve-static's bundled `mime` version predates .avif, so it falls back to @@ -29,8 +30,8 @@ async function bootstrap() { // and a sales expert's app token carries their AdminUser id in `sub`. Warn as // loudly as the master-code banner rather than fail the boot, so a running // deployment that has not set it yet still starts. - if (!process.env.JWT_CUSTOMER_SECRET?.trim()) { - const sharedWithAdmin = Boolean(process.env.JWT_SECRET?.trim()); + if (!envOrNull('JWT_CUSTOMER_SECRET')) { + const sharedWithAdmin = Boolean(envOrNull('JWT_SECRET')); // eslint-disable-next-line no-console console.warn( '\n' + @@ -87,7 +88,7 @@ async function bootstrap() { app.enableCors(); // Uploaded files, mounted OUTSIDE the /api prefix so the URLs stored on // UploadedFile.url ("/uploads/") resolve directly. - app.useStaticAssets(process.env.UPLOAD_DIR ?? join(process.cwd(), 'storage', 'uploads'), { + app.useStaticAssets(envOr('UPLOAD_DIR', join(process.cwd(), 'storage', 'uploads')), { prefix: '/uploads/', index: false, dotfiles: 'deny', @@ -96,7 +97,9 @@ async function bootstrap() { if (mime) res.setHeader('Content-Type', mime); }, }); - const port = Number(process.env.PORT ?? 3000); + // envOr, not `??`: Number('') is 0, which would bind a random ephemeral port + // and make the container unreachable at the port compose maps. See DEPLOY-02. + const port = Number(envOr('PORT', '3000')); try { // Bind all interfaces explicitly so the container is reachable from nginx. diff --git a/apps/api/src/modules/admin-uploads/upload-path.ts b/apps/api/src/modules/admin-uploads/upload-path.ts index 32fbc25..afc3ada 100644 --- a/apps/api/src/modules/admin-uploads/upload-path.ts +++ b/apps/api/src/modules/admin-uploads/upload-path.ts @@ -1,5 +1,6 @@ import { existsSync, statSync } from 'node:fs'; import { basename, isAbsolute, join, relative, resolve } from 'node:path'; +import { envOr } from '../../common/env'; /** * Directory files are written to and served from. @@ -10,7 +11,7 @@ import { basename, isAbsolute, join, relative, resolve } from 'node:path'; * directory and the previous static root then 404'd every file. */ export function getUploadDir(): string { - return resolve(process.env.UPLOAD_DIR ?? join(process.cwd(), 'storage', 'uploads')); + return resolve(envOr('UPLOAD_DIR', join(process.cwd(), 'storage', 'uploads'))); } const UPLOAD_MIME: Record = { diff --git a/apps/api/src/modules/auth/auth.module.ts b/apps/api/src/modules/auth/auth.module.ts index d71f978..1d2dde8 100644 --- a/apps/api/src/modules/auth/auth.module.ts +++ b/apps/api/src/modules/auth/auth.module.ts @@ -3,6 +3,7 @@ import { APP_GUARD } from '@nestjs/core'; import { JwtModule } from '@nestjs/jwt'; import { PassportModule } from '@nestjs/passport'; import { PrismaModule } from '../../prisma/prisma.module'; +import { envOr } from '../../common/env'; import { AuthController } from './auth.controller'; import { AuthService } from './auth.service'; import { JwtStrategy } from './jwt.strategy'; @@ -13,11 +14,15 @@ import { JwtAuthGuard, PermissionsGuard } from './guards'; PrismaModule, PassportModule, JwtModule.register({ - secret: process.env.JWT_SECRET ?? 'dev-only-insecure-secret', + // envOr, not `??`: docker-compose passes every optional variable as + // `${X:-}`, so an unconfigured one arrives as "" rather than undefined. + // An empty expiresIn makes jsonwebtoken throw on EVERY sign, which 500s + // admin login in Docker only. See DEPLOY-02. + secret: envOr('JWT_SECRET', 'dev-only-insecure-secret'), // jsonwebtoken types expiresIn as a template-literal union, which a plain // `string` from process.env does not satisfy; the cast is the documented escape. signOptions: { - expiresIn: (process.env.JWT_EXPIRES_IN ?? '12h') as `${number}h`, + expiresIn: envOr('JWT_EXPIRES_IN', '12h') as `${number}h`, }, }), ], diff --git a/apps/api/src/modules/auth/jwt.strategy.ts b/apps/api/src/modules/auth/jwt.strategy.ts index 1598dc7..b52988e 100644 --- a/apps/api/src/modules/auth/jwt.strategy.ts +++ b/apps/api/src/modules/auth/jwt.strategy.ts @@ -2,6 +2,7 @@ import { Injectable, UnauthorizedException } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { ExtractJwt, Strategy } from 'passport-jwt'; import { PrismaService } from '../../prisma/prisma.service'; +import { envOr } from '../../common/env'; import { expandWithParents } from './acl.constants'; export interface JwtPayload { @@ -49,7 +50,7 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, - secretOrKey: process.env.JWT_SECRET ?? 'dev-only-insecure-secret', + secretOrKey: envOr('JWT_SECRET', 'dev-only-insecure-secret'), }); } diff --git a/apps/api/src/modules/customer-auth/customer-auth.service.ts b/apps/api/src/modules/customer-auth/customer-auth.service.ts index ae8f68a..bb98cc3 100644 --- a/apps/api/src/modules/customer-auth/customer-auth.service.ts +++ b/apps/api/src/modules/customer-auth/customer-auth.service.ts @@ -3,6 +3,7 @@ import { JwtService } from '@nestjs/jwt'; import { AdminRole, CustomerKind, MessageAudience, Prisma } from '@prisma/client'; import * as bcrypt from 'bcryptjs'; import { PrismaService } from '../../prisma/prisma.service'; +import { envOr, envOrNull } from '../../common/env'; import { CUSTOMER_TOKEN_TYPE, EXPERT_TOKEN_TYPE, type CustomerJwtPayload } from './customer-jwt.strategy'; import { faToEn } from './digits'; import { SmsService, smsConfigured } from '../sms/sms.service'; @@ -386,7 +387,7 @@ export class CustomerAuthService { }; return this.jwt.signAsync(payload, { secret: customerJwtSecret(), - expiresIn: (process.env.JWT_CUSTOMER_EXPIRES_IN ?? '30d') as `${number}d`, + expiresIn: envOr('JWT_CUSTOMER_EXPIRES_IN', '30d') as `${number}d`, }); } @@ -408,7 +409,7 @@ export class CustomerAuthService { secret: customerJwtSecret(), // jsonwebtoken types expiresIn as a template-literal union, which a plain // string from process.env does not satisfy — same cast AuthModule uses. - expiresIn: (process.env.JWT_CUSTOMER_EXPIRES_IN ?? '30d') as `${number}d`, + expiresIn: envOr('JWT_CUSTOMER_EXPIRES_IN', '30d') as `${number}d`, }); } @@ -512,8 +513,8 @@ export class CustomerAuthService { */ export function customerJwtSecret(): string { return ( - process.env.JWT_CUSTOMER_SECRET ?? - process.env.JWT_SECRET ?? + envOrNull('JWT_CUSTOMER_SECRET') ?? + envOrNull('JWT_SECRET') ?? 'dev-only-insecure-customer-secret' ); } diff --git a/apps/api/src/modules/sms/sms.service.ts b/apps/api/src/modules/sms/sms.service.ts index 9b01661..a233a2f 100644 --- a/apps/api/src/modules/sms/sms.service.ts +++ b/apps/api/src/modules/sms/sms.service.ts @@ -1,6 +1,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { MessageAudience } from '@prisma/client'; import { PrismaService } from '../../prisma/prisma.service'; +import { envOr } from '../../common/env'; /** * SMS delivery — port of henza_server/app/Models/Sms.php. @@ -49,8 +50,8 @@ export interface SmsSendOptions { /** Reads the provider config. Exported so main.ts can validate it at boot. */ export function smsConfig() { return { - provider: (process.env.SMS_PROVIDER ?? 'trez').toLowerCase(), - url: process.env.SMS_TREZ_URL ?? DEFAULT_TREZ_URL, + provider: envOr('SMS_PROVIDER', 'trez').toLowerCase(), + url: envOr('SMS_TREZ_URL', DEFAULT_TREZ_URL), username: process.env.SMS_TREZ_USERNAME ?? '', password: process.env.SMS_TREZ_PASSWORD ?? '', senderLine: process.env.SMS_TREZ_SENDER_LINE ?? '', diff --git a/docker-compose.delivery-test.yml b/docker-compose.delivery-test.yml new file mode 100644 index 0000000..0c88259 --- /dev/null +++ b/docker-compose.delivery-test.yml @@ -0,0 +1,8 @@ +# Temporary overlay used ONLY to verify the compose stack end to end before +# delivery. It publishes the API port to the host so the test script can reach +# it; the real deployment keeps `expose` and routes by domain. Not part of the +# deployment — start it explicitly with `-f docker-compose.yml -f this`. +services: + api: + ports: + - "3100:3000" diff --git a/docker-compose.yml b/docker-compose.yml index 8eab3d6..ca275f4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,6 +47,9 @@ services: - SMS_TREZ_USERNAME=${SMS_TREZ_USERNAME:-} - SMS_TREZ_PASSWORD=${SMS_TREZ_PASSWORD:-} - SMS_TREZ_SENDER_LINE=${SMS_TREZ_SENDER_LINE:-} + # Admin mobiles told when a customer completes their profile (D22). + # Blank = nobody is notified; the upload still succeeds. + - PROFILE_ADMIN_MOBILES=${PROFILE_ADMIN_MOBILES:-} # No host ports — Dokploy/Traefik routes by domain to container port 3000 expose: - "3000" diff --git a/docs/parity/DECISIONS.md b/docs/parity/DECISIONS.md index 9bce721..7286ccb 100644 --- a/docs/parity/DECISIONS.md +++ b/docs/parity/DECISIONS.md @@ -1338,3 +1338,26 @@ than rendering a fabricated value. Recorded as REP-01. The reference gated the page on `myStore.hasMCode()`; with no mcode in the target the gate is simply a session, and the server scopes every row to the token. + +## D25 — blank env vars are treated as unset, 2026-09-23 + +Found by the pre-delivery compose run (DEPLOY-02): **admin login returned 500 +for every user in Docker.** `docker-compose.yml` passes each optional variable +as `${X:-}`, so an unconfigured one arrives as `""`, not `undefined`. The code +read them with `process.env.X ?? 'default'`, which only falls back on +`undefined` — so the empty string won and `jsonwebtoken` threw on every sign. + +Invisible to every existing suite, all of which ran against a local dev process +where the variable is genuinely absent. + +`src/common/env.ts` now provides `envOr` / `envOrNull`, which treat blank as +unset, and every auth, port, upload and SMS read uses them. Three of those were +not merely cosmetic: a blank `UPLOAD_DIR` would have served static files from +the **filesystem root**, a blank `PORT` would have bound a random ephemeral port +(`Number('') === 0`), and a blank `JWT_SECRET` would have become the verifying +key. Pinned by `src/common/env.spec.ts`, which reproduces the original throw +against the real `jsonwebtoken` and asserts the reader can no longer produce it. + +**Standing rule from here on:** read an environment variable with `envOr` / +`envOrNull`, never with `??`, unless an empty string is a meaningful value for +that specific variable. diff --git a/docs/parity/ISSUES.md b/docs/parity/ISSUES.md index 26805ab..02f9fb4 100644 --- a/docs/parity/ISSUES.md +++ b/docs/parity/ISSUES.md @@ -165,3 +165,10 @@ Severity: `Critical` (money loss / security / data loss) · `High` · `Medium` | **REP-01** | Medium (documented gap) | REF external Tahesab system vs TGT `LedgerEntry` | The reference's statement carried columns the target has no source for: foreign currency (`rial_V2`/`rial_Kh2`/`tahesabP2`), `vaznPlastik`, `nO_kar` (نام کارساخته), `jens_felez` (جنس فلز) and the external system's internal ids (`gid`/`rec`/`reg`). | `henza_pwa/src/type/accounting.ts`; `AccountingDetail.vue:34-46` | The API returns `null`/zero for these and the UI **omits** the row rather than showing a fabricated value. If the business needs them, they need columns on `LedgerEntry` first. | | **REP-02** | Low (performance) | TGT `customer-report.service.ts` `summary()` | The running balance makes a row depend on every entry before it, so the whole confirmed ledger for the customer is folded in memory and the page sliced afterwards. Cheap at one customer's own document counts; linear in their history. | `summary()` `findMany` without `take` | If a customer ever accumulates enough entries for it to matter, move the fold to a stored running total written at entry time. | | **SEC-17** | Medium (reference, not applicable to target) | REF `Customer/TahesabController.php` | The reference calls the external accounting system with `Http::withoutVerifying()` (TLS certificate verification **disabled**) against a bare IP, with a bearer token and an `sfx` secret hardcoded in the source and embedded in the URL. | `->withToken("S3F1P8…")`, `sfx=wcG97tkF…` in four URLs | Not applicable — the target makes no external call. Listed so it is not reintroduced if a Tahesab integration is ever added; the credentials in that reference file should be rotated regardless. | + +## Found by the pre-delivery Docker Compose run (2026-09-23) + +| ID | Severity | Where | Description | Evidence | Suggested fix | +|---|---|---|---|---|---| +| **DEPLOY-02** | ~~**Critical**~~ **FIXED** | TGT `auth.module.ts`, `customer-auth.service.ts`, `jwt.strategy.ts`, `main.ts`, `upload-path.ts`, `sms.service.ts` | **Admin login returned 500 for every user in Docker.** `docker-compose.yml` passes each optional variable as `${X:-}`, so an unconfigured one arrives as `""`, not `undefined`. The code read them with `process.env.X ?? 'default'`, which only falls back on `undefined` — so the empty string won and `jsonwebtoken` threw `"expiresIn" should be a number of seconds or string representing a timespan` on **every** sign. Invisible to every suite that ran against a local dev process, where the variable is genuinely absent. The same pattern also meant a blank `UPLOAD_DIR` would have served static files from the **filesystem root**, and a blank `PORT` would have bound a random ephemeral port (`Number('') === 0`), making the container unreachable. | Container log: `ERROR [ExceptionsHandler] "expiresIn" should be a number of seconds or string representing a timespan` | ✅ Added `src/common/env.ts` (`envOr` / `envOrNull`) which treats blank as unset, and applied it to every auth, port, upload and SMS read. Pinned by `src/common/env.spec.ts` (9 tests), which reproduces the original throw against the real `jsonwebtoken` and asserts the reader can no longer produce it. Re-verified end to end in compose: **32/32**. | +| **DEPLOY-03** | Medium (process) | TGT `apps/api/Dockerfile` | The image is `COPY release/ ./` — it ships a **pre-built bundle** produced on the developer machine by `pnpm build:release`, not a build performed inside the image. So `docker compose build` happily produces an image from a **stale** `release/app.js` and reports success. At the start of this run that bundle was four days old, and the rebuilt container was missing every route added since. | `apps/api/release/app.js` dated Sep 19 while the source was Sep 23; the container mapped none of the new routes | **`pnpm build:release` MUST be run before `docker compose build`.** Noted in DELIVERY.md's deploy section. A longer-term fix is to build inside the Dockerfile, or to have the build fail when `release/` is older than `src/`. |