diff --git a/src/orders/dto/order.dto.ts b/src/orders/dto/order.dto.ts index 7936626..3f20012 100644 --- a/src/orders/dto/order.dto.ts +++ b/src/orders/dto/order.dto.ts @@ -1,5 +1,6 @@ import { ArrayMinSize, + IsBoolean, IsDateString, IsEnum, IsIn, @@ -12,7 +13,7 @@ import { ValidateIf, ValidateNested, } from 'class-validator'; -import { Type } from 'class-transformer'; +import { Transform, Type } from 'class-transformer'; import { OrderStatus, TransactionType } from '@prisma/client'; import { ShippingAddressDto } from '../../cart/dto/cart.dto'; @@ -172,4 +173,13 @@ export class ListOrdersDto { @IsOptional() @IsIn(['paid', 'unpaid', 'failed']) paymentStatus?: 'paid' | 'unpaid' | 'failed'; + + /** + * When true, only return the authenticated user's own orders + * (even if they also have orders.read staff permission). + */ + @IsOptional() + @Transform(({ value }) => value === true || value === 'true' || value === '1') + @IsBoolean() + selfOnly?: boolean; } diff --git a/src/orders/orders.service.ts b/src/orders/orders.service.ts index 7e486c5..deae6e6 100644 --- a/src/orders/orders.service.ts +++ b/src/orders/orders.service.ts @@ -113,9 +113,9 @@ export class OrdersService { const where: Prisma.OrderWhereInput = { businessId, - ...(scope === 'customer' ? { userId: actor.id } : {}), + ...(scope === 'customer' || query.selfOnly ? { userId: actor.id } : {}), ...(query.status ? { status: query.status } : {}), - ...(scope === 'admin' && query.customerUserId + ...(scope === 'admin' && !query.selfOnly && query.customerUserId ? { userId: BigInt(query.customerUserId) } : {}), ...(query.orderNumber @@ -150,7 +150,7 @@ export class OrdersService { }, } : {}), - ...(scope === 'admin' && query.customerQuery?.trim() + ...(scope === 'admin' && !query.selfOnly && query.customerQuery?.trim() ? { customer: this.buildCustomerSearchFilter(query.customerQuery.trim()), } @@ -931,20 +931,27 @@ export class OrdersService { cellNumber: order.customer.cellNumber, email: order.customer.email, }, - items: order.items.map((item) => ({ - id: item.id.toString(), - storeItemVariantId: item.storeItemVariantId?.toString() ?? null, - productId: item.productId.toString(), - productTitle: item.productTitle, - productImage: this.resolveProductImage(item.product, galleryByProductId), - variantSku: item.variantSku, - unitPrice: Number(item.unitPrice), - compareAtPrice: - item.compareAtPrice === null ? null : Number(item.compareAtPrice), - quantity: item.quantity, - lineTotal: Number(item.lineTotal), - selections: item.selectionsSnapshot, - })), + items: order.items.map((item) => { + const content = isRecord(item.product.content) ? item.product.content : null + const productNameFa = + content && typeof content.nameFa === 'string' ? content.nameFa.trim() : '' + return { + id: item.id.toString(), + storeItemVariantId: item.storeItemVariantId?.toString() ?? null, + productId: item.productId.toString(), + productTitle: item.productTitle, + productNameFa: productNameFa || null, + productSlug: item.product.slug ?? null, + productImage: this.resolveProductImage(item.product, galleryByProductId), + variantSku: item.variantSku, + unitPrice: Number(item.unitPrice), + compareAtPrice: + item.compareAtPrice === null ? null : Number(item.compareAtPrice), + quantity: item.quantity, + lineTotal: Number(item.lineTotal), + selections: item.selectionsSnapshot, + } + }), transactions: (order.transactions ?? []).map((transaction) => this.serializeTransaction(transaction), ),