import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; import { PreOrderStatus, Prisma } from '@prisma/client'; import { PrismaService } from '../../prisma/prisma.service'; import { TableService } from '../admin-table/table.service'; import type { TableQueryDto } from '../admin-table/table-query.dto'; /** * Custom / made-to-order requests — legacy "سفارش های ساختنی". * * Legacy served these from CustomOrderController (`POST get-custom-orders`, * `PUT custom-order/{id}`, `DELETE custom-order/{id}`). The redesigned schema * models them as PreOrder: a design request that an admin prices and later * converts into a real, debt-bearing Order. */ const PREORDER_STATUS_LABELS: Record = { PLACED: 'ثبت شده', ADMIN_APPROVED: 'تایید مدیر', WORKSHOP_APPROVED: 'تایید کارگاه', READY_TO_SHIP: 'آماده ارسال', DELIVERED: 'تحویل شده', CANCELLED: 'لغو شده', }; @Injectable() export class AdminPreOrdersService { constructor( private readonly prisma: PrismaService, private readonly table: TableService, ) {} private static readonly ALLOWED = [ 'id', 'status', 'createdAt', 'updatedAt', 'colorName', 'sizeSpec', 'stoneColor', 'minaColor', 'estimatedWeightGrams', 'estimatedPriceRial', 'customer.fullName', 'customer.mobile', ]; /** Legacy `POST get-custom-orders`. */ async list(query: TableQueryDto) { this.table.assertNotExport(query); const { where, orderBy, skip, take, page, limit } = this.table.build(query, { searchable: ['customer.fullName', 'customer.mobile'], allowed: AdminPreOrdersService.ALLOWED, defaultOrderBy: { createdAt: 'desc' }, }); const [rows, total] = await Promise.all([ this.prisma.preOrder.findMany({ where: where as Prisma.PreOrderWhereInput, orderBy: orderBy as Prisma.PreOrderOrderByWithRelationInput, skip, take, include: { customer: true, city: true, workshop: true }, }), this.prisma.preOrder.count({ where: where as Prisma.PreOrderWhereInput }), ]); return this.table.respond(rows.map((r) => this.toRow(r)), total, query, page, limit); } /** Legacy field names (color, size, color_mina, color_stone, ...) preserved. */ private toRow( p: Prisma.PreOrderGetPayload<{ include: { customer: true; city: true; workshop: true } }>, ) { return { id: p.id, status: p.status, sts_str_fa: PREORDER_STATUS_LABELS[p.status], color: p.colorName, size: p.sizeSpec, color_mina: p.minaColor, color_stone: p.stoneColor, // legacy 'sum' (quantity); a PreOrder models a single design request sum: 1, description: p.description, designImageUrl: p.designImageUrl, estimatedWeightGrams: p.estimatedWeightGrams, estimatedPriceRial: p.estimatedPriceRial, time_creat_fa: p.createdAt, time_update_fa: p.updatedAt, convertedOrderId: p.convertedOrderId, city: p.city ? { id: p.city.id, title: p.city.name } : null, workshop: p.workshop ? { id: p.workshop.id, name: p.workshop.name } : null, customer: { id: p.customer.id, // 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, }, }; } async findOne(id: string) { const p = await this.prisma.preOrder.findUnique({ where: { id }, include: { customer: true, city: true, workshop: true }, }); if (!p) throw new NotFoundException('سفارش ساختنی یافت نشد'); return this.toRow(p); } /** Legacy `PUT custom-order/{id}`. */ async update(id: string, data: Record) { const existing = await this.prisma.preOrder.findUnique({ where: { id } }); if (!existing) throw new NotFoundException('سفارش ساختنی یافت نشد'); const patch: Prisma.PreOrderUpdateInput = {}; if (data.status !== undefined) patch.status = data.status as PreOrderStatus; if (data.color !== undefined) patch.colorName = data.color as string; if (data.size !== undefined) patch.sizeSpec = data.size as string; if (data.color_mina !== undefined) patch.minaColor = data.color_mina as string; if (data.color_stone !== undefined) patch.stoneColor = data.color_stone as string; if (data.description !== undefined) patch.description = data.description as string; if (data.estimatedWeightGrams !== undefined) { patch.estimatedWeightGrams = new Prisma.Decimal(data.estimatedWeightGrams as string); } if (data.estimatedPriceRial !== undefined) { patch.estimatedPriceRial = new Prisma.Decimal(data.estimatedPriceRial as string); } return this.prisma.preOrder.update({ where: { id }, data: patch }); } /** Legacy `DELETE custom-order/{id}`. */ async remove(id: string) { const p = await this.prisma.preOrder.findUnique({ where: { id } }); if (!p) throw new NotFoundException('سفارش ساختنی یافت نشد'); // Once converted into a real Order the design request is part of an // accounting trail; deleting it would orphan that link. Legacy had no guard. if (p.convertedOrderId) { throw new BadRequestException( 'حذف ممکن نیست: این سفارش ساختنی قبلاً به سفارش قطعی تبدیل شده است.', ); } await this.prisma.$transaction([ this.prisma.preOrderStatusEvent.deleteMany({ where: { preOrderId: id } }), this.prisma.preOrder.delete({ where: { id } }), ]); return { success: true }; } statusOptions() { return Object.entries(PREORDER_STATUS_LABELS).map(([value, title]) => ({ value, title })); } }