|
|
@@ -48,9 +48,11 @@ import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderLineRecord |
|
|
import org.springframework.context.annotation.Lazy |
|
|
import org.springframework.context.annotation.Lazy |
|
|
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository |
|
|
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository |
|
|
import com.ffii.fpsms.modules.deliveryOrder.web.models.TicketReleaseTableResponse |
|
|
import com.ffii.fpsms.modules.deliveryOrder.web.models.TicketReleaseTableResponse |
|
|
|
|
|
import com.ffii.fpsms.modules.deliveryOrder.web.models.TruckScheduleDashboardResponse |
|
|
import com.ffii.fpsms.modules.deliveryOrder.web.models.SearchDeliveryOrderInfoRequest |
|
|
import com.ffii.fpsms.modules.deliveryOrder.web.models.SearchDeliveryOrderInfoRequest |
|
|
import com.ffii.core.response.RecordsRes |
|
|
import com.ffii.core.response.RecordsRes |
|
|
import org.springframework.data.domain.PageRequest |
|
|
import org.springframework.data.domain.PageRequest |
|
|
|
|
|
import java.time.temporal.ChronoUnit |
|
|
@Service |
|
|
@Service |
|
|
open class DoPickOrderService( |
|
|
open class DoPickOrderService( |
|
|
private val doPickOrderRepository: DoPickOrderRepository, |
|
|
private val doPickOrderRepository: DoPickOrderRepository, |
|
|
@@ -748,5 +750,183 @@ open class DoPickOrderService( |
|
|
return allPickOrderLines.size |
|
|
return allPickOrderLines.size |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Get truck schedule dashboard data aggregated by store, truck lane, and departure time. |
|
|
|
|
|
* Groups DoPickOrder and DoPickOrderRecord data to provide summary statistics. |
|
|
|
|
|
*/ |
|
|
|
|
|
open fun getTruckScheduleDashboard(): List<TruckScheduleDashboardResponse> { |
|
|
|
|
|
val today = LocalDate.now() |
|
|
|
|
|
|
|
|
|
|
|
// Fetch all active DoPickOrders for today |
|
|
|
|
|
val doPickOrders = doPickOrderRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( |
|
|
|
|
|
"2/F", today, listOf(DoPickOrderStatus.pending, DoPickOrderStatus.released, DoPickOrderStatus.completed) |
|
|
|
|
|
) + doPickOrderRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( |
|
|
|
|
|
"4/F", today, listOf(DoPickOrderStatus.pending, DoPickOrderStatus.released, DoPickOrderStatus.completed) |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
// Fetch all DoPickOrderRecords for today (completed records) |
|
|
|
|
|
val doPickOrderRecords = doPickOrderRecordRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( |
|
|
|
|
|
"2/F", today, listOf(DoPickOrderStatus.completed) |
|
|
|
|
|
) + doPickOrderRecordRepository.findByStoreIdAndRequiredDeliveryDateAndTicketStatusIn( |
|
|
|
|
|
"4/F", today, listOf(DoPickOrderStatus.completed) |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
// Combine both types into a unified data structure for aggregation |
|
|
|
|
|
data class TicketData( |
|
|
|
|
|
val storeId: String?, |
|
|
|
|
|
val truckId: Long?, |
|
|
|
|
|
val truckLanceCode: String?, |
|
|
|
|
|
val truckDepartureTime: java.time.LocalTime?, |
|
|
|
|
|
val shopId: Long?, |
|
|
|
|
|
val shopCode: String?, |
|
|
|
|
|
val ticketNo: String?, |
|
|
|
|
|
val ticketReleaseTime: LocalDateTime?, |
|
|
|
|
|
val ticketCompleteDateTime: LocalDateTime?, |
|
|
|
|
|
val ticketStatus: DoPickOrderStatus?, |
|
|
|
|
|
val doPickOrderId: Long?, |
|
|
|
|
|
val isRecord: Boolean |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
val allTickets = mutableListOf<TicketData>() |
|
|
|
|
|
|
|
|
|
|
|
doPickOrders.forEach { dpo -> |
|
|
|
|
|
allTickets.add(TicketData( |
|
|
|
|
|
storeId = dpo.storeId, |
|
|
|
|
|
truckId = dpo.truckId, |
|
|
|
|
|
truckLanceCode = dpo.truckLanceCode, |
|
|
|
|
|
truckDepartureTime = dpo.truckDepartureTime, |
|
|
|
|
|
shopId = dpo.shopId, |
|
|
|
|
|
shopCode = dpo.shopCode, |
|
|
|
|
|
ticketNo = dpo.ticketNo, |
|
|
|
|
|
ticketReleaseTime = dpo.ticketReleaseTime, |
|
|
|
|
|
ticketCompleteDateTime = dpo.ticketCompleteDateTime, |
|
|
|
|
|
ticketStatus = dpo.ticketStatus, |
|
|
|
|
|
doPickOrderId = dpo.id, |
|
|
|
|
|
isRecord = false |
|
|
|
|
|
)) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
doPickOrderRecords.forEach { record -> |
|
|
|
|
|
allTickets.add(TicketData( |
|
|
|
|
|
storeId = record.storeId, |
|
|
|
|
|
truckId = record.truckId, |
|
|
|
|
|
truckLanceCode = record.truckLanceCode, |
|
|
|
|
|
truckDepartureTime = record.truckDepartureTime, |
|
|
|
|
|
shopId = record.shopId, |
|
|
|
|
|
shopCode = record.shopCode, |
|
|
|
|
|
ticketNo = record.ticketNo, |
|
|
|
|
|
ticketReleaseTime = record.ticketReleaseTime, |
|
|
|
|
|
ticketCompleteDateTime = record.ticketCompleteDateTime, |
|
|
|
|
|
ticketStatus = record.ticketStatus, |
|
|
|
|
|
doPickOrderId = record.recordId, |
|
|
|
|
|
isRecord = true |
|
|
|
|
|
)) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Group by storeId, truckLanceCode, truckDepartureTime |
|
|
|
|
|
val grouped = allTickets.groupBy { |
|
|
|
|
|
Triple(it.storeId, it.truckLanceCode, it.truckDepartureTime) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return grouped.map { (key, tickets) -> |
|
|
|
|
|
val (storeId, truckLanceCode, truckDepartureTime) = key |
|
|
|
|
|
|
|
|
|
|
|
// Count distinct shops |
|
|
|
|
|
val distinctShops = tickets.mapNotNull { it.shopId ?: it.shopCode?.hashCode()?.toLong() }.distinct().size |
|
|
|
|
|
|
|
|
|
|
|
// Count distinct tickets |
|
|
|
|
|
val distinctTickets = tickets.mapNotNull { it.ticketNo }.distinct().size |
|
|
|
|
|
|
|
|
|
|
|
// Calculate total items to pick |
|
|
|
|
|
var totalItems = 0 |
|
|
|
|
|
tickets.forEach { ticket -> |
|
|
|
|
|
if (ticket.doPickOrderId != null) { |
|
|
|
|
|
if (ticket.isRecord) { |
|
|
|
|
|
totalItems += countFGItemsFromRecordById(ticket.doPickOrderId) |
|
|
|
|
|
} else { |
|
|
|
|
|
totalItems += countFGItemsById(ticket.doPickOrderId) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Count released tickets (ticketReleaseTime is not null) |
|
|
|
|
|
val releasedTickets = tickets.count { it.ticketReleaseTime != null } |
|
|
|
|
|
|
|
|
|
|
|
// Find first ticket start time (earliest ticketReleaseTime) |
|
|
|
|
|
val firstTicketStartTime = tickets |
|
|
|
|
|
.mapNotNull { it.ticketReleaseTime } |
|
|
|
|
|
.minOrNull() |
|
|
|
|
|
|
|
|
|
|
|
// Count completed tickets (ticketCompleteDateTime is not null) |
|
|
|
|
|
val completedTickets = tickets.count { it.ticketCompleteDateTime != null } |
|
|
|
|
|
|
|
|
|
|
|
// Find last ticket end time (latest ticketCompleteDateTime) |
|
|
|
|
|
val lastTicketEndTime = tickets |
|
|
|
|
|
.mapNotNull { it.ticketCompleteDateTime } |
|
|
|
|
|
.maxOrNull() |
|
|
|
|
|
|
|
|
|
|
|
// Calculate pick time taken in minutes |
|
|
|
|
|
val pickTimeTakenMinutes = if (firstTicketStartTime != null && lastTicketEndTime != null) { |
|
|
|
|
|
ChronoUnit.MINUTES.between(firstTicketStartTime, lastTicketEndTime) |
|
|
|
|
|
} else { |
|
|
|
|
|
null |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Get truck ID (use first non-null) |
|
|
|
|
|
val truckId = tickets.firstOrNull { it.truckId != null }?.truckId |
|
|
|
|
|
|
|
|
|
|
|
TruckScheduleDashboardResponse( |
|
|
|
|
|
storeId = storeId, |
|
|
|
|
|
truckId = truckId, |
|
|
|
|
|
truckLanceCode = truckLanceCode, |
|
|
|
|
|
truckDepartureTime = truckDepartureTime, |
|
|
|
|
|
numberOfShopsToServe = distinctShops, |
|
|
|
|
|
numberOfPickTickets = distinctTickets, |
|
|
|
|
|
totalItemsToPick = totalItems, |
|
|
|
|
|
numberOfTicketsReleased = releasedTickets, |
|
|
|
|
|
firstTicketStartTime = firstTicketStartTime, |
|
|
|
|
|
numberOfTicketsCompleted = completedTickets, |
|
|
|
|
|
lastTicketEndTime = lastTicketEndTime, |
|
|
|
|
|
pickTimeTakenMinutes = pickTimeTakenMinutes |
|
|
|
|
|
) |
|
|
|
|
|
}.sortedWith(compareBy({ it.storeId }, { it.truckDepartureTime })) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private fun countFGItemsById(doPickOrderId: Long): Int { |
|
|
|
|
|
val doPickOrderLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(doPickOrderId) |
|
|
|
|
|
val pickOrderIds = doPickOrderLines.mapNotNull { it.pickOrderId }.distinct() |
|
|
|
|
|
|
|
|
|
|
|
if (pickOrderIds.isEmpty()) { |
|
|
|
|
|
val doPickOrder = doPickOrderRepository.findById(doPickOrderId).orElse(null) |
|
|
|
|
|
val directPickOrderId = doPickOrder?.pickOrderId |
|
|
|
|
|
if (directPickOrderId != null) { |
|
|
|
|
|
val pickOrderLines = pickOrderLineRepository.findAllByPickOrderId(directPickOrderId) |
|
|
|
|
|
return pickOrderLines.size |
|
|
|
|
|
} |
|
|
|
|
|
return 0 |
|
|
|
|
|
} |
|
|
|
|
|
val allPickOrderLines = pickOrderIds.flatMap { pickOrderId -> |
|
|
|
|
|
pickOrderLineRepository.findAllByPickOrderId(pickOrderId) |
|
|
|
|
|
} |
|
|
|
|
|
return allPickOrderLines.size |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private fun countFGItemsFromRecordById(recordId: Long): Int { |
|
|
|
|
|
val doPickOrderLineRecords = doPickOrderLineRecordRepository.findByDoPickOrderIdAndDeletedFalse(recordId) |
|
|
|
|
|
if (doPickOrderLineRecords.isEmpty()) { |
|
|
|
|
|
return 0 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val pickOrderIds = doPickOrderLineRecords.mapNotNull { it.pickOrderId }.distinct() |
|
|
|
|
|
if (pickOrderIds.isEmpty()) { |
|
|
|
|
|
return 0 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val allPickOrderLines = pickOrderIds.flatMap { pickOrderId -> |
|
|
|
|
|
pickOrderLineRepository.findAllByPickOrderId(pickOrderId) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return allPickOrderLines.size |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
}// 类结束 |
|
|
}// 类结束 |