From 5f816fcc92ce3020180927e69015e063bd69fe07 Mon Sep 17 00:00:00 2001 From: tommy Date: Thu, 11 Jun 2026 16:46:02 +0800 Subject: [PATCH] re-schedule truck --- .../service/TruckLaneScheduleService.kt | 43 +++++++++++++++++++ .../web/TruckLaneScheduleController.kt | 9 ++++ 2 files changed, 52 insertions(+) diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/TruckLaneScheduleService.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/TruckLaneScheduleService.kt index 1b75ebd..00d7507 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/TruckLaneScheduleService.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/TruckLaneScheduleService.kt @@ -224,6 +224,49 @@ open class TruckLaneScheduleService( ) } + @Transactional + open fun reactivateCancelled( + id: Long, + executeAt: LocalDateTime?, + ): TruckLaneScheduleResponse { + val s = scheduleRepository.findByIdAndDeletedFalse(id) + ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "排程不存在") + if (s.status != TruckLaneScheduleStatus.CANCELLED) { + throw ResponseStatusException(HttpStatus.BAD_REQUEST, "僅 CANCELLED 可重新排程") + } + val lines = + scheduleLineRepository.findAllBySchedule_IdAndDeletedFalseOrderByIdAsc(s.id ?: 0L) + val reactivatable = + lines.filter { it.lineStatus == TruckLaneScheduleLineStatus.SKIPPED } + if (reactivatable.isEmpty()) { + throw ResponseStatusException(HttpStatus.BAD_REQUEST, "無可重新排程的明細") + } + val whenAt = executeAt ?: LocalDateTime.now().plusMinutes(1) + validateExecuteAt(whenAt) + val requestLines = reactivatable.mapNotNull { line -> lineToRequest(line) } + if (requestLines.isEmpty()) { + throw ResponseStatusException(HttpStatus.BAD_REQUEST, "無可重新排程的明細") + } + val blocking = schedulePlanValidator.validatePlanLines(requestLines) + if (blocking.isNotEmpty()) { + throw ResponseStatusException( + HttpStatus.BAD_REQUEST, + blocking.joinToString("; ") { "${it.code}:${it.messageKey}" }, + ) + } + s.executeAt = whenAt + s.status = TruckLaneScheduleStatus.PENDING + s.appliedAt = null + s.errorMessage = null + for (line in reactivatable) { + line.lineStatus = TruckLaneScheduleLineStatus.PENDING + line.errorMessage = null + line.appliedAt = null + } + scheduleLineRepository.saveAll(reactivatable) + return toResponse(scheduleRepository.save(s), includeLines = true) + } + @Transactional open fun ignore(id: Long): TruckLaneScheduleResponse { val s = scheduleRepository.findByIdAndDeletedFalse(id) diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/TruckLaneScheduleController.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/TruckLaneScheduleController.kt index 081a284..23f9bc7 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/TruckLaneScheduleController.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/TruckLaneScheduleController.kt @@ -87,6 +87,15 @@ open class TruckLaneScheduleController @Autowired constructor( return truckLaneScheduleService.retryFailed(id, request?.executeAt) } + @PreAuthorize("hasAnyAuthority('ADMIN','TESTING')") + @PostMapping("/{id}/reactivate") + open fun reactivateCancelled( + @PathVariable id: Long, + @RequestBody(required = false) request: RetryFailedTruckLaneScheduleRequest?, + ): TruckLaneScheduleResponse { + return truckLaneScheduleService.reactivateCancelled(id, request?.executeAt) + } + @PostMapping("/{id}/ignore") open fun ignore(@PathVariable id: Long): TruckLaneScheduleResponse { return truckLaneScheduleService.ignore(id)