|
|
|
@@ -887,43 +887,114 @@ return MessageResponse( |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Workbench Etra view: all `delivery_order_pick_order` with `releaseType` = isExtra (case-insensitive), |
|
|
|
* for one [requiredDeliveryDate], grouped by shop then by truck / time / loading sequence. |
|
|
|
* FP-MTMS Version Checklist | Functions Ref. No. 37 | v1.0.0 | 2026-07-27 |
|
|
|
* Workbench Etra view: `isExtra` / `isExtrabatch` / `isExtrasingle` tickets for one day, |
|
|
|
* grouped by shop then truck / time / loading sequence. |
|
|
|
* Truck X (`車線-X`, DB `storeId` null) is included and split by supplier preferred floor |
|
|
|
* into display `storeId` `2/F`/`4/F` (response only; not written to DB). |
|
|
|
*/ |
|
|
|
open fun getWorkbenchEtraLaneSummary(requiredDate: LocalDate?): List<WorkbenchEtraShopLaneGroup> { |
|
|
|
val targetDate = requiredDate ?: LocalDate.now() |
|
|
|
val defaultTruck = truckRepository.findById(5577L).orElse(null) |
|
|
|
val defaultTruckLaneCode = defaultTruck?.truckLanceCode ?: "" |
|
|
|
val defaultTruckLaneCode = (defaultTruck?.truckLanceCode ?: "車線-X").trim() |
|
|
|
val (_, s4f) = doFloorSupplierSettingsService.loadDoFloorSupplierLists() |
|
|
|
val in4 = s4f.joinToString(", ") { "'" + it.replace("'", "''") + "'" } |
|
|
|
.ifEmpty { "'__NO_4F_SUPPLIER__'" } |
|
|
|
val floorExpr = "CASE WHEN s.code IN ($in4) THEN '4F' ELSE '2F' END" |
|
|
|
val etraReleaseFilter = WorkbenchReleaseTypeSupport.etraFamilySql("dop.releaseType") |
|
|
|
|
|
|
|
val sql = """ |
|
|
|
SELECT |
|
|
|
dop.shopCode AS shopCode, |
|
|
|
dop.shopName AS shopName, |
|
|
|
dop.storeId AS storeId, |
|
|
|
dop.truckDepartureTime AS truckDepartureTime, |
|
|
|
dop.truckLanceCode AS truckLanceCode, |
|
|
|
dop.loadingSequence AS loadingSequence, |
|
|
|
COUNT(DISTINCT dop.id) AS total_cnt, |
|
|
|
SUM(CASE WHEN dop.handledBy IS NULL THEN 1 ELSE 0 END) AS unassigned_cnt, |
|
|
|
base.shopCode AS shopCode, |
|
|
|
base.shopName AS shopName, |
|
|
|
base.displayStoreId AS storeId, |
|
|
|
base.truckDepartureTime AS truckDepartureTime, |
|
|
|
base.truckLanceCode AS truckLanceCode, |
|
|
|
base.loadingSequence AS loadingSequence, |
|
|
|
COUNT(DISTINCT base.id) AS total_cnt, |
|
|
|
SUM(CASE WHEN base.handledBy IS NULL THEN 1 ELSE 0 END) AS unassigned_cnt, |
|
|
|
GROUP_CONCAT( |
|
|
|
DISTINCT NULLIF(TRIM(dop.handlerName), '') |
|
|
|
ORDER BY dop.handlerName |
|
|
|
DISTINCT NULLIF(TRIM(base.handlerName), '') |
|
|
|
ORDER BY base.handlerName |
|
|
|
SEPARATOR ', ' |
|
|
|
) AS handler_names |
|
|
|
FROM fpsmsdb.delivery_order_pick_order dop |
|
|
|
WHERE dop.deleted = 0 |
|
|
|
AND LOWER(COALESCE(dop.releaseType, '')) = 'isExtra' |
|
|
|
AND dop.requiredDeliveryDate = :requiredDate |
|
|
|
AND dop.ticketStatus IN ('pending', 'released') |
|
|
|
AND EXISTS ( |
|
|
|
SELECT 1 FROM fpsmsdb.pick_order po |
|
|
|
WHERE po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0 |
|
|
|
) |
|
|
|
GROUP BY dop.shopCode, dop.shopName, dop.storeId, dop.truckDepartureTime, dop.truckLanceCode, dop.loadingSequence |
|
|
|
FROM ( |
|
|
|
SELECT |
|
|
|
dop.id AS id, |
|
|
|
dop.shopCode AS shopCode, |
|
|
|
dop.shopName AS shopName, |
|
|
|
dop.handledBy AS handledBy, |
|
|
|
dop.handlerName AS handlerName, |
|
|
|
dop.truckDepartureTime AS truckDepartureTime, |
|
|
|
dop.truckLanceCode AS truckLanceCode, |
|
|
|
dop.loadingSequence AS loadingSequence, |
|
|
|
CASE |
|
|
|
WHEN (dop.storeId IS NULL OR TRIM(dop.storeId) = '') |
|
|
|
AND TRIM(dop.truckLanceCode) = :defaultTruckLane |
|
|
|
THEN CASE |
|
|
|
WHEN EXISTS ( |
|
|
|
SELECT 1 |
|
|
|
FROM fpsmsdb.pick_order po |
|
|
|
INNER JOIN fpsmsdb.delivery_order d ON d.id = po.doId AND d.deleted = 0 |
|
|
|
LEFT JOIN fpsmsdb.shop s ON s.id = d.supplierId AND s.deleted = 0 |
|
|
|
WHERE po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0 |
|
|
|
AND ($floorExpr) = '4F' |
|
|
|
) |
|
|
|
AND NOT EXISTS ( |
|
|
|
SELECT 1 |
|
|
|
FROM fpsmsdb.pick_order po2 |
|
|
|
INNER JOIN fpsmsdb.delivery_order d2 ON d2.id = po2.doId AND d2.deleted = 0 |
|
|
|
LEFT JOIN fpsmsdb.shop s2 ON s2.id = d2.supplierId AND s2.deleted = 0 |
|
|
|
WHERE po2.deliveryOrderPickOrderId = dop.id AND po2.deleted = 0 |
|
|
|
AND (CASE WHEN s2.code IN ($in4) THEN '4F' ELSE '2F' END) <> '4F' |
|
|
|
) |
|
|
|
THEN '4/F' |
|
|
|
WHEN EXISTS ( |
|
|
|
SELECT 1 |
|
|
|
FROM fpsmsdb.pick_order po |
|
|
|
INNER JOIN fpsmsdb.delivery_order d ON d.id = po.doId AND d.deleted = 0 |
|
|
|
LEFT JOIN fpsmsdb.shop s ON s.id = d.supplierId AND s.deleted = 0 |
|
|
|
WHERE po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0 |
|
|
|
AND ($floorExpr) = '2F' |
|
|
|
) |
|
|
|
AND NOT EXISTS ( |
|
|
|
SELECT 1 |
|
|
|
FROM fpsmsdb.pick_order po2 |
|
|
|
INNER JOIN fpsmsdb.delivery_order d2 ON d2.id = po2.doId AND d2.deleted = 0 |
|
|
|
LEFT JOIN fpsmsdb.shop s2 ON s2.id = d2.supplierId AND s2.deleted = 0 |
|
|
|
WHERE po2.deliveryOrderPickOrderId = dop.id AND po2.deleted = 0 |
|
|
|
AND (CASE WHEN s2.code IN ($in4) THEN '4F' ELSE '2F' END) <> '2F' |
|
|
|
) |
|
|
|
THEN '2/F' |
|
|
|
ELSE NULL |
|
|
|
END |
|
|
|
ELSE dop.storeId |
|
|
|
END AS displayStoreId |
|
|
|
FROM fpsmsdb.delivery_order_pick_order dop |
|
|
|
WHERE dop.deleted = 0 |
|
|
|
$etraReleaseFilter |
|
|
|
AND dop.requiredDeliveryDate = :requiredDate |
|
|
|
AND dop.ticketStatus IN ('pending', 'released') |
|
|
|
AND EXISTS ( |
|
|
|
SELECT 1 FROM fpsmsdb.pick_order po |
|
|
|
WHERE po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0 |
|
|
|
) |
|
|
|
) base |
|
|
|
WHERE base.displayStoreId IS NOT NULL |
|
|
|
OR TRIM(COALESCE(base.truckLanceCode, '')) <> :defaultTruckLane |
|
|
|
GROUP BY |
|
|
|
base.shopCode, base.shopName, base.displayStoreId, |
|
|
|
base.truckDepartureTime, base.truckLanceCode, base.loadingSequence |
|
|
|
""".trimIndent() |
|
|
|
|
|
|
|
val rawRows: List<Map<String, Any?>> = try { |
|
|
|
jdbcDao.queryForList(sql, mapOf("requiredDate" to targetDate)) |
|
|
|
jdbcDao.queryForList( |
|
|
|
sql, |
|
|
|
mapOf( |
|
|
|
"requiredDate" to targetDate, |
|
|
|
"defaultTruckLane" to defaultTruckLaneCode, |
|
|
|
), |
|
|
|
) |
|
|
|
} catch (e: Exception) { |
|
|
|
println("❌ getWorkbenchEtraLaneSummary: ${e.message}") |
|
|
|
emptyList() |
|
|
|
@@ -966,8 +1037,11 @@ return MessageResponse( |
|
|
|
|
|
|
|
val aggs = rawRows.mapNotNull { row -> |
|
|
|
val lance = cellStr(row, "truckLanceCode") ?: return@mapNotNull null |
|
|
|
if (lance == defaultTruckLaneCode) return@mapNotNull null |
|
|
|
val storeIdCol = cellStr(row, "storeId") |
|
|
|
// Truck X must have derived display floor (2/F or 4/F); drop mixed/unknown. |
|
|
|
if (lance == defaultTruckLaneCode && storeIdCol.isNullOrBlank()) { |
|
|
|
return@mapNotNull null |
|
|
|
} |
|
|
|
val ttKey = row.keys.find { it.equals("truckDepartureTime", true) } |
|
|
|
val ttVal = ttKey?.let { row[it] } |
|
|
|
val sortTime = when (ttVal) { |
|
|
|
@@ -1001,13 +1075,14 @@ return MessageResponse( |
|
|
|
} |
|
|
|
|
|
|
|
return byShop.entries |
|
|
|
.map { (key, group) -> |
|
|
|
.map { (_, group) -> |
|
|
|
val head = group.first() |
|
|
|
val lanes = group |
|
|
|
.sortedWith( |
|
|
|
compareBy<EtraAgg> { it.sortTime } |
|
|
|
.thenBy { it.lance } |
|
|
|
.thenBy { it.loadingSequence ?: 999 } |
|
|
|
.thenBy { it.storeId ?: "" } |
|
|
|
) |
|
|
|
.map { |
|
|
|
val is4F = it.storeId?.replace("/", "")?.trim()?.equals("4F", ignoreCase = true) == true |
|
|
|
@@ -1052,7 +1127,7 @@ return MessageResponse( |
|
|
|
/** |
|
|
|
* @param requiredDeliveryDate when null, uses [LocalDate.now] (calendar today). |
|
|
|
* When set, filters `dop.requiredDeliveryDate = :targetDate` (workbench date picker / select day). |
|
|
|
* @param releaseTypeFilter when `isExtra` (case-insensitive), only `delivery_order_pick_order.releaseType = isExtra` rows. |
|
|
|
* @param releaseTypeFilter when `isExtra`/`etra`/`etraFamily`, filter Etra family (`isExtra`/`isExtrabatch`/`isExtrasingle`). |
|
|
|
* @param floor optional `2F`/`4F`: for Truck X (null storeId) filter by DO supplier preferred floor. |
|
|
|
*/ |
|
|
|
open fun findWorkbenchReleasedDeliveryOrderPickOrdersForSelectionToday( |
|
|
|
@@ -1697,6 +1772,7 @@ return MessageResponse( |
|
|
|
""".trimIndent() |
|
|
|
} |
|
|
|
|
|
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 37 | v1.0.0 | 2026-07-27 */ |
|
|
|
private fun queryWorkbenchReleasedDopoList( |
|
|
|
shopName: String?, |
|
|
|
storeId: String?, |
|
|
|
@@ -1756,8 +1832,9 @@ return MessageResponse( |
|
|
|
params["shopPat"] = "%${shopName.trim()}%" |
|
|
|
} |
|
|
|
val rtNorm = releaseTypeFilter?.trim()?.lowercase().orEmpty() |
|
|
|
if (rtNorm == "isExtra") { |
|
|
|
sqlBuilder.append(" AND LOWER(COALESCE(dop.releaseType, '')) = 'isExtra' ") |
|
|
|
if (rtNorm == "isextra" || rtNorm == "etra" || rtNorm == "etrafamily") { |
|
|
|
// Etra modal: standalone isExtra + merged isExtrabatch / isExtrasingle |
|
|
|
sqlBuilder.append(WorkbenchReleaseTypeSupport.etraFamilySql("dop.releaseType")) |
|
|
|
} |
|
|
|
// Optional floor: used for Truck X (null storeId) display split; does not write storeId. |
|
|
|
val floorClause = truckXPreferredFloorExistsSql(floor) |
|
|
|
|