# Conflicts: # src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.ktmaster
| @@ -0,0 +1,97 @@ | |||||
| package com.ffii.fpsms.modules.deliveryOrder.entity | |||||
| import jakarta.persistence.* | |||||
| import java.time.LocalDateTime | |||||
| import java.time.LocalTime | |||||
| import com.ffii.fpsms.modules.deliveryOrder.enums.DoPickOrderStatus | |||||
| import org.hibernate.annotations.CreationTimestamp | |||||
| import org.hibernate.annotations.UpdateTimestamp | |||||
| @Entity | |||||
| @Table(name = "do_pick_order_record") | |||||
| class DoPickOrderRecord { | |||||
| @Id | |||||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | |||||
| var id: Long? = null | |||||
| @Column(name = "store_id", length = 10) | |||||
| var storeId: String? = null | |||||
| @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 | |||||
| @Column(name = "truck_id") | |||||
| var truckId: Long? = null | |||||
| @Column(name = "truck_departure_time") | |||||
| var truckDepartureTime: LocalTime? = null | |||||
| @Column(name = "item_id") | |||||
| var itemId: Long? = null | |||||
| @Column(name = "shop_id") | |||||
| var shopId: Long? = null | |||||
| @Column(name = "shop_po_supplier_id") | |||||
| var shopPoSupplierId: Long? = null | |||||
| @Column(name = "handled_by") | |||||
| var handledBy: Long? = null | |||||
| @CreationTimestamp | |||||
| @Column(name = "created") | |||||
| var created: LocalDateTime? = null | |||||
| @Column(name = "createdBy", length = 30) | |||||
| var createdBy: String? = null | |||||
| @Version | |||||
| var version: Int = 0 | |||||
| @UpdateTimestamp | |||||
| @Column(name = "modified") | |||||
| var modified: LocalDateTime? = null | |||||
| @Column(name = "modifiedBy", length = 30) | |||||
| var modifiedBy: String? = null | |||||
| @Column(name = "deleted") | |||||
| var deleted: Boolean = false | |||||
| // Default constructor for Hibernate | |||||
| constructor() | |||||
| // Constructor for creating new instances | |||||
| constructor( | |||||
| storeId: String, | |||||
| ticketNo: String, | |||||
| ticketStatus: DoPickOrderStatus, | |||||
| truckId: Long? = null, | |||||
| truckDepartureTime: LocalTime? = null, | |||||
| pickOrderId: Long? = null, | |||||
| itemId: Long? = null, | |||||
| shopId: Long? = null, | |||||
| shopPoSupplierId: Long? = null, | |||||
| handledBy: Long? = null, | |||||
| createdBy: String? = null, | |||||
| modifiedBy: String? = null | |||||
| ) { | |||||
| this.storeId = storeId | |||||
| this.ticketNo = ticketNo | |||||
| this.pickOrderId = pickOrderId | |||||
| this.ticketStatus = ticketStatus | |||||
| this.truckId = truckId | |||||
| this.truckDepartureTime = truckDepartureTime | |||||
| this.itemId = itemId | |||||
| this.shopId = shopId | |||||
| this.shopPoSupplierId = shopPoSupplierId | |||||
| this.handledBy = handledBy | |||||
| this.createdBy = createdBy | |||||
| this.modifiedBy = modifiedBy | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,18 @@ | |||||
| package com.ffii.fpsms.modules.deliveryOrder.entity | |||||
| import com.ffii.core.support.AbstractRepository | |||||
| import com.ffii.fpsms.modules.deliveryOrder.entity.models.DeliveryOrderInfo | |||||
| import com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderStatus | |||||
| import com.ffii.fpsms.modules.deliveryOrder.enums.DoPickOrderStatus | |||||
| import com.ffii.fpsms.modules.master.entity.projections.SearchId | |||||
| import org.springframework.data.jpa.repository.JpaRepository | |||||
| import org.springframework.data.jpa.repository.Query | |||||
| import org.springframework.stereotype.Repository | |||||
| import java.io.Serializable | |||||
| import java.time.LocalDateTime | |||||
| @Repository | |||||
| interface DoPickOrderRecordRepository : JpaRepository<DoPickOrderRecord, Long> { | |||||
| fun findByPickOrderId(pickOrderId: Long): List<DoPickOrderRecord> | |||||
| fun findByTicketNoStartingWith(ticketPrefix: String): List<DoPickOrderRecord> | |||||
| } | |||||
| @@ -2,5 +2,6 @@ package com.ffii.fpsms.modules.deliveryOrder.enums | |||||
| enum class DoPickOrderStatus(val value: String) { | enum class DoPickOrderStatus(val value: String) { | ||||
| pending("pending"), | pending("pending"), | ||||
| released("released"), | |||||
| completed("completed") | completed("completed") | ||||
| } | } | ||||
| @@ -57,6 +57,9 @@ import org.springframework.core.io.ClassPathResource | |||||
| import java.io.File | import java.io.File | ||||
| import java.io.FileNotFoundException | import java.io.FileNotFoundException | ||||
| import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecord | |||||
| import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecordRepository | |||||
| @Service | @Service | ||||
| open class DeliveryOrderService( | open class DeliveryOrderService( | ||||
| private val deliveryOrderRepository: DeliveryOrderRepository, | private val deliveryOrderRepository: DeliveryOrderRepository, | ||||
| @@ -75,7 +78,8 @@ open class DeliveryOrderService( | |||||
| private val stockOutRepository: StockOutRepository, | private val stockOutRepository: StockOutRepository, | ||||
| private val stockOutLineRepository: StockOutLIneRepository, | private val stockOutLineRepository: StockOutLIneRepository, | ||||
| private val pickOrderLineRepository: PickOrderLineRepository, | private val pickOrderLineRepository: PickOrderLineRepository, | ||||
| private val printerService: PrinterService | |||||
| private val printerService: PrinterService, | |||||
| private val doPickOrderRecordRepository: DoPickOrderRecordRepository | |||||
| ) { | ) { | ||||
| open fun findByM18DataLogId(m18DataLogId: Long): DeliveryOrder? { | open fun findByM18DataLogId(m18DataLogId: Long): DeliveryOrder? { | ||||
| @@ -452,14 +456,9 @@ open class DeliveryOrderService( | |||||
| val datePrefix = targetDate.format(DateTimeFormatter.ofPattern("ddMMyy")) | val datePrefix = targetDate.format(DateTimeFormatter.ofPattern("ddMMyy")) | ||||
| println("�� DEBUG: Target date: $targetDate, Date prefix: $datePrefix") | 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 -> | val truck = deliveryOrder.shop?.id?.let { shopId -> | ||||
| println("�� DEBUG: Looking for truck with shop ID: $shopId") | |||||
| println("🔍 DEBUG: Looking for truck with shop ID: $shopId") | |||||
| val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId) | val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId) | ||||
| println("🔍 DEBUG: Found ${trucks.size} trucks for shop $shopId") | println("🔍 DEBUG: Found ${trucks.size} trucks for shop $shopId") | ||||
| trucks.forEach { t -> | trucks.forEach { t -> | ||||
| @@ -472,29 +471,59 @@ open class DeliveryOrderService( | |||||
| println("🔍 DEBUG: Processing ${deliveryOrder.deliveryOrderLines.size} delivery order lines") | println("🔍 DEBUG: Processing ${deliveryOrder.deliveryOrderLines.size} delivery order lines") | ||||
| deliveryOrder.deliveryOrderLines.forEach { line -> | |||||
| val storeId = if (deliveryOrder.supplier?.code == "P06B") "4/F" else "2/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 = null | |||||
| ) | |||||
| println("�� DEBUG: Creating DoPickOrder - Store: $storeId, Ticket: $nextTicketNumber, Truck: ${truck?.id}") | |||||
| val savedDoPickOrder = doPickOrderService.save(doPickOrder) | |||||
| println("🔍 DEBUG: Saved DoPickOrder - ID: ${savedDoPickOrder.id}") | |||||
| // ✅ Group lines by store ID to get unique ticket numbers per store | |||||
| val linesByStore = deliveryOrder.deliveryOrderLines.groupBy { line -> | |||||
| if (deliveryOrder.supplier?.code == "P06B") "4/F" else "2/F" | |||||
| } | } | ||||
| linesByStore.forEach { (storeId, lines) -> | |||||
| // ✅ Get ticket number for this specific store | |||||
| val nextTicketNumber = doPickOrderService.getNextTicketNumber(datePrefix, storeId) | |||||
| println("🔍 DEBUG: Next ticket number for store $storeId: $nextTicketNumber") | |||||
| lines.forEach { line -> | |||||
| println("�� DEBUG: Processing line - Item ID: ${line.item?.id}, Store ID: $storeId") | |||||
| val doPickOrderRecord = DoPickOrderRecord( | |||||
| 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 = null | |||||
| ) | |||||
| println("�� DEBUG: Creating DoPickOrderRecord - Store: $storeId, Ticket: $nextTicketNumber, Truck: ${truck?.id}") | |||||
| val savedDoPickOrderRecord = doPickOrderRecordRepository.save(doPickOrderRecord) | |||||
| println("🔍 DEBUG: Saved DoPickOrderRecord - ID: ${savedDoPickOrderRecord.id}") | |||||
| } | |||||
| // ✅ Also create DoPickOrder records for this store | |||||
| lines.forEach { line -> | |||||
| println("�� DEBUG: Creating DoPickOrder - Store: $storeId, Ticket: $nextTicketNumber, Truck: ${truck?.id}") | |||||
| 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 = null | |||||
| ) | |||||
| val savedDoPickOrder = doPickOrderService.save(doPickOrder) | |||||
| println("🔍 DEBUG: Saved DoPickOrder - ID: ${savedDoPickOrder.id}") | |||||
| } | |||||
| } | |||||
| return MessageResponse( | return MessageResponse( | ||||
| id = deliveryOrder.id, | id = deliveryOrder.id, | ||||
| code = deliveryOrder.code, | code = deliveryOrder.code, | ||||
| @@ -33,21 +33,27 @@ import java.math.BigDecimal | |||||
| import com.ffii.fpsms.modules.master.web.models.MessageResponse | import com.ffii.fpsms.modules.master.web.models.MessageResponse | ||||
| import java.time.LocalDateTime | import java.time.LocalDateTime | ||||
| import com.ffii.fpsms.modules.deliveryOrder.web.models.AssignByStoreRequest | import com.ffii.fpsms.modules.deliveryOrder.web.models.AssignByStoreRequest | ||||
| import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecord | |||||
| import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecordRepository | |||||
| @Service | @Service | ||||
| class DoPickOrderService( | class DoPickOrderService( | ||||
| private val doPickOrderRepository: DoPickOrderRepository | |||||
| private val doPickOrderRepository: DoPickOrderRepository, | |||||
| private val doPickOrderRecordRepository: DoPickOrderRecordRepository | |||||
| ) { | ) { | ||||
| fun getNextTicketNumber(datePrefix: String): String { | |||||
| println("🔍 DEBUG: Getting next ticket number for date prefix: $datePrefix") | |||||
| fun getNextTicketNumber(datePrefix: String, storeId: String): String { | |||||
| println("🔍 DEBUG: Getting next ticket number for date prefix: $datePrefix, store: $storeId") | |||||
| try { | try { | ||||
| val todayTickets = doPickOrderRepository.findByTicketNoStartingWith("${datePrefix}_") | |||||
| println("🔍 DEBUG: Found ${todayTickets.size} existing tickets with prefix ${datePrefix}_") | |||||
| val sanitizedStoreId = storeId.replace("/", "") | |||||
| // ✅ Include store ID in the search pattern | |||||
| val searchPattern = "${datePrefix}_${sanitizedStoreId}_" | |||||
| val todayTickets = doPickOrderRepository.findByTicketNoStartingWith(searchPattern) | |||||
| println("🔍 DEBUG: Found ${todayTickets.size} existing tickets with prefix $searchPattern") | |||||
| todayTickets.forEach { ticket -> | todayTickets.forEach { ticket -> | ||||
| println("�� DEBUG: Existing ticket: ${ticket.ticketNo}, Status: ${ticket.ticketStatus}") | println("�� DEBUG: Existing ticket: ${ticket.ticketNo}, Status: ${ticket.ticketStatus}") | ||||
| } | } | ||||
| val nextNumber = (todayTickets.size + 1).toString().padStart(3, '0') | val nextNumber = (todayTickets.size + 1).toString().padStart(3, '0') | ||||
| val ticketNumber = "${datePrefix}_${nextNumber}" | |||||
| val ticketNumber = "${datePrefix}_${sanitizedStoreId}_${nextNumber}" | |||||
| println("🔍 DEBUG: Generated ticket number: $ticketNumber") | println("🔍 DEBUG: Generated ticket number: $ticketNumber") | ||||
| return ticketNumber | return ticketNumber | ||||
| } catch (e: Exception) { | } catch (e: Exception) { | ||||
| @@ -94,7 +100,52 @@ class DoPickOrderService( | |||||
| } | } | ||||
| fun updateHandledByForPickOrder(pickOrderId: Long, userId: Long): List<DoPickOrder> { | fun updateHandledByForPickOrder(pickOrderId: Long, userId: Long): List<DoPickOrder> { | ||||
| val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId) | val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId) | ||||
| doPickOrders.forEach { it.handledBy = userId } | |||||
| doPickOrders.forEach { it.handledBy = userId | |||||
| it.ticketStatus = DoPickOrderStatus.released } | |||||
| return doPickOrderRepository.saveAll(doPickOrders) | return doPickOrderRepository.saveAll(doPickOrders) | ||||
| } | } | ||||
| fun completeDoPickOrdersForPickOrder(pickOrderId: Long): List<DoPickOrder> { | |||||
| val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId) | |||||
| doPickOrders.forEach { | |||||
| it.ticketStatus = DoPickOrderStatus.completed // ✅ Update status to "completed" | |||||
| } | |||||
| return doPickOrderRepository.saveAll(doPickOrders) | |||||
| } | |||||
| // ✅ New method to remove do_pick_order records when auto-assigning by store | |||||
| fun removeDoPickOrdersForPickOrder(pickOrderId: Long): Int { | |||||
| val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId) | |||||
| if (doPickOrders.isNotEmpty()) { | |||||
| // Mark as deleted instead of physically deleting | |||||
| doPickOrders.forEach { | |||||
| it.ticketStatus = DoPickOrderStatus.completed | |||||
| it.deleted = true } | |||||
| doPickOrderRepository.saveAll(doPickOrders) | |||||
| return doPickOrders.size | |||||
| } | |||||
| return 0 | |||||
| } | |||||
| fun saveRecord(record: DoPickOrderRecord): DoPickOrderRecord { | |||||
| return doPickOrderRecordRepository.save(record) | |||||
| } | |||||
| // ✅ Add method to update DoPickOrderRecord status | |||||
| fun updateRecordHandledByForPickOrder(pickOrderId: Long, userId: Long): List<DoPickOrderRecord> { | |||||
| val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId) | |||||
| doPickOrderRecords.forEach { | |||||
| it.handledBy = userId | |||||
| it.ticketStatus = DoPickOrderStatus.released | |||||
| } | |||||
| return doPickOrderRecordRepository.saveAll(doPickOrderRecords) | |||||
| } | |||||
| // ✅ Add method to complete DoPickOrderRecord | |||||
| fun completeDoPickOrderRecordsForPickOrder(pickOrderId: Long): List<DoPickOrderRecord> { | |||||
| val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId) | |||||
| doPickOrderRecords.forEach { | |||||
| it.ticketStatus = DoPickOrderStatus.completed | |||||
| } | |||||
| return doPickOrderRecordRepository.saveAll(doPickOrderRecords) | |||||
| } | |||||
| } | } | ||||
| @@ -22,5 +22,5 @@ interface RouterRepository : AbstractRepository<Router, Long> { | |||||
| @Query("SELECT r FROM Router r WHERE r.route = :route AND r.deleted = false") | @Query("SELECT r FROM Router r WHERE r.route = :route AND r.deleted = false") | ||||
| fun findByRouteAndDeletedFalse(@Param("route") route: String): List<Router> | fun findByRouteAndDeletedFalse(@Param("route") route: String): List<Router> | ||||
| } | } | ||||
| @@ -266,4 +266,20 @@ class PickOrderController( | |||||
| fun getAllPickOrderLotsWithDetailsWithoutAutoAssign(@PathVariable userId: Long): List<Map<String, Any>> { | fun getAllPickOrderLotsWithDetailsWithoutAutoAssign(@PathVariable userId: Long): List<Map<String, Any>> { | ||||
| return pickOrderService.getAllPickOrderLotsWithDetailsWithoutAutoAssign(userId) | return pickOrderService.getAllPickOrderLotsWithDetailsWithoutAutoAssign(userId) | ||||
| } | } | ||||
| @GetMapping("/all-lots-hierarchical/{userId}") | |||||
| fun getAllPickOrderLotsHierarchical(@PathVariable userId: Long): Map<String, Any?> { | |||||
| return pickOrderService.getAllPickOrderLotsWithDetailsHierarchical(userId) | |||||
| } | |||||
| @PostMapping("/auto-assign-release-by-ticket") | |||||
| fun autoAssignAndReleasePickOrderByTicket( | |||||
| @RequestParam storeId: String, | |||||
| @RequestParam ticketNo: String, | |||||
| @RequestParam userId: Long | |||||
| ): MessageResponse { | |||||
| return pickOrderService.autoAssignAndReleasePickOrderByStoreAndTicket(storeId, ticketNo, userId) | |||||
| } | |||||
| @GetMapping("/orders-by-store/{storeId}") | |||||
| fun getPickOrdersByStore(@PathVariable storeId: String): Map<String, Any?> { | |||||
| return pickOrderService.getPickOrdersByDateAndStore(storeId) | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,7 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset enson:altertable_enson | |||||
| ALTER TABLE `fpsmsdb`.`do_pick_order` | |||||
| CHANGE COLUMN `ticket_status` `ticket_status` ENUM('pending', 'released', 'completed') NULL DEFAULT 'pending' ; | |||||
| ALTER TABLE `fpsmsdb`.`do_pick_order_record` | |||||
| CHANGE COLUMN `ticket_status` `ticket_status` ENUM('pending', 'released', 'completed') NULL DEFAULT 'pending' ; | |||||