From 2a119d0d236524c729bc19431a0b0166ce2a40bb Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Thu, 18 Sep 2025 16:23:53 +0800 Subject: [PATCH] update --- .../deliveryOrder/entity/DoPickOrder.kt | 5 +- .../service/DeliveryOrderService.kt | 266 ++++++++++++------ .../web/models/DoDetailResponse.kt | 5 +- .../entity/PickOrderLineRepository.kt | 7 + .../01_create_group_enson.sql | 69 +++++ 5 files changed, 269 insertions(+), 83 deletions(-) create mode 100644 src/main/resources/db/changelog/changes/20250918_01_enson/01_create_group_enson.sql diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrder.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrder.kt index c0861ca..7db8d11 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrder.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrder.kt @@ -19,7 +19,8 @@ class DoPickOrder { @Column(name = "ticket_no", length = 50) var ticketNo: String? = null - + @Column(name = "pick_order_id") + var pickOrderId: Long? = null @Enumerated(EnumType.STRING) @Column(name = "ticket_status") var ticketStatus: DoPickOrderStatus? = null @@ -72,6 +73,7 @@ class DoPickOrder { ticketStatus: DoPickOrderStatus, truckId: Long? = null, truckDepartureTime: LocalTime? = null, + pickOrderId: Long? = null, itemId: Long? = null, shopId: Long? = null, shopPoSupplierId: Long? = null, @@ -81,6 +83,7 @@ class DoPickOrder { ) { this.storeId = storeId this.ticketNo = ticketNo + this.pickOrderId = pickOrderId this.ticketStatus = ticketStatus this.truckId = truckId this.truckDepartureTime = truckDepartureTime diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt index 78550ab..0e6dc75 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt @@ -35,6 +35,18 @@ import com.ffii.fpsms.modules.deliveryOrder.enums.DoPickOrderStatus import java.time.format.DateTimeFormatter import com.ffii.fpsms.modules.pickOrder.entity.TruckRepository import java.time.LocalTime +import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService +import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository +import com.ffii.fpsms.modules.stock.entity.StockOutRepository +import com.ffii.fpsms.modules.stock.entity.StockOutLIneRepository +import com.ffii.fpsms.modules.stock.entity.StockOut +import com.ffii.fpsms.modules.stock.entity.StockOutLine +import com.ffii.fpsms.modules.stock.web.model.StockOutStatus +import com.ffii.fpsms.modules.stock.web.model.StockOutLineStatus +import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotForPolRequest +import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository +import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository + @Service open class DeliveryOrderService( private val deliveryOrderRepository: DeliveryOrderRepository, @@ -46,7 +58,13 @@ open class DeliveryOrderService( private val userRepository: UserRepository, private val pickOrderService: PickOrderService, private val doPickOrderService: DoPickOrderService, - private val truckRepository: TruckRepository + private val truckRepository: TruckRepository, + private val pickOrderRepository: PickOrderRepository, + private val suggestedPickLotService: SuggestedPickLotService, + private val inventoryLotLineRepository: InventoryLotLineRepository, + private val stockOutRepository: StockOutRepository, + private val stockOutLineRepository: StockOutLIneRepository, + private val pickOrderLineRepository: PickOrderLineRepository ) { open fun findByM18DataLogId(m18DataLogId: Long): DeliveryOrder? { @@ -302,93 +320,179 @@ open class DeliveryOrderService( return savedDeliveryOrder } - @Transactional(rollbackFor = [Exception::class]) -open fun releaseDeliveryOrder(request: ReleaseDoRequest): MessageResponse { - println("�� DEBUG: Starting releaseDeliveryOrder for DO ID: ${request.id}, User ID: ${request.userId}") - - val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(request.id) - ?: throw NoSuchElementException("Delivery Order not found") - - println("�� DEBUG: Found delivery order - ID: ${deliveryOrder.id}, Shop: ${deliveryOrder.shop?.code}, Status: ${deliveryOrder.status}") - - deliveryOrder.apply { - status = DeliveryOrderStatus.PENDING - } - deliveryOrderRepository.save(deliveryOrder) + open fun releaseDeliveryOrder(request: ReleaseDoRequest): MessageResponse { + println("�� DEBUG: Starting releaseDeliveryOrder for DO ID: ${request.id}, User ID: ${request.userId}") + + val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(request.id) + ?: throw NoSuchElementException("Delivery Order not found") + + println("�� DEBUG: Found delivery order - ID: ${deliveryOrder.id}, Shop: ${deliveryOrder.shop?.code}, Status: ${deliveryOrder.status}") + + deliveryOrder.apply { + status = DeliveryOrderStatus.PENDING + } + deliveryOrderRepository.save(deliveryOrder) - val pols = deliveryOrder.deliveryOrderLines.map { - SavePickOrderLineRequest( - itemId = it.item?.id, - qty = it.qty ?: BigDecimal.ZERO, - uomId = it.uom?.id, + val pols = deliveryOrder.deliveryOrderLines.map { + SavePickOrderLineRequest( + itemId = it.item?.id, + qty = it.qty ?: BigDecimal.ZERO, + uomId = it.uom?.id, + ) + } + val po = SavePickOrderRequest( + doId = deliveryOrder.id, + type = PickOrderType.DELIVERY_ORDER, + targetDate = deliveryOrder.estimatedArrivalDate?.toLocalDate() ?: LocalDate.now(), + pickOrderLine = pols ) - } - val po = SavePickOrderRequest( - doId = deliveryOrder.id, - type = PickOrderType.DELIVERY_ORDER, - targetDate = deliveryOrder.estimatedArrivalDate?.toLocalDate() ?: LocalDate.now(), - pickOrderLine = pols - ) - - val createdPickOrder = pickOrderService.create(po) - println("🔍 DEBUG: Created pick order - ID: ${createdPickOrder.id}") - - // ✅ CREATE do_pick_order_record entries - val targetDate = deliveryOrder.estimatedArrivalDate?.toLocalDate() ?: LocalDate.now() - val datePrefix = targetDate.format(DateTimeFormatter.ofPattern("ddMMyy")) - - println("�� DEBUG: Target date: $targetDate, Date prefix: $datePrefix") - - // Get next ticket number for this date - val nextTicketNumber = doPickOrderService.getNextTicketNumber(datePrefix) - println("�� DEBUG: Next ticket number: $nextTicketNumber") - - // ✅ Find truck by shop ID with earliest departure time - val truck = deliveryOrder.shop?.id?.let { shopId -> - println("�� DEBUG: Looking for truck with shop ID: $shopId") - val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId) - println("🔍 DEBUG: Found ${trucks.size} trucks for shop $shopId") - trucks.forEach { t -> - println("🔍 DEBUG: Truck ID: ${t.id}, DepartureTime: ${t.departureTime}") + + val createdPickOrder = pickOrderService.create(po) + println("🔍 DEBUG: Created pick order - ID: ${createdPickOrder.id}") + + val consoCode = pickOrderService.assignConsoCode() + val pickOrderEntity = pickOrderRepository.findById(createdPickOrder.id!!).orElse(null) + + if (pickOrderEntity != null) { + pickOrderEntity.consoCode = consoCode + pickOrderEntity.status = com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus.RELEASED + pickOrderRepository.saveAndFlush(pickOrderEntity) + println("�� DEBUG: Assigned consoCode $consoCode to pick order ${createdPickOrder.id}") + + // ✅ Debug: Check pick order lines + println("�� DEBUG: Pick order has ${pickOrderEntity.pickOrderLines?.size ?: 0} pick order lines") + pickOrderEntity.pickOrderLines?.forEach { line -> + println("🔍 DEBUG: Pick order line - Item ID: ${line.item?.id}, Qty: ${line.qty}") + } + val lines = pickOrderLineRepository.findAllByPickOrderId(pickOrderEntity.id!!) + println("🔍 DEBUG: Loaded ${lines.size} pick order lines from DB") + if (lines.isEmpty()) { + println("⚠️ No pick order lines found; suggestions will be empty") + } + // ✅ Create suggested pick lots and hold inventory (like normal release) + println("🔍 DEBUG: About to call suggestionForPickOrderLines for pick order ${pickOrderEntity.id}") + val suggestions = suggestedPickLotService.suggestionForPickOrderLines( + SuggestedPickLotForPolRequest(pickOrderLines = lines) + ) + println("🔍 DEBUG: Got ${suggestions.suggestedList.size} suggested pick lots") + + if (suggestions.suggestedList.isEmpty()) { + println("⚠️ WARNING: No suggested pick lots generated - this might be due to no inventory available") + } + + val saveSuggestedPickLots = suggestedPickLotService.saveAll(suggestions.suggestedList) + println("🔍 DEBUG: Saved ${saveSuggestedPickLots.size} suggested pick lots") + + + // ✅ Hold inventory quantities + val inventoryLotLines = inventoryLotLineRepository.findAllByIdIn( + saveSuggestedPickLots.mapNotNull { it.suggestedLotLine?.id } + ) + + saveSuggestedPickLots.forEach { lot -> + if (lot.suggestedLotLine != null && lot.suggestedLotLine?.id != null && lot.suggestedLotLine!!.id!! > 0) { + val lineIndex = inventoryLotLines.indexOf(lot.suggestedLotLine) + if (lineIndex >= 0) { + inventoryLotLines[lineIndex].holdQty = + (inventoryLotLines[lineIndex].holdQty ?: BigDecimal.ZERO).plus(lot.qty ?: BigDecimal.ZERO) + } + } + } + inventoryLotLineRepository.saveAll(inventoryLotLines) + + // ✅ Create stock out record and pre-create stock out lines + val stockOut = StockOut().apply { + this.type = "job" + this.consoPickOrderCode = consoCode + this.status = StockOutStatus.PENDING.status + this.handler = request.userId + } + val savedStockOut = stockOutRepository.saveAndFlush(stockOut) + + // ✅ Pre-create stock out lines for suggested lots + saveSuggestedPickLots.forEach { lot -> + val polId = lot.pickOrderLine?.id + val illId = lot.suggestedLotLine?.id + if (polId != null && illId != null) { + val existingLines = stockOutLineRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(polId, illId) + if (existingLines.isEmpty()) { + val pickOrderLine = pickOrderLineRepository.findById(polId).orElse(null) + val inventoryLotLine = inventoryLotLineRepository.findById(illId).orElse(null) + + if (pickOrderLine != null && inventoryLotLine != null) { + val line = StockOutLine().apply { + this.stockOut = savedStockOut + this.pickOrderLine = pickOrderLine + this.inventoryLotLine = inventoryLotLine + this.item = pickOrderLine.item + this.status = StockOutLineStatus.PENDING.status + this.qty = 0.0 + } + stockOutLineRepository.save(line) + } + } + } + } } - val selectedTruck = trucks.minByOrNull { it.departureTime ?: LocalTime.MAX } - println("🔍 DEBUG: Selected truck: ID=${selectedTruck?.id}, DepartureTime=${selectedTruck?.departureTime}") - selectedTruck - } - - println("🔍 DEBUG: Processing ${deliveryOrder.deliveryOrderLines.size} delivery order lines") - - deliveryOrder.deliveryOrderLines.forEach { line -> - val storeId = if (deliveryOrder.shop?.code == "P06B") "2/F" else "4/F" - println("�� DEBUG: Processing line - Item ID: ${line.item?.id}, Store ID: $storeId") + + // ✅ CREATE do_pick_order_record entries + val targetDate = deliveryOrder.estimatedArrivalDate?.toLocalDate() ?: LocalDate.now() + val datePrefix = targetDate.format(DateTimeFormatter.ofPattern("ddMMyy")) - val doPickOrder = DoPickOrder( - storeId = storeId, - ticketNo = nextTicketNumber, - ticketStatus = DoPickOrderStatus.pending, - truckId = truck?.id, - truckDepartureTime = truck?.departureTime, - itemId = line.item?.id, - shopId = deliveryOrder.shop?.id, - shopPoSupplierId = deliveryOrder.shop?.id, - handledBy = request.userId - ) + println("�� DEBUG: Target date: $targetDate, Date prefix: $datePrefix") - println("�� DEBUG: Creating DoPickOrder - Store: $storeId, Ticket: $nextTicketNumber, Truck: ${truck?.id}") + // Get next ticket number for this date + val nextTicketNumber = doPickOrderService.getNextTicketNumber(datePrefix) + println("🔍 DEBUG: Next ticket number: $nextTicketNumber") - val savedDoPickOrder = doPickOrderService.save(doPickOrder) - println("🔍 DEBUG: Saved DoPickOrder - ID: ${savedDoPickOrder.id}") + // ✅ Find truck by shop ID with earliest departure time + val truck = deliveryOrder.shop?.id?.let { shopId -> + println("�� DEBUG: Looking for truck with shop ID: $shopId") + val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId) + println("🔍 DEBUG: Found ${trucks.size} trucks for shop $shopId") + trucks.forEach { t -> + println("🔍 DEBUG: Truck ID: ${t.id}, DepartureTime: ${t.departureTime}") + } + val selectedTruck = trucks.minByOrNull { it.departureTime ?: LocalTime.MAX } + println("🔍 DEBUG: Selected truck: ID=${selectedTruck?.id}, DepartureTime=${selectedTruck?.departureTime}") + selectedTruck + } + + println("🔍 DEBUG: Processing ${deliveryOrder.deliveryOrderLines.size} delivery order lines") + + deliveryOrder.deliveryOrderLines.forEach { line -> + val storeId = if (deliveryOrder.shop?.code == "P06B") "2/F" else "4/F" + println("�� DEBUG: Processing line - Item ID: ${line.item?.id}, Store ID: $storeId") + + val doPickOrder = DoPickOrder( + storeId = storeId, + ticketNo = nextTicketNumber, + ticketStatus = DoPickOrderStatus.pending, + truckId = truck?.id, + pickOrderId = createdPickOrder.id, + truckDepartureTime = truck?.departureTime, + itemId = line.item?.id, + shopId = deliveryOrder.shop?.id, + shopPoSupplierId = deliveryOrder.shop?.id, + handledBy = request.userId + ) + + println("�� DEBUG: Creating DoPickOrder - Store: $storeId, Ticket: $nextTicketNumber, Truck: ${truck?.id}") + + val savedDoPickOrder = doPickOrderService.save(doPickOrder) + println("🔍 DEBUG: Saved DoPickOrder - ID: ${savedDoPickOrder.id}") + } + + return MessageResponse( + id = deliveryOrder.id, + code = deliveryOrder.code, + name = deliveryOrder.shop?.name, + type = null, + message = null, + errorPosition = null, + entity = mapOf("status" to deliveryOrder.status?.value) + ) } - return MessageResponse( - id = deliveryOrder.id, - code = deliveryOrder.code, - name = deliveryOrder.shop?.name, - type = null, - message = null, - errorPosition = null, - entity = mapOf("status" to deliveryOrder.status?.value) - ) -} } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/DoDetailResponse.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/DoDetailResponse.kt index b538d43..04942b4 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/DoDetailResponse.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/DoDetailResponse.kt @@ -1,15 +1,18 @@ package com.ffii.fpsms.modules.deliveryOrder.web.models import java.time.LocalDateTime - +import com.fasterxml.jackson.annotation.JsonFormat data class DoDetailResponse( val id: Long, val code: String, val supplierCode: String?, val shopCode: String?, val currencyCode: String?, + @JsonFormat(pattern = "yyyy-MM-dd") val orderDate: LocalDateTime?, + @JsonFormat(pattern = "yyyy-MM-dd") val estimatedArrivalDate: LocalDateTime?, + @JsonFormat(pattern = "yyyy-MM-dd") val completeDate: LocalDateTime?, val status: String?, val deliveryOrderLines: List diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLineRepository.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLineRepository.kt index 0b7d533..3554e5c 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLineRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLineRepository.kt @@ -14,4 +14,11 @@ interface PickOrderLineRepository : AbstractRepository { AND pol.pickOrder.deleted = false """) fun findAllPickOrdersByItemId(@Param("itemId") itemId: Long): List + + +@Query(""" + SELECT pol FROM PickOrderLine pol + WHERE pol.pickOrder.id = :pickOrderId AND pol.deleted = false +""") +fun findAllByPickOrderId(@Param("pickOrderId") pickOrderId: Long): List } \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20250918_01_enson/01_create_group_enson.sql b/src/main/resources/db/changelog/changes/20250918_01_enson/01_create_group_enson.sql new file mode 100644 index 0000000..5f662b1 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250918_01_enson/01_create_group_enson.sql @@ -0,0 +1,69 @@ +--liquibase formatted sql + +--changeset enson:update + +CREATE TABLE `do_pick_order` ( + `id` int NOT NULL AUTO_INCREMENT, + `store_id` varchar(10) NOT NULL, + `ticket_no` varchar(50) NOT NULL, + `ticket_status` enum('pending','completed') DEFAULT 'pending', + `pick_order_id` int NOT NULL, + `truck_id` int NOT NULL, + `truck_departure_time` TIME NOT NULL, + `item_id` int NOT NULL, + `shop_id` int DEFAULT NULL, + `shop_po_supplier_id` int NOT NULL, + `handled_by` int NOT NULL, + `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` varchar(30) DEFAULT NULL, + `version` int NOT NULL DEFAULT '0', + `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` varchar(30) DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `IDX_DO_PICK_ORDER_PICK_ORDER` (`pick_order_id`), + KEY `IDX_DO_PICK_ORDER_TRUCK` (`truck_id`), + KEY `IDX_DO_PICK_ORDER_ITEM` (`item_id`), + KEY `IDX_DO_PICK_ORDER_SHOP` (`shop_id`), + KEY `IDX_DO_PICK_ORDER_SHOP_SUPP` (`shop_po_supplier_id`), + KEY `IDX_DO_PICK_ORDER_HANDLED_BY` (`handled_by`), + CONSTRAINT `FK_DO_PICK_ORDER_ON_PICK_ORDER` FOREIGN KEY (`pick_order_id`) REFERENCES `pick_order` (`id`), + CONSTRAINT `FK_DO_PICK_ORDER_ON_HANDLED_BY` FOREIGN KEY (`handled_by`) REFERENCES `user` (`id`), + CONSTRAINT `FK_DO_PICK_ORDER_ON_ITEM` FOREIGN KEY (`item_id`) REFERENCES `items` (`id`), + CONSTRAINT `FK_DO_PICK_ORDER_ON_SHOP` FOREIGN KEY (`shop_id`) REFERENCES `shop` (`id`), + CONSTRAINT `FK_DO_PICK_ORDER_ON_SHOP_PO_SUPPLIER` FOREIGN KEY (`shop_po_supplier_id`) REFERENCES `shop` (`id`), + CONSTRAINT `FK_DO_PICK_ORDER_ON_TRUCK` FOREIGN KEY (`truck_id`) REFERENCES `Truck` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `do_pick_order_record` ( + `id` int NOT NULL AUTO_INCREMENT, + `store_id` varchar(10) NOT NULL, + `ticket_no` varchar(50) NOT NULL, + `ticket_status` enum('pending','completed') DEFAULT 'pending', + `pick_order_id` int NOT NULL, + `truck_id` int NOT NULL, + `truck_departure_time` TIME NOT NULL, + `item_id` int NOT NULL, + `shop_id` int DEFAULT NULL, + `shop_po_supplier_id` int NOT NULL, + `handled_by` int NOT NULL, + `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` varchar(30) DEFAULT NULL, + `version` int NOT NULL DEFAULT '0', + `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` varchar(30) DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `IDX_DO_PICK_ORDER_REC_PICK_ORDER` (`pick_order_id`), + KEY `IDX_DO_PICK_ORDER_REC_TRUCK` (`truck_id`), + KEY `IDX_DO_PICK_ORDER_REC_ITEM` (`item_id`), + KEY `IDX_DO_PICK_ORDER_REC_SHOP` (`shop_id`), + KEY `IDX_DO_PICK_ORDER_REC_SHOP_SUPP` (`shop_po_supplier_id`), + KEY `IDX_DO_PICK_ORDER_REC_HANDLED_BY` (`handled_by`), + CONSTRAINT `FK_DO_PICK_ORDER_REC_ON_PICK_ORDER` FOREIGN KEY (`pick_order_id`) REFERENCES `pick_order` (`id`), + CONSTRAINT `FK_DO_PICK_ORDER_REC_ON_HANDLED_BY` FOREIGN KEY (`handled_by`) REFERENCES `user` (`id`), + CONSTRAINT `FK_DO_PICK_ORDER_REC_ON_ITEM` FOREIGN KEY (`item_id`) REFERENCES `items` (`id`), + CONSTRAINT `FK_DO_PICK_ORDER_REC_ON_SHOP` FOREIGN KEY (`shop_id`) REFERENCES `shop` (`id`), + CONSTRAINT `FK_DO_PICK_ORDER_REC_ON_SHOP_PO_SUPPLIER` FOREIGN KEY (`shop_po_supplier_id`) REFERENCES `shop` (`id`), + CONSTRAINT `FK_DO_PICK_ORDER_REC_ON_TRUCK` FOREIGN KEY (`truck_id`) REFERENCES `Truck` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; \ No newline at end of file