import { BadRequestException, Injectable, Logger, NotFoundException, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { MoreThanOrEqual, Repository } from 'typeorm'; import { Queue } from './entities/queue.entity'; import { CreateQueueDto } from './dto/create-queue.dto'; import { UpdateQueueDto } from './dto/update-queue.dto'; import { Machine } from '@/machines/entities/machine.entity'; import { Page } from '@/pages/entities/page.entity'; import { Order } from '@/orders/entities/order.entity'; import { Employee } from '@/employees/entities/employee.entity'; import { QueueType } from '@/queue-types/entities/queue-type.entity'; @Injectable() export class QueuesService { private readonly logger = new Logger(QueuesService.name); deleteByDate( dateStr: string, ): { deletedCount: number } | PromiseLike<{ deletedCount: number }> { throw new Error('Method not implemented.'); } constructor( @InjectRepository(Queue) private readonly queueRepository: Repository<Queue>, @InjectRepository(Machine) private readonly machineRepository: Repository<Machine>, @InjectRepository(Page) private readonly pageRepository: Repository<Page>, @InjectRepository(Order) private readonly orderRepository: Repository<Order>, @InjectRepository(Employee) private readonly employeeRepository: Repository<Employee>, @InjectRepository(QueueType) // ✅ เพิ่มบรรทัดนี้ private readonly queueTypeRepository: Repository<QueueType>, ) {} async getLatestQueuesFromDate(dateStr?: string): Promise<Queue[]> { this.logger.log( `📡 Fetching queues from ${dateStr || 'the most recent date'}`, ); let startDate: Date; if (dateStr) { // ✅ ถ้ามี `dateStr` ให้ใช้เป็นวันที่เริ่มต้น const parts = dateStr.includes('/') ? dateStr.trim().split('/') : dateStr.trim().split('-'); if (parts.length !== 3) { throw new BadRequestException( 'Invalid date format. Use dd/MM/yyyy or yyyy-MM-dd', ); } if (dateStr.includes('/')) { const [day, month, year] = parts.map(Number); startDate = new Date(year, month - 1, day); } else { const [year, month, day] = parts.map(Number); startDate = new Date(year, month - 1, day); } } else { // ✅ ถ้าไม่ส่ง `dateStr` มา → ใช้วันที่ล่าสุดแทน const latestExistingQueue = await this.queueRepository.find({ order: { createdAt: 'DESC' }, take: 1, }); if (!latestExistingQueue.length) { this.logger.warn(`⚠️ No queues found in the system`); return []; } startDate = new Date(latestExistingQueue[0].createdAt); } startDate.setHours(0, 0, 0, 0); // ตั้งเป็น 00:00 ของวันนั้น this.logger.log( `🔍 Fetching all queues from ${startDate.toISOString()} onwards`, ); return await this.queueRepository.find({ where: { createdAt: MoreThanOrEqual(startDate) }, order: { createdAt: 'ASC' }, // เรียงจากเก่าสุดไปใหม่สุด }); } async create(createQueueDto: CreateQueueDto) { const { MachineID, PageID, OrderID, EmployeeIds = [], ...queueData } = createQueueDto; // ✅ แปลง ID ให้แน่ใจว่าเป็นตัวเลข const machineId = Number(MachineID); const pageId = Number(PageID); const orderId = Number(OrderID); if (isNaN(machineId)) throw new BadRequestException(`Invalid MachineID: ${MachineID}`); if (isNaN(pageId)) throw new BadRequestException(`Invalid PageID: ${PageID}`); if (isNaN(orderId)) throw new BadRequestException(`Invalid OrderID: ${OrderID}`); // 🔍 หา Machine const machine = await this.machineRepository.findOne({ where: { MachineID: machineId }, }); if (!machine) throw new NotFoundException(`Machine with ID ${machineId} not found`); // 🔍 หา Page const page = await this.pageRepository.findOne({ where: { PageID: pageId }, }); if (!page) throw new NotFoundException(`Page with ID ${pageId} not found`); // 🔍 เช็คประเภทว่าเป็น ORDER หรือ BUFFER let order = null; let queueTypeName = 'ORDER'; if (orderId === -1) { queueTypeName = 'ผลิตเผื่อ'; } else { order = await this.orderRepository.findOne({ where: { OrderID: orderId }, }); if (!order) throw new NotFoundException(`Order with ID ${orderId} not found`); } // 🔍 หา QueueType const queueType = await this.queueTypeRepository.findOne({ where: { TypeName: queueTypeName }, }); if (!queueType) throw new NotFoundException(`QueueType '${queueTypeName}' not found`); // ✅ เตรียม employees let employees = []; if (Array.isArray(EmployeeIds) && EmployeeIds.length > 0) { const validEmployeeIds = EmployeeIds.map((id) => Number(id)).filter( (id) => !isNaN(id) && id > 0, ); if (validEmployeeIds.length !== EmployeeIds.length) { throw new BadRequestException(`Invalid EmployeeIds provided`); } employees = await this.employeeRepository.findByIds(validEmployeeIds); const foundIds = employees.map((e) => e.EmployeeID); const missingIds = validEmployeeIds.filter( (id) => !foundIds.includes(id), ); if (missingIds.length > 0) { throw new NotFoundException( `Employees not found: ${missingIds.join(', ')}`, ); } } // ✅ สร้าง Queue object const newQueue = this.queueRepository.create({ ...queueData, machine, page, order: queueTypeName === 'ORDER' ? order : null, employees, QueueType: queueType, // 👈 ชื่อต้องตรงกับ entity }); const savedQueue = await this.queueRepository.save(newQueue); return { message: 'Queue created successfully', queue: savedQueue, }; } async createMultiple(createQueueDtos: CreateQueueDto[]) { const newQueues = []; if (!Array.isArray(createQueueDtos)) { throw new BadRequestException('Request body must be an array of queues'); } for (const createQueueDto of createQueueDtos) { const { MachineID, PageID, OrderID, EmployeeIds, ...queueData } = createQueueDto; // ✅ แปลงค่าเบื้องต้น const machineId = Number(MachineID); const orderId = Number(OrderID); const pageId = Number(PageID); if (isNaN(machineId)) throw new BadRequestException(`Invalid MachineID: ${MachineID}`); if (isNaN(orderId)) throw new BadRequestException(`Invalid OrderID: ${OrderID}`); if (isNaN(pageId)) throw new BadRequestException(`Invalid PageID: ${PageID}`); // ✅ หา Machine const machine = await this.machineRepository.findOne({ where: { MachineID: machineId }, }); if (!machine) throw new NotFoundException(`Machine with ID ${machineId} not found`); // ✅ หา Page const page = await this.pageRepository.findOne({ where: { PageID: pageId }, }); if (!page) throw new NotFoundException(`Page with ID ${pageId} not found`); // ✅ เช็คประเภทคิว และหา Order (ถ้ามี) let order = null; let queueTypeName = 'ORDER'; if (orderId === -1) { queueTypeName = 'ผลิตเผื่อ'; } else { order = await this.orderRepository.findOne({ where: { OrderID: orderId }, }); if (!order) throw new NotFoundException(`Order with ID ${orderId} not found`); } // ✅ หา QueueType ตามประเภท const queueType = await this.queueTypeRepository.findOne({ where: { TypeName: queueTypeName }, }); if (!queueType) throw new NotFoundException(`QueueType '${queueTypeName}' not found`); // ✅ ตรวจสอบ EmployeeIds (optional) let validEmployeeIds: number[] = []; let employees = []; if (Array.isArray(EmployeeIds) && EmployeeIds.length > 0) { validEmployeeIds = EmployeeIds.map((id) => Number(id)).filter( (id) => !isNaN(id) && id > 0, ); if (validEmployeeIds.length !== EmployeeIds.length) { throw new BadRequestException( `Invalid EmployeeIds provided: ${EmployeeIds}`, ); } employees = await this.employeeRepository.findByIds(validEmployeeIds); const foundIds = employees.map((e) => e.EmployeeID); const missingIds = validEmployeeIds.filter( (id) => !foundIds.includes(id), ); if (missingIds.length > 0) { console.error(`❌ Missing EmployeeIDs: ${missingIds.join(', ')}`); throw new NotFoundException( `Employees not found: ${missingIds.join(', ')}`, ); } } // ✅ สร้าง Queue object const newQueue = this.queueRepository.create({ ...queueData, machine, page, order: queueTypeName === 'ORDER' ? order : null, employees, QueueType: queueType, // 👈 ต้องตรงชื่อ property ใน entity }); newQueues.push(newQueue); } // ✅ บันทึกทั้งหมดพร้อมกัน const savedQueues = await this.queueRepository.save(newQueues); return { message: `${savedQueues.length} queues created successfully`, queues: savedQueues, }; } // ✅ Fetch all Queues with relationships async findAll() { return await this.queueRepository.find({ relations: [ 'machine', 'page', 'order', 'order.customer', // 👈 เพิ่มแบบนี้ ไม่ใช่ 'customer' 'employees', 'QueueType', ], }); } // ✅ Fetch a single Queue by ID async findOne(id: number) { const queue = await this.queueRepository.findOne({ where: { QueueID: id }, relations: [ 'machine', 'page', 'order', 'order.customer', // 👈 ดึง customer ผ่าน order 'employees', 'QueueType', // เพิ่มอันนี้ด้วยถ้าอยากได้ type มาด้วยนะ ], }); if (!queue) { throw new NotFoundException(`Queue with ID ${id} not found`); } return queue; } // ✅ Update Queue async update(id: number, updateQueueDto: UpdateQueueDto) { try { console.log('🔄 Updating Queue ID:', id, updateQueueDto); const { MachineID, PageID, OrderID, EmployeeIds, startTime, finishTime, ...queueData } = updateQueueDto; // 🔍 ค้นหา Queue ที่จะอัปเดต const existingQueue = await this.queueRepository.findOne({ where: { QueueID: id }, relations: ['machine', 'page', 'order', 'employees'], }); if (!existingQueue) { throw new NotFoundException(`Queue with ID ${id} not found`); } // 🔍 Fetch entities if IDs are provided const machine = MachineID ? await this.machineRepository.findOne({ where: { MachineID } }) : existingQueue.machine; if (MachineID && !machine) throw new NotFoundException(`Machine with ID ${MachineID} not found`); const page = PageID ? await this.pageRepository.findOne({ where: { PageID } }) : existingQueue.page; if (PageID && !page) throw new NotFoundException(`Page with ID ${PageID} not found`); const order = OrderID ? await this.orderRepository.findOne({ where: { OrderID } }) : existingQueue.order; if (OrderID && !order) throw new NotFoundException(`Order with ID ${OrderID} not found`); // 🔍 ค้นหา Employees และคำนวณการเชื่อมโยง let employees = existingQueue.employees; if (EmployeeIds) { const fetchedEmployees = await this.employeeRepository.findByIds(EmployeeIds); if (fetchedEmployees.length !== EmployeeIds.length) { throw new NotFoundException(`One or more employees not found`); } employees = fetchedEmployees; } // ✅ Convert timestamps from string to Date const updateData: Partial<Queue> = { ...queueData, machine, page, order, employees, startTime: startTime ? new Date(startTime) : existingQueue.startTime, finishTime: finishTime ? new Date(finishTime) : existingQueue.finishTime, }; // ✅ ใช้ `save()` แทน `update()` เพื่อรองรับ Many-to-Many (employees) const updatedQueue = await this.queueRepository.save({ ...existingQueue, ...updateData, }); console.log('✅ Queue updated successfully:', updatedQueue); return updatedQueue; } catch (error) { console.error('❌ Error in updateQueue:', error); throw new Error('Database update failed'); } } // ✅ Delete Queue async remove(id: number) { const queue = await this.queueRepository.findOne({ where: { QueueID: id }, relations: ['machine', 'page', 'order', 'employees'], }); if (!queue) { throw new NotFoundException(`Queue with ID ${id} not found`); } await this.queueRepository.delete(id); return { message: 'Queue deleted successfully', deletedQueue: queue }; } async deleteNewlyCreatedQueuesFromDate( dateStr: string, ): Promise<{ deletedCount: number }> { this.logger.log(`🔄 Deleting newly created queues from date: ${dateStr}`); if (!dateStr) throw new BadRequestException('dateStr is required'); // ✅ แปลง string (dd/MM/yyyy หรือ yyyy-MM-dd) → Date object let date: Date; const parts = dateStr.includes('/') ? dateStr.trim().split('/') : dateStr.trim().split('-'); if (parts.length !== 3) { throw new BadRequestException( 'Invalid date format. Use dd/MM/yyyy or yyyy-MM-dd', ); } if (dateStr.includes('/')) { const [day, month, year] = parts.map(Number); date = new Date(year, month - 1, day); } else { const [year, month, day] = parts.map(Number); date = new Date(year, month - 1, day); } const startOfDay = new Date(date.setHours(0, 0, 0, 0)); const now = new Date(); // ✅ ค้นหา Queue ที่ตรงเงื่อนไข const queuesToDelete = await this.queueRepository.find({ where: { startTime: MoreThanOrEqual(startOfDay), createdAt: MoreThanOrEqual(startOfDay), }, }); if (queuesToDelete.length === 0) { this.logger.warn(`⚠️ No queues matched deletion criteria for ${dateStr}`); return { deletedCount: 0 }; } // ✅ Log รายละเอียด queue ที่จะลบ this.logger.log( `📌 Queues to be deleted (${queuesToDelete.length} items):`, ); queuesToDelete.forEach((queue) => { this.logger.log( `🗑️ QueueID: ${queue.QueueID}, startTime: ${queue.startTime}, createdAt: ${queue.createdAt}`, ); }); // ✅ ลบ queue await this.queueRepository.remove(queuesToDelete); this.logger.log( `✅ Successfully deleted ${queuesToDelete.length} queue(s).`, ); return { deletedCount: queuesToDelete.length }; } }