From f74dc566ee1cca980cf13582db0ea830a950a465 Mon Sep 17 00:00:00 2001 From: "kelvin.yau" Date: Thu, 11 Jun 2026 18:34:01 +0800 Subject: [PATCH] replenishment setup --- .../entity/DoPickOrderRecordRepository.kt | 2 + .../entity/DoPickOrderRepository.kt | 2 + .../service/DeliveryOrderService.kt | 59 +++++++++++++ .../web/models/DoDetailResponse.kt | 2 + .../01_create_do_replenishment.sql | 88 +++++++++++++++++++ 5 files changed, 153 insertions(+) create mode 100644 src/main/resources/db/changelog/changes/20260611_KelvinY/01_create_do_replenishment.sql diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRecordRepository.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRecordRepository.kt index 1aff62d..991db7b 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRecordRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRecordRepository.kt @@ -16,6 +16,8 @@ import java.time.LocalDate @Repository interface DoPickOrderRecordRepository : JpaRepository { fun findByPickOrderId(pickOrderId: Long): List + + fun findByDoOrderIdAndDeletedFalse(doOrderId: Long): List fun findByTicketNoStartingWith(ticketPrefix: String): List @Query(""" diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRepository.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRepository.kt index 0d41964..d0710fe 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrderRepository.kt @@ -21,6 +21,8 @@ interface DoPickOrderRepository : JpaRepository { ): List fun findByPickOrderId(pickOrderId: Long): List + fun findByDoOrderIdAndDeletedFalse(doOrderId: Long): List + fun findByTicketStatusIn(statuses: List): List // 在 DoPickOrderRepository 中添加这个方法 fun findByHandledByAndTicketStatusIn(handledBy: Long, status: List): List 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 0ee4173..c5f36f5 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 @@ -473,8 +473,66 @@ open class DeliveryOrderService( } } + /** + * Picker display name from [DeliveryOrderPickOrder.handlerName] (workbench source of truth). + * Linked via [pick_order.doId] or [do_pick_order_line.do_order_id]; falls back to [User.name] + * from [DeliveryOrderPickOrder.handledBy] when [handlerName] was not persisted. + */ + private fun resolveHandlerNameForDeliveryOrder(doId: Long): String? { + val sql = """ + SELECT DISTINCT TRIM(handler_name) AS handler_name + FROM ( + SELECT COALESCE( + NULLIF(TRIM(dop.handlerName), ''), + NULLIF(TRIM(u.name), '') + ) AS handler_name + FROM fpsmsdb.delivery_order_pick_order dop + INNER JOIN fpsmsdb.pick_order po + ON po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0 + LEFT JOIN fpsmsdb.user u + ON u.id = dop.handledBy AND u.deleted = 0 + WHERE dop.deleted = 0 + AND po.doId = :doId + + UNION + + SELECT COALESCE( + NULLIF(TRIM(dop.handlerName), ''), + NULLIF(TRIM(u.name), '') + ) AS handler_name + FROM fpsmsdb.delivery_order_pick_order dop + INNER JOIN fpsmsdb.pick_order po + ON po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0 + INNER JOIN fpsmsdb.do_pick_order_line dpol + ON dpol.pick_order_id = po.id AND dpol.deleted = 0 + LEFT JOIN fpsmsdb.user u + ON u.id = dop.handledBy AND u.deleted = 0 + WHERE dop.deleted = 0 + AND dpol.do_order_id = :doId + ) resolved + WHERE handler_name IS NOT NULL AND TRIM(handler_name) <> '' + ORDER BY handler_name + """.trimIndent() + + val names = jdbcDao.queryForList(sql, mapOf("doId" to doId)) + .mapNotNull { row -> + val key = row.keys.find { it.equals("handler_name", true) } ?: return@mapNotNull null + row[key]?.toString()?.trim()?.takeIf { it.isNotEmpty() } + } + .distinct() + .sorted() + + return names.joinToString(",").ifBlank { null } + } + open fun getDetailedDo(id: Long): DoDetailResponse? { val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(id) ?: return null + val handlerName = try { + resolveHandlerNameForDeliveryOrder(id) + } catch (ex: Exception) { + log.warn("Failed to resolve handler name for delivery order {}: {}", id, ex.message) + null + } val itemIds = deliveryOrder.deliveryOrderLines.mapNotNull { it.item?.id }.distinct() val stockQtyByItemId = itemIds.associateWith { itemId -> @@ -493,6 +551,7 @@ open class DeliveryOrderService( completeDate = deliveryOrder.completeDate, status = deliveryOrder.status?.value, isExtra = deliveryOrder.isExtra, + handlerName = handlerName, deliveryOrderLines = deliveryOrder.deliveryOrderLines.map { line -> val itemId = line.item?.id val stockQty = itemId?.let { stockQtyByItemId[it] } ?: BigDecimal.ZERO 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 e2519c3..3c5a533 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 @@ -20,6 +20,8 @@ data class DoDetailResponse( val status: String?, /** 加單 DO(M18 加單專用同步) */ val isExtra: Boolean = false, + /** 揀貨員名稱(來源:delivery_order_pick_order.handlerName) */ + val handlerName: String? = null, val deliveryOrderLines: List ) diff --git a/src/main/resources/db/changelog/changes/20260611_KelvinY/01_create_do_replenishment.sql b/src/main/resources/db/changelog/changes/20260611_KelvinY/01_create_do_replenishment.sql new file mode 100644 index 0000000..0af9618 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20260611_KelvinY/01_create_do_replenishment.sql @@ -0,0 +1,88 @@ +-- liquibase formatted sql +-- changeset KelvinY:20260611_01_create_do_replenishment +-- preconditions onFail:MARK_RAN +-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'do_replenishment' + +CREATE TABLE IF NOT EXISTS `do_replenishment` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` VARCHAR(30) NULL DEFAULT NULL, + `version` INT NOT NULL DEFAULT '0', + `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` VARCHAR(30) NULL DEFAULT NULL, + `deleted` TINYINT(1) NOT NULL DEFAULT '0', + `code` VARCHAR(100) NOT NULL, + `deliveryDate` DATE NOT NULL, + `sourceDoId` BIGINT NOT NULL, + `sourceDoCode` VARCHAR(100) NULL DEFAULT NULL, + `sourceDoLineId` BIGINT NOT NULL, + `sourceM18DataLogId` BIGINT NOT NULL, + `sourceM18Id` BIGINT NOT NULL, + `itemId` BIGINT NOT NULL, + `itemNo` VARCHAR(100) NULL DEFAULT NULL, + `itemName` VARCHAR(255) NULL DEFAULT NULL, + `replenishQty` DECIMAL(14, 2) NOT NULL, + `uomId` BIGINT NULL DEFAULT NULL, + `shopId` BIGINT NULL DEFAULT NULL, + `shopCode` VARCHAR(50) NULL DEFAULT NULL, + `shopName` VARCHAR(255) NULL DEFAULT NULL, + `truckLaneCode` VARCHAR(100) NULL DEFAULT NULL, + `targetDoId` BIGINT NULL DEFAULT NULL, + `targetDoCode` VARCHAR(100) NULL DEFAULT NULL, + `pickOrderLineId` BIGINT NULL DEFAULT NULL, + `status` VARCHAR(20) NOT NULL DEFAULT 'pending', + CONSTRAINT pk_do_replenishment PRIMARY KEY (`id`) +); + +SET @idx_dr_code := ( + SELECT COUNT(*) FROM information_schema.statistics + WHERE table_schema = DATABASE() AND table_name = 'do_replenishment' AND index_name = 'uk_dr_code' +); +SET @sql_dr_code := IF( + @idx_dr_code = 0, + 'CREATE UNIQUE INDEX uk_dr_code ON `do_replenishment` (`code`)', + 'SELECT 1' +); +PREPARE stmt_dr_code FROM @sql_dr_code; +EXECUTE stmt_dr_code; +DEALLOCATE PREPARE stmt_dr_code; + +SET @idx_dr_status_date := ( + SELECT COUNT(*) FROM information_schema.statistics + WHERE table_schema = DATABASE() AND table_name = 'do_replenishment' AND index_name = 'idx_dr_status_delivery_date' +); +SET @sql_dr_sd := IF( + @idx_dr_status_date = 0, + 'CREATE INDEX idx_dr_status_delivery_date ON `do_replenishment` (`status`, `deliveryDate`)', + 'SELECT 1' +); +PREPARE stmt_dr_sd FROM @sql_dr_sd; +EXECUTE stmt_dr_sd; +DEALLOCATE PREPARE stmt_dr_sd; + +SET @idx_dr_lane_shop := ( + SELECT COUNT(*) FROM information_schema.statistics + WHERE table_schema = DATABASE() AND table_name = 'do_replenishment' AND index_name = 'idx_dr_lane_shop_status' +); +SET @sql_dr_ls := IF( + @idx_dr_lane_shop = 0, + 'CREATE INDEX idx_dr_lane_shop_status ON `do_replenishment` (`truckLaneCode`, `shopId`, `status`)', + 'SELECT 1' +); +PREPARE stmt_dr_ls FROM @sql_dr_ls; +EXECUTE stmt_dr_ls; +DEALLOCATE PREPARE stmt_dr_ls; + +SET @idx_dr_source_line := ( + SELECT COUNT(*) FROM information_schema.statistics + WHERE table_schema = DATABASE() AND table_name = 'do_replenishment' AND index_name = 'idx_dr_source_line' +); +SET @sql_dr_sl := IF( + @idx_dr_source_line = 0, + 'CREATE INDEX idx_dr_source_line ON `do_replenishment` (`sourceDoId`, `sourceDoLineId`)', + 'SELECT 1' +); +PREPARE stmt_dr_sl FROM @sql_dr_sl; +EXECUTE stmt_dr_sl; +DEALLOCATE PREPARE stmt_dr_sl;