Просмотр исходного кода

isextra truck X ticket fix

production
CANCERYS\kw093 14 часов назад
Родитель
Сommit
b12b9a499f
4 измененных файлов: 127 добавлений и 36 удалений
  1. +7
    -3
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchDopoAssignmentService.kt
  2. +106
    -29
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchMainService.kt
  3. +13
    -3
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/WorkbenchReleaseTypeSupport.kt
  4. +1
    -1
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/DoDetailResponse.kt

+ 7
- 3
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchDopoAssignmentService.kt Просмотреть файл

@@ -95,6 +95,7 @@ open class DoWorkbenchDopoAssignmentService(
}

/**
* FP-MTMS Version Checklist | Functions Ref. No. 37 | v1.0.0 | 2026-07-27
* Same UX as [DoPickOrderAssignmentService.assignByLane] but candidates come from
* [delivery_order_pick_order] (+ unassigned [pick_order]), not [do_pick_order].
*/
@@ -145,7 +146,7 @@ open class DoWorkbenchDopoAssignmentService(
params["loadingSequence"] = request.loadingSequence
}
if (isisExtraReleaseType(request.releaseType)) {
sql.append(WorkbenchReleaseTypeSupport.legacyIsExtraSql())
sql.append(WorkbenchReleaseTypeSupport.etraFamilySql())
} else {
sql.append(WorkbenchReleaseTypeSupport.assignFilterSql(request.releaseType))
}
@@ -206,7 +207,10 @@ open class DoWorkbenchDopoAssignmentService(
)
}

/** Legacy lane assign (V1): old FG-style (no atomic conflict guard). */
/**
* FP-MTMS Version Checklist | Functions Ref. No. 37 | v1.0.0 | 2026-07-27
* Legacy lane assign (V1): old FG-style (no atomic conflict guard).
*/
@Transactional
open fun assignByLaneForWorkbenchV1(request: AssignByLaneRequest): MessageResponse {
val user = userRepository.findById(request.userId).orElse(null)
@@ -253,7 +257,7 @@ open class DoWorkbenchDopoAssignmentService(
params["loadingSequence"] = request.loadingSequence
}
if (isisExtraReleaseType(request.releaseType)) {
sql.append(WorkbenchReleaseTypeSupport.legacyIsExtraSql())
sql.append(WorkbenchReleaseTypeSupport.etraFamilySql())
} else {
sql.append(WorkbenchReleaseTypeSupport.assignFilterSql(request.releaseType))
}


+ 106
- 29
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchMainService.kt Просмотреть файл

@@ -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)


+ 13
- 3
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/WorkbenchReleaseTypeSupport.kt Просмотреть файл

@@ -2,7 +2,7 @@ package com.ffii.fpsms.modules.deliveryOrder.service

/**
* Workbench [delivery_order_pick_order.releaseType] values and SQL filters.
* Legacy `isExtra` tickets are excluded from merge; only used for legacy Etra views.
* Etra UI uses [etraFamilySql] (standalone `isExtra` + merged `isExtrabatch` / `isExtrasingle`).
*/
object WorkbenchReleaseTypeSupport {
const val BATCH = "batch"
@@ -15,20 +15,23 @@ object WorkbenchReleaseTypeSupport {

fun singleFamilyTypes(): List<String> = listOf(SINGLE, IS_EXTRA_SINGLE)

/** FP-MTMS Version Checklist | Functions Ref. No. 37 | v1.0.0 | 2026-07-27 */
fun summaryFilterSql(releaseType: String, column: String = "dop.releaseType"): String =
when (releaseType.trim().lowercase()) {
"batch" -> batchFamilySql(column)
"single" -> singleFamilySql(column)
"isextra" -> legacyIsExtraSql(column)
// Etra UI / list: standalone + merged extra family
"isextra", "etra", "etrafamily" -> etraFamilySql(column)
else -> ""
}

/** FP-MTMS Version Checklist | Functions Ref. No. 37 | v1.0.0 | 2026-07-27 */
fun assignFilterSql(releaseType: String?, column: String = "dop.releaseType"): String {
val n = releaseType?.trim()?.lowercase().orEmpty()
return when (n) {
"batch" -> batchFamilySql(column)
"single" -> singleFamilySql(column)
"isextra" -> legacyIsExtraSql(column)
"isextra", "etra", "etrafamily" -> etraFamilySql(column)
else -> ""
}
}
@@ -42,6 +45,13 @@ object WorkbenchReleaseTypeSupport {
fun legacyIsExtraSql(column: String = "dop.releaseType"): String =
" AND LOWER(COALESCE($column, '')) = 'isextra' "

/**
* FP-MTMS Version Checklist | Functions Ref. No. 37 | v1.0.0 | 2026-07-27
* Standalone `isExtra` plus merged `isExtrabatch` / `isExtrasingle` (Etra workbench).
*/
fun etraFamilySql(column: String = "dop.releaseType"): String =
" AND LOWER(COALESCE($column, '')) IN ('isextra', 'isextrabatch', 'isextrasingle') "

fun newHeaderReleaseType(isExtraRelease: Boolean, isSingleRelease: Boolean): String = when {
isExtraRelease && isSingleRelease -> IS_EXTRA_SINGLE
isExtraRelease -> IS_EXTRA_BATCH


+ 1
- 1
src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/models/DoDetailResponse.kt Просмотреть файл

@@ -68,7 +68,7 @@ data class LaneBtn(
val truckDepartureTime: String? = null,
)

/** All Etra (`releaseType=isExtra`) tickets for a day, grouped by shop then truck (no 2F/4F split in UI). */
/** Etra family tickets (`isExtra` / `isExtrabatch` / `isExtrasingle`) for a day, grouped by shop then truck. */
data class WorkbenchEtraShopLaneGroup(
val shopCode: String?,
val shopName: String?,


Загрузка…
Отмена
Сохранить