From 1c37e8aeb3193bf3df0abab343999662781d2fc9 Mon Sep 17 00:00:00 2001 From: "Tommy\\2Fi-Staff" Date: Mon, 5 Jan 2026 18:40:57 +0800 Subject: [PATCH 1/5] Added trucklane related api --- .../modules/master/entity/ShopRepository.kt | 2 +- .../master/entity/projections/ShopAndTruck.kt | 1 + .../pickOrder/entity/TruckRepository.kt | 40 ++++++++++++++++++- .../modules/pickOrder/service/TruckService.kt | 27 ++++++++++++- .../modules/pickOrder/web/TruckController.kt | 40 +++++++++++++++++++ .../pickOrder/web/models/SaveTruckRequest.kt | 4 ++ 6 files changed, 110 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/ShopRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/ShopRepository.kt index 6cc2182..6986125 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/ShopRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/ShopRepository.kt @@ -33,7 +33,7 @@ interface ShopRepository : AbstractRepository { @Query( nativeQuery = true, value = """ - SELECT s.id, s.code, s.name, s.contactNo, s.contactEmail, s.contactName, s.addr1, s.addr2, s.addr3, s.type, t.TruckLanceCode, t.LoadingSequence, t.districtReference,t.Store_id, t.remark + SELECT s.id, s.code, s.name, s.contactNo, s.contactEmail, s.contactName, s.addr1, s.addr2, s.addr3, s.type, t.TruckLanceCode, t.DepartureTime as departureTime, t.LoadingSequence, t.districtReference, t.Store_id as Store_id, t.remark FROM shop s LEFT JOIN truck t ON s.id = t.shopId WHERE s.type = 'shop' AND s.deleted = false; diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/projections/ShopAndTruck.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/projections/ShopAndTruck.kt index 427a586..345dbdf 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/projections/ShopAndTruck.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/projections/ShopAndTruck.kt @@ -19,4 +19,5 @@ interface ShopAndTruck { val districtReference: Long? val Store_id: String? val remark: String? + val truckId: Long? } diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/TruckRepository.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/TruckRepository.kt index 0a87914..e9797b7 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/TruckRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/TruckRepository.kt @@ -1,6 +1,7 @@ package com.ffii.fpsms.modules.pickOrder.entity import com.ffii.core.support.AbstractRepository +import com.ffii.fpsms.modules.master.entity.projections.ShopAndTruck import org.springframework.data.jpa.repository.Query import org.springframework.data.repository.query.Param import org.springframework.stereotype.Repository @@ -37,5 +38,42 @@ interface TruckRepository : AbstractRepository { fun findByShopIdAndStoreId(shopId: Long, storeId: String): Truck? fun findAllByShopId(shopId :Long):List //fun findByTruckLanceCode(truckLanceCode: String):List - //fun deleteByTruckLanceCodeAndDepartureTimeAndLoadingSequenceAndDistrictReferenceAndStoreId(truckLanceCode: String, departureTime: LocalTime, loadingSequence: Long, districtReference: Long, storeId: Long): String + + @Query( + nativeQuery = true, + value = """ + SELECT t.* + FROM truck t + INNER JOIN ( + SELECT TruckLanceCode, remark, MIN(id) as min_id + FROM truck + WHERE deleted = false + AND TruckLanceCode IS NOT NULL + GROUP BY TruckLanceCode, remark + ) AS unique_combos + ON t.id = unique_combos.min_id + WHERE t.deleted = false + ORDER BY t.TruckLanceCode, t.remark + """ + ) + fun findAllUniqueTruckLanceCodeAndRemarkCombinations(): List + + @Query( + nativeQuery = true, + value = """ + SELECT s.id as id, s.code as code, s.name as name, s.contactNo as contactNo, + s.contactEmail as contactEmail, s.contactName as contactName, + s.addr1 as addr1, s.addr2 as addr2, s.addr3 as addr3, s.type as type, + t.TruckLanceCode as truckLanceCode, t.DepartureTime as departureTime, + t.LoadingSequence as LoadingSequence, t.districtReference as districtReference, + t.Store_id as Store_id, t.remark as remark, t.id as truckId + FROM shop s INNER JOIN truck t ON s.id = t.shopId + WHERE t.TruckLanceCode = :truckLanceCode + AND ((:remark = '' AND (t.remark IS NULL OR t.remark = '')) + OR (:remark != '' AND t.remark = :remark)) + AND t.deleted = false + AND s.deleted = false; + """ + ) + fun findAllFromShopAndTruckByTruckLanceCodeAndRemarkAndDeletedFalse(@Param("truckLanceCode") truckLanceCode: String, @Param("remark") remark: String): List } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/TruckService.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/TruckService.kt index 41c5fce..1045b5c 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/TruckService.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/TruckService.kt @@ -12,7 +12,9 @@ import com.ffii.fpsms.modules.pickOrder.web.models.SaveTruckRequest import java.time.LocalTime import java.time.format.DateTimeFormatter import com.ffii.fpsms.modules.master.entity.ShopRepository +import com.ffii.fpsms.modules.master.entity.projections.ShopAndTruck import com.ffii.fpsms.modules.pickOrder.web.models.SaveTruckLane +import com.ffii.fpsms.modules.pickOrder.web.models.UpdateLoadingSequenceRequest import jakarta.transaction.Transactional @@ -187,7 +189,7 @@ open class TruckService( } open fun findAllByShopId(shopId: Long): List { - return truckRepository.findAllByShopId(shopId) + return truckRepository.findByShopIdAndDeletedFalse(shopId) } @Transactional @@ -212,7 +214,10 @@ open class TruckService( @Transactional open fun deleteById(id: Long): String { - truckRepository.deleteById(id) + val deleteTruck = truckRepository.findById(id).orElseThrow().apply { + deleted = true + } + truckRepository.save(deleteTruck) return "Truck deleted successfully with id: $id" } @@ -241,4 +246,22 @@ open class TruckService( return truckRepository.save(truck) } + open fun findAllUniqueTruckLanceCodeAndRemarkCombinations(): List { + return truckRepository.findAllUniqueTruckLanceCodeAndRemarkCombinations() + } + + open fun findAllFromShopAndTruckByTruckLanceCodeAndRemarkAndDeletedFalse(truckLanceCode: String, remark: String): List { + return truckRepository.findAllFromShopAndTruckByTruckLanceCodeAndRemarkAndDeletedFalse(truckLanceCode, remark) + } + + @Transactional + open fun updateLoadingSequence(request: UpdateLoadingSequenceRequest): Truck { + val truck = truckRepository.findById(request.id).orElseThrow { + IllegalArgumentException("Truck not found with id: ${request.id}") + } + + truck.loadingSequence = request.loadingSequence + return truckRepository.save(truck) + } + } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/TruckController.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/TruckController.kt index 4f144cd..e551c44 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/TruckController.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/TruckController.kt @@ -1,5 +1,6 @@ package com.ffii.fpsms.modules.pickOrder.web +import com.ffii.fpsms.modules.master.entity.projections.ShopAndTruck import com.ffii.fpsms.modules.master.web.models.MessageResponse import com.ffii.fpsms.modules.pickOrder.entity.Truck import org.springframework.web.bind.ServletRequestBindingException @@ -15,6 +16,7 @@ import com.ffii.fpsms.modules.pickOrder.service.TruckService import com.ffii.fpsms.modules.pickOrder.entity.TruckRepository import com.ffii.fpsms.modules.pickOrder.web.models.SaveTruckLane import com.ffii.fpsms.modules.pickOrder.web.models.deleteTruckLane +import com.ffii.fpsms.modules.pickOrder.web.models.UpdateLoadingSequenceRequest import jakarta.validation.Valid @RestController @@ -173,4 +175,42 @@ class TruckController( ) } } + + @GetMapping("/findAllUniqueTruckLanceCodeAndRemarkCombinations") + fun findAllUniqueTruckLanceCodeAndRemarkCombinations(): List { + return truckService.findAllUniqueTruckLanceCodeAndRemarkCombinations() + } + + + @GetMapping("/findAllFromShopAndTruckByTruckLanceCodeAndRemarkAndDeletedFalse") + fun findAllFromShopAndTruckByTruckLanceCodeAndRemarkAndDeletedFalse(@RequestParam truckLanceCode: String, @RequestParam remark: String): List { + return truckService.findAllFromShopAndTruckByTruckLanceCodeAndRemarkAndDeletedFalse(truckLanceCode, remark) + } + + @PostMapping("/updateLoadingSequence") + fun updateLoadingSequence(@Valid @RequestBody request: UpdateLoadingSequenceRequest): MessageResponse { + try { + val truck = truckService.updateLoadingSequence(request) + return MessageResponse( + id = truck.id, + name = truck.shopName, + code = truck.truckLanceCode, + type = "truck", + message = "Loading sequence updated successfully", + errorPosition = null, + entity = truck + ) + } catch (e: Exception) { + return MessageResponse( + id = null, + name = null, + code = null, + type = "truck", + message = "Error: ${e.message}", + errorPosition = null, + entity = null + ) + } + } + } diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/SaveTruckRequest.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/SaveTruckRequest.kt index 3953fb5..c0f8a6b 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/SaveTruckRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/SaveTruckRequest.kt @@ -24,3 +24,7 @@ data class SaveTruckLane( data class deleteTruckLane( val id: Long ) +data class UpdateLoadingSequenceRequest( + val id: Long, + val loadingSequence: Int +) From 19e2039a52543dc339ca0ffd1f081aa784d80359 Mon Sep 17 00:00:00 2001 From: "vluk@2fi-solutions.com.hk" Date: Mon, 5 Jan 2026 19:04:33 +0800 Subject: [PATCH 2/5] adding shcedule query --- .../jobOrder/entity/JobOrderRepository.kt | 41 ++++ .../jobOrder/service/JobOrderService.kt | 19 ++ .../ProductionScheduleLineRepository.kt | 1 + .../entity/ProductionScheduleRepository.kt | 10 +- .../service/ProductionScheduleService.kt | 221 +++++++++++++----- 5 files changed, 228 insertions(+), 64 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderRepository.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderRepository.kt index bb087b8..60eae3c 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderRepository.kt @@ -172,10 +172,51 @@ interface JobOrderRepository : AbstractRepository { left join inventory_lot_line ill on ill.id = sol.inventoryLotLineId left join inventory_lot il on il.id = ill.inventoryLotId where jo.prodScheduleLineId = :prodScheduleLineId + and jo.actualEnd is null group by jo.id, uc2.udfudesc limit 1 """ ) fun findJobOrderByProdScheduleLineId(prodScheduleLineId: Long): JobOrderDetailWithJsonString?; + @Query( + nativeQuery = true, + value = """ + select + jo.id, + jo.code, + b.name, + jo.reqQty, + b.outputQtyUom as unit, + uc2.udfudesc as uom, + json_arrayagg( + json_object( + 'id', jobm.id, + 'code', i.code, + 'name', i.name, + 'lotNo', il.lotNo, + 'reqQty', jobm.reqQty, + 'uom', uc.udfudesc, + 'status', jobm.status + ) + ) as pickLines, + jo.status + from job_order jo + left join bom b on b.id = jo.bomId + left join item_uom iu on b.itemId = iu.itemId and iu.salesUnit = true + left join uom_conversion uc2 on uc2.id = iu.uomId + left join job_order_bom_material jobm on jo.id = jobm.jobOrderId + left join items i on i.id = jobm.itemId + left join uom_conversion uc on uc.id = jobm.uomId + left join stock_out_line sol on sol.id = jobm.stockOutLineId + left join inventory_lot_line ill on ill.id = sol.inventoryLotLineId + left join inventory_lot il on il.id = ill.inventoryLotId + where b.itemId = :itemId + and jo.actualEnd is null + group by jo.id, uc2.udfudesc + limit 1 + """ + ) + fun findJobOrderByItemId(itemId: Long): JobOrderDetailWithJsonString?; + } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt index 3ed0e53..4c19d07 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt @@ -286,6 +286,25 @@ open class JobOrderService( ) } + open fun jobOrderDetailByItemId(itemId: Long): JobOrderDetail { + val sqlResult = jobOrderRepository.findJobOrderByItemId(itemId) ?: throw NoSuchElementException("Job Order not found with itemId: $itemId"); + + val type = object : TypeToken>() {}.type + val jsonResult = sqlResult.pickLines?.let { GsonUtils.stringToJson>(it, type) } + return JobOrderDetail( + id = sqlResult.id, + code = sqlResult.code, + itemCode = sqlResult.itemCode, + name = sqlResult.name, + reqQty = sqlResult.reqQty, + uom = sqlResult.uom, + pickLines = jsonResult, + status = sqlResult.status, + shortUom = sqlResult.shortUom + ) + } + + open fun jobOrderDetailByCode(code: String): JobOrderDetail { val sqlResult = jobOrderRepository.findJobOrderDetailByCode(code) ?: throw NoSuchElementException("Job Order not found: $code"); diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleLineRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleLineRepository.kt index f0328d8..2c2f932 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleLineRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleLineRepository.kt @@ -39,6 +39,7 @@ interface ProductionScheduleLineRepository : AbstractRepository? + @Query(""" SELECT psl FROM ProductionScheduleLine psl JOIN psl.productionSchedule ps diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt index 6dba219..7ffc1e0 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt @@ -7,6 +7,7 @@ import org.springframework.data.domain.Pageable import org.springframework.data.jpa.repository.Query import org.springframework.stereotype.Repository import java.time.LocalDateTime +import java.time.LocalDate @Repository interface ProductionScheduleRepository : AbstractRepository { @@ -117,8 +118,11 @@ interface ProductionScheduleRepository : AbstractRepository= :produceAt + ) and (:totalEstProdCount is null or :totalEstProdCount = '' or totalEstProdCount = :totalEstProdCount) and (coalesce(:types) is null or type in :types) order by id ASC; @@ -126,7 +130,7 @@ interface ProductionScheduleRepository : AbstractRepository?, pageable: Pageable diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt index 412ec66..6459f3a 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt @@ -11,6 +11,7 @@ import com.ffii.fpsms.modules.jobOrder.service.JobOrderService import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderBomMaterialRequest import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderProcessRequest import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderRequest +import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetail import com.ffii.fpsms.modules.master.entity.* import com.ffii.fpsms.modules.master.entity.projections.* import com.ffii.fpsms.modules.master.web.models.MessageResponse @@ -46,10 +47,16 @@ import kotlin.collections.component2 import kotlin.jvm.optionals.getOrNull import kotlin.math.ceil import kotlin.comparisons.maxOf -import org.apache.poi.xssf.usermodel.XSSFWorkbook -import org.apache.poi.ss.usermodel.FillPatternType + +// === POI IMPORTS FOR EXCEL EXPORT WITH PRINT SETUP === +import org.apache.poi.ss.usermodel.* import org.apache.poi.ss.usermodel.IndexedColors +import org.apache.poi.ss.usermodel.FillPatternType +import org.apache.poi.ss.usermodel.VerticalAlignment +import org.apache.poi.xssf.usermodel.XSSFWorkbook +import org.apache.poi.xssf.usermodel.XSSFPrintSetup import java.io.ByteArrayOutputStream +import java.sql.Timestamp @Service open class ProductionScheduleService( @@ -118,10 +125,15 @@ open class ProductionScheduleService( open fun allDetailedProdSchedulesByPage(request: SearchProdScheduleRequest): RecordsRes { val pageable = PageRequest.of(request.pageNum ?: 0, request.pageSize ?: 10); + val produceAtDate: LocalDate = request.produceAt?.takeIf { it.isNotBlank() } + ?.let { + LocalDate.parse(it.trim()) + } + ?: LocalDate.now() + val response = productionScheduleRepository.findProdScheduleInfoByProduceAtByPage( - produceAt = request.produceAt, + produceAt = produceAtDate!!, totalEstProdCount = request.totalEstProdCount, -// types = listOf("detailed", "manual"), types = request.types, pageable = pageable ) @@ -397,18 +409,22 @@ open class ProductionScheduleService( val prodScheduleLine = productionScheduleLineRepository.findById(prodScheduleLineId).getOrNull() ?: throw NoSuchElementException("Production Schedule Line with ID $prodScheduleLineId not found.") - try { - jobOrderService.jobOrderDetailByPsId(prodScheduleLineId) - } catch (e: NoSuchElementException) { - - // 3. Fetch BOM, handling nullability safely - val item = prodScheduleLine.item - ?: throw IllegalStateException("Item object is missing for Production Schedule Line $prodScheduleLineId.") + // 3. Fetch BOM, handling nullability safely + val item = prodScheduleLine.item + ?: throw IllegalStateException("Item object is missing for Production Schedule Line $prodScheduleLineId.") - val itemId = item.id - ?: throw IllegalStateException("Item ID is missing for Production Schedule Line $prodScheduleLineId.") + val itemId = item.id + ?: throw IllegalStateException("Item ID is missing for Production Schedule Line $prodScheduleLineId.") - val bom = bomService.findByItemId(itemId) + try { + jobOrderService.jobOrderDetailByItemId(itemId) + } catch (e: NoSuchElementException) { + //only do with no JO is working + + try { + jobOrderService.jobOrderDetailByItemId(itemId) + } catch (e: NoSuchElementException) { + val bom = bomService.findByItemId(itemId) ?: throw NoSuchElementException("BOM not found for Item ID $itemId.") // 4. Update Prod Schedule Line fields @@ -448,6 +464,7 @@ open class ProductionScheduleService( jobOrderProcessService.createJobOrderProcessesByJoId(createdJobOrderId) productProcessService.createProductProcessByJobOrderId(createdJobOrderId, prodScheduleLine.itemPriority.toInt()) //} + } } @@ -1338,60 +1355,106 @@ open class ProductionScheduleService( } - fun exportProdScheduleToExcel(lines: List>, lineMats: List>): ByteArray { + fun exportProdScheduleToExcel( + lines: List>, + lineMats: List> + ): ByteArray { val workbook = XSSFWorkbook() - - // 1. Group Production Lines by Date - val groupedData = lines.groupBy { - val produceAt = it["produceAt"] - when (produceAt) { - is LocalDateTime -> produceAt.toLocalDate().toString() - is java.sql.Timestamp -> produceAt.toLocalDateTime().toLocalDate().toString() - else -> produceAt?.toString()?.substring(0, 10) ?: "Unknown_Date" - } - } - // 2. Define Header Style + // Header style val headerStyle = workbook.createCellStyle().apply { fillForegroundColor = IndexedColors.GREY_25_PERCENT.index fillPattern = FillPatternType.SOLID_FOREGROUND + wrapText = true + verticalAlignment = VerticalAlignment.CENTER val font = workbook.createFont() font.bold = true setFont(font) } - // 3. Create Production Worksheets + // Body style + val wrapStyle = workbook.createCellStyle().apply { + wrapText = true + verticalAlignment = VerticalAlignment.TOP + } + + // Group production lines by date + val groupedData = lines.groupBy { + val produceAt = it["produceAt"] + when (produceAt) { + is LocalDateTime -> produceAt.toLocalDate().toString() + is Timestamp -> produceAt.toLocalDateTime().toLocalDate().toString() + is String -> produceAt.take(10) + else -> produceAt?.toString()?.substring(0, 10) ?: "Unknown_Date" + } + } + + // Production sheets (one per date) groupedData.forEach { (dateKey, dailyLines) -> - val sheetName = dateKey.replace("[/\\\\?*:\\[\\]]".toRegex(), "-") - val sheet = workbook.createSheet(sheetName) + val safeSheetName = dateKey.replace(Regex("[/\\\\?*:\\[\\]]"), "-").take(31) + val sheet = workbook.createSheet(safeSheetName) - val headers = listOf("Item Name", "Avg Qty Last Month", "Stock Qty", "Days Left", "Output Qty", "Batch Need", "Priority") + val headers = listOf( + "Item Name", "Avg Qty Last Month", "Stock Qty", "Days Left", + "Output Qty", "Batch Need", "Priority" + ) + + // Header row val headerRow = sheet.createRow(0) headers.forEachIndexed { i, title -> - val cell = headerRow.createCell(i) - cell.setCellValue(title) - cell.setCellStyle(headerStyle) + headerRow.createCell(i).apply { + setCellValue(title) + cellStyle = headerStyle + } } + // Data rows dailyLines.forEachIndexed { index, line -> val row = sheet.createRow(index + 1) - row.createCell(0).setCellValue(line["itemName"]?.toString() ?: "") - row.createCell(1).setCellValue(asDouble(line["avgQtyLastMonth"])) - row.createCell(2).setCellValue(asDouble(line["stockQty"])) - row.createCell(3).setCellValue(asDouble(line["daysLeft"])) - row.createCell(4).setCellValue(asDouble(line["outputdQty"])) // Note: Matching your snippet's "outputdQty" key - row.createCell(5).setCellValue(asDouble(line["batchNeed"])) - row.createCell(6).setCellValue(asDouble(line["itemPriority"])) + row.heightInPoints = 35f // Slightly taller for portrait readability + + row.createCell(0).apply { setCellValue(line["itemName"]?.toString() ?: ""); cellStyle = wrapStyle } + row.createCell(1).apply { setCellValue(asDouble(line["avgQtyLastMonth"])); cellStyle = wrapStyle } + row.createCell(2).apply { setCellValue(asDouble(line["stockQty"])); cellStyle = wrapStyle } + row.createCell(3).apply { setCellValue(asDouble(line["daysLeft"])); cellStyle = wrapStyle } + row.createCell(4).apply { setCellValue(asDouble(line["outputdQty"] ?: line["outputQty"])); cellStyle = wrapStyle } + row.createCell(5).apply { setCellValue(asDouble(line["batchNeed"])); cellStyle = wrapStyle } + row.createCell(6).apply { setCellValue(asDouble(line["itemPriority"])); cellStyle = wrapStyle } } - for (i in headers.indices) { sheet.autoSizeColumn(i) } + // Auto-size with wider limits for portrait + for (i in headers.indices) { + sheet.autoSizeColumn(i) + val maxWidth = when (i) { + 0 -> 35 * 256 // Item Name can be longer + else -> 18 * 256 + } + if (sheet.getColumnWidth(i) > maxWidth) { + sheet.setColumnWidth(i, maxWidth) + } + } + + // === PORTRAIT PRINT SETUP === + val printSetup = sheet.printSetup + printSetup.paperSize = XSSFPrintSetup.A4_PAPERSIZE + printSetup.landscape = false // ← Portrait mode + printSetup.fitWidth = 1.toShort() // Crucial: scale to fit width + printSetup.fitHeight = 0.toShort() // Allow multiple pages tall + + sheet.fitToPage = true + sheet.horizontallyCenter = true + sheet.setMargin(Sheet.LeftMargin, 0.5) + sheet.setMargin(Sheet.RightMargin, 0.5) + sheet.setMargin(Sheet.TopMargin, 0.7) + sheet.setMargin(Sheet.BottomMargin, 0.7) } - // 4. Create Material Summary Worksheet + // === MATERIAL SUMMARY SHEET - PORTRAIT OPTIMIZED === val matSheet = workbook.createSheet("Material Summary") + val matHeaders = listOf( - "Mat Code", "Mat Name", "Required Qty", "Total Qty Need", - "UoM", "Purchased Qty", "On Hand Qty", "Unavailable Qty", + "Mat Code", "Mat Name", "Required Qty", "Total Qty Need", + "UoM", "Purchased Qty", "On Hand Qty", "Unavailable Qty", "Related Item Code", "Related Item Name" ) @@ -1399,37 +1462,73 @@ open class ProductionScheduleService( matHeaders.forEachIndexed { i, title -> matHeaderRow.createCell(i).apply { setCellValue(title) - setCellStyle(headerStyle) + cellStyle = headerStyle } } lineMats.forEachIndexed { index, rowData -> val row = matSheet.createRow(index + 1) - + row.heightInPoints = 35f + val totalNeed = asDouble(rowData["totalMatQtyNeed"]) val purchased = asDouble(rowData["purchasedQty"]) val onHand = asDouble(rowData["onHandQty"]) - - // Calculation: Required Qty = totalMatQtyNeed - purchasedQty - onHandQty (minimum 0) val requiredQty = (totalNeed - purchased - onHand).coerceAtLeast(0.0) - row.createCell(0).setCellValue(rowData["matCode"]?.toString() ?: "") - row.createCell(1).setCellValue(rowData["matName"]?.toString() ?: "") - row.createCell(2).setCellValue(requiredQty) - row.createCell(3).setCellValue(totalNeed) - row.createCell(4).setCellValue(rowData["uomName"]?.toString() ?: "") - row.createCell(5).setCellValue(purchased) - row.createCell(6).setCellValue(onHand) - row.createCell(7).setCellValue(asDouble(rowData["unavailableQty"])) - row.createCell(8).setCellValue(rowData["itemCode"]?.toString() ?: "") - row.createCell(9).setCellValue(rowData["itemName"]?.toString() ?: "") - } + val values = listOf( + rowData["matCode"]?.toString() ?: "", + rowData["matName"]?.toString() ?: "", + requiredQty, + totalNeed, + rowData["uomName"]?.toString() ?: "", + purchased, + onHand, + asDouble(rowData["unavailableQty"]), + rowData["itemCode"]?.toString() ?: "", + rowData["itemName"]?.toString() ?: "" + ) - for (i in matHeaders.indices) { matSheet.autoSizeColumn(i) } + values.forEachIndexed { i, value -> + val cell = row.createCell(i) + when (value) { + is String -> cell.setCellValue(value) + is Number -> cell.setCellValue(value.toDouble()) + else -> cell.setCellValue("") + } + cell.cellStyle = wrapStyle + } + } - // 5. Finalize and Return + // Manual column widths optimized for PORTRAIT A4 + matSheet.setColumnWidth(0, 16 * 256) // Mat Code + matSheet.setColumnWidth(1, 32 * 256) // Mat Name + matSheet.setColumnWidth(2, 14 * 256) // Required Qty + matSheet.setColumnWidth(3, 14 * 256) // Total Qty Need + matSheet.setColumnWidth(4, 10 * 256) // UoM + matSheet.setColumnWidth(5, 14 * 256) // Purchased Qty + matSheet.setColumnWidth(6, 14 * 256) // On Hand Qty + matSheet.setColumnWidth(7, 14 * 256) // Unavailable Qty + matSheet.setColumnWidth(8, 22 * 256) // Related Item Code + matSheet.setColumnWidth(9, 40 * 256) // Related Item Name (longest) + + // Portrait print setup + val matPrintSetup = matSheet.printSetup + matPrintSetup.paperSize = XSSFPrintSetup.A4_PAPERSIZE + matPrintSetup.landscape = false // ← Portrait + matPrintSetup.fitWidth = 1.toShort() + matPrintSetup.fitHeight = 0.toShort() + + matSheet.fitToPage = true + matSheet.horizontallyCenter = true + matSheet.setMargin(Sheet.LeftMargin, 0.5) + matSheet.setMargin(Sheet.RightMargin, 0.5) + matSheet.setMargin(Sheet.TopMargin, 0.7) + matSheet.setMargin(Sheet.BottomMargin, 0.7) + + // Finalize val out = ByteArrayOutputStream() workbook.use { it.write(out) } + workbook.close() return out.toByteArray() } From 8aa86ec2b6bd6ab1ad71131d990eed7f29500fea Mon Sep 17 00:00:00 2001 From: "B.E.N.S.O.N" Date: Mon, 5 Jan 2026 19:27:24 +0800 Subject: [PATCH 3/5] Supporting Function: Equipment Repair & Maintenance --- .../modules/master/entity/EquipmentDetail.kt | 14 ++++ .../master/service/EquipmentDetailService.kt | 80 ++++++++++++++++++- .../master/service/EquipmentService.kt | 52 +++++++----- .../master/web/EquipmentDetailController.kt | 17 +++- .../master/web/UpdateMaintenanceRequest.kt | 6 ++ 5 files changed, 142 insertions(+), 27 deletions(-) create mode 100644 src/main/java/com/ffii/fpsms/modules/master/web/UpdateMaintenanceRequest.kt diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/EquipmentDetail.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/EquipmentDetail.kt index efda5c8..2e10d80 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/EquipmentDetail.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/EquipmentDetail.kt @@ -6,6 +6,7 @@ import jakarta.persistence.Entity import jakarta.persistence.Table import jakarta.validation.constraints.NotNull import jakarta.validation.constraints.Size +import java.time.LocalDateTime @Table(name = "equipment_detail") @Entity @@ -15,6 +16,19 @@ open class EquipmentDetail : BaseEntity() { @Column(name = "equipmentCode", nullable = true, length = 255) open var equipmentCode: String? = null + @Column(name = "repairAndMaintenanceStatus", nullable = true) + open var repairAndMaintenanceStatus: Boolean? = null + + @Column(name = "latestRepairAndMaintenanceDate", nullable = true) + open var latestRepairAndMaintenanceDate: LocalDateTime? = null + + @Column(name = "lastRepairAndMaintenanceDate", nullable = true) + open var lastRepairAndMaintenanceDate: LocalDateTime? = null + + @Size(max = 255) + @Column(name = "repairAndMaintenanceRemarks", nullable = true, length = 255) + open var repairAndMaintenanceRemarks: String? = null + @Size(max = 30) @NotNull @Column(name = "code", nullable = false, length = 30) diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/EquipmentDetailService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/EquipmentDetailService.kt index 008184a..d14144d 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/EquipmentDetailService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/EquipmentDetailService.kt @@ -7,6 +7,9 @@ import com.ffii.fpsms.modules.master.entity.EquipmentDetailRepository import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import com.ffii.fpsms.modules.master.web.models.NewEquipmentDetailRequest +import com.ffii.fpsms.modules.master.web.models.UpdateMaintenanceRequest +import java.time.LocalDateTime + @Service open class EquipmentDetailService( private val jdbcDao: JdbcDao, @@ -18,21 +21,54 @@ open class EquipmentDetailService( } open fun getEquipmentDetailsByPage(args: Map): List> { + println("Search args: $args") + val sql = StringBuilder( - "SELECT e.id, e.code, e.name, e.description FROM equipment_detail e WHERE e.deleted = FALSE" + """ + SELECT + e.id AS id, + e.code AS code, + e.name AS name, + e.description AS description, + e.equipmentCode AS equipmentCode, + e.repairAndMaintenanceStatus AS repairAndMaintenanceStatus, + e.latestRepairAndMaintenanceDate AS latestRepairAndMaintenanceDate, + e.lastRepairAndMaintenanceDate AS lastRepairAndMaintenanceDate, + e.repairAndMaintenanceRemarks AS repairAndMaintenanceRemarks + FROM equipment_detail e + WHERE e.deleted = FALSE + """ ) - if (args.containsKey("code")) { - sql.append(" AND e.code like :code ") + + // Handle combined search for code and equipmentCode (OR logic) + val searchTerm = args["equipmentCode"] as? String + val codeTerm = args["code"] as? String + + if (searchTerm != null && codeTerm != null && searchTerm == codeTerm) { + // When both are provided with the same value, search using OR + sql.append(" AND (LOWER(e.code) LIKE LOWER(:equipmentCode) OR LOWER(e.equipmentCode) LIKE LOWER(:equipmentCode)) ") + } else { + // Otherwise, use individual conditions + if (codeTerm != null && (searchTerm == null || searchTerm != codeTerm)) { + sql.append(" AND LOWER(e.code) LIKE LOWER(:code) ") + } + if (searchTerm != null && (codeTerm == null || searchTerm != codeTerm)) { + sql.append(" AND LOWER(e.equipmentCode) LIKE LOWER(:equipmentCode) ") + } } + if (args.containsKey("id")) { sql.append(" AND e.id like :id ") } if (args.containsKey("name")) { - sql.append(" AND e.name like :name ") + sql.append(" AND LOWER(e.name) LIKE LOWER(:name) ") } if (args.containsKey("description")) { sql.append(" AND e.description like :description ") } + if (args.containsKey("repairAndMaintenanceStatus")) { + sql.append(" AND e.repairAndMaintenanceStatus = :repairAndMaintenanceStatus ") + } return jdbcDao.queryForList(sql.toString(), args) } @@ -51,6 +87,42 @@ open class EquipmentDetailService( open fun findByDescription(description: String): EquipmentDetail? { return equipmentDetailRepository.findByDescriptionAndDeletedIsFalse(description) } + + @Transactional + open fun updateMaintenanceAndRepair( + id: Long, + request: UpdateMaintenanceRequest + ): EquipmentDetail { + val equipmentDetail = findById(id) + ?: throw IllegalArgumentException("Equipment detail not found with id: $id") + + // Store the previous status and latest date before updating + val previousStatus = equipmentDetail.repairAndMaintenanceStatus + val previousLatestDate = equipmentDetail.latestRepairAndMaintenanceDate + + // Update status and remarks + equipmentDetail.repairAndMaintenanceStatus = request.repairAndMaintenanceStatus + equipmentDetail.repairAndMaintenanceRemarks = request.repairAndMaintenanceRemarks + + // Handle date updates based on status change + when { + // Changing from "是" (true) to "否" (false) + previousStatus == true && request.repairAndMaintenanceStatus == false -> { + // Save current latestRepairAndMaintenanceDate to lastRepairAndMaintenanceDate + equipmentDetail.lastRepairAndMaintenanceDate = previousLatestDate + // Update latestRepairAndMaintenanceDate to current time + equipmentDetail.latestRepairAndMaintenanceDate = LocalDateTime.now() + } + // Changing from "否" (false) to "是" (true) + // Keep dates unchanged - no action needed + previousStatus == false && request.repairAndMaintenanceStatus == true -> { + // Dates remain unchanged + } + // Other cases (null to true/false, or same status) - no date changes + } + + return equipmentDetailRepository.saveAndFlush(equipmentDetail) + } /* @Transactional open fun saveEquipmentDetail(request: NewEquipmentDetailRequest): EquipmentDetail { diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/EquipmentService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/EquipmentService.kt index e40444d..9bc2d8f 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/EquipmentService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/EquipmentService.kt @@ -18,26 +18,40 @@ open class EquipmentService( } open fun getEquipmentsByPage(args: Map): List> { - val sql = StringBuilder( - "SELECT e.id, e.code, e.name, e.description, e.equipmentTypeId FROM equipment e WHERE e.deleted = FALSE" - ) - if (args.containsKey("code")) { - sql.append(" AND e.code like :code ") - } - if (args.containsKey("id")) { - sql.append(" AND e.id like :id ") - } - if (args.containsKey("name")) { - sql.append(" AND e.name like :name ") - } - if (args.containsKey("description")) { - sql.append(" AND e.description like :description ") - } - if (args.containsKey("equipmentTypeId")) { - sql.append(" AND e.equipmentTypeId like :equipmentTypeId ") - } - return jdbcDao.queryForList(sql.toString(), args) + val sql = StringBuilder( + """ + SELECT + e.id AS id, + e.code AS code, + e.name AS name, + e.description AS description, + e.equipmentTypeId AS equipmentTypeId, + ed.repairAndMaintenanceStatus AS repairAndMaintenanceStatus, + ed.latestRepairAndMaintenanceDate AS latestRepairAndMaintenanceDate, + ed.lastRepairAndMaintenanceDate AS lastRepairAndMaintenanceDate, + ed.repairAndMaintenanceRemarks AS repairAndMaintenanceRemarks + FROM equipment e + LEFT JOIN equipment_detail ed ON e.code = ed.equipmentCode + WHERE e.deleted = FALSE + """ + ) + if (args.containsKey("code")) { + sql.append(" AND e.code like :code ") + } + if (args.containsKey("id")) { + sql.append(" AND e.id like :id ") + } + if (args.containsKey("name")) { + sql.append(" AND e.name like :name ") + } + if (args.containsKey("description")) { + sql.append(" AND e.description like :description ") + } + if (args.containsKey("equipmentTypeId")) { + sql.append(" AND e.equipmentTypeId like :equipmentTypeId ") } + return jdbcDao.queryForList(sql.toString(), args) +} open fun findById(id: Long): Equipment? { return equipmentRepository.findByIdAndDeletedFalse(id) diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/EquipmentDetailController.kt b/src/main/java/com/ffii/fpsms/modules/master/web/EquipmentDetailController.kt index bfa06d3..b62cc14 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/web/EquipmentDetailController.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/web/EquipmentDetailController.kt @@ -10,6 +10,8 @@ import jakarta.validation.Valid import org.springframework.web.bind.annotation.* import java.util.Collections.emptyList import com.ffii.fpsms.modules.master.web.models.NewEquipmentDetailRequest +import com.ffii.fpsms.modules.master.web.models.UpdateMaintenanceRequest + @RestController @RequestMapping("/EquipmentDetail") class EquipmentDetailController( @@ -43,31 +45,38 @@ fun getAllEquipmentDetailByPage( .addStringLike("code") .addStringLike("description") .addStringLike("id") + .addStringLike("equipmentCode") + .addBoolean("repairAndMaintenanceStatus") .build() val pageSize = request.getParameter("pageSize")?.toIntOrNull() ?: 10 val pageNum = request.getParameter("pageNum")?.toIntOrNull() ?: 1 - // 方法名和变量名都要和 Service 保持一致 val fullList = equipmentDetailService.getEquipmentDetailsByPage(criteriaArgs) ?: emptyList() val paginatedList = PagingUtils.getPaginatedList(fullList, pageSize, pageNum) return RecordsRes(paginatedList as List>, fullList.size) } - // 详情 @GetMapping("/details/{id}") fun getEquipmentDetail(@PathVariable id: Long): EquipmentDetail? { return equipmentDetailService.findById(id) } + + @PutMapping("/update/{id}") + fun updateMaintenanceAndRepair( + @PathVariable id: Long, + @RequestBody request: UpdateMaintenanceRequest + ): EquipmentDetail { + return equipmentDetailService.updateMaintenanceAndRepair(id, request) + } /* - // 新增/编辑 @PostMapping("/save") fun saveEquipmentDetail(@Valid @RequestBody equipmentDetail: NewEquipmentDetailRequest): EquipmentDetail { return equipmentDetailService.saveEquipmentDetail(equipmentDetail) } */ - // 逻辑删除 + @DeleteMapping("/delete/{id}") fun deleteEquipmentDetail(@PathVariable id: Long) { equipmentDetailService.deleteEquipmentDetail(id) diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/UpdateMaintenanceRequest.kt b/src/main/java/com/ffii/fpsms/modules/master/web/UpdateMaintenanceRequest.kt new file mode 100644 index 0000000..bed17d5 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/master/web/UpdateMaintenanceRequest.kt @@ -0,0 +1,6 @@ +package com.ffii.fpsms.modules.master.web.models + +data class UpdateMaintenanceRequest( + val repairAndMaintenanceStatus: Boolean?, + val repairAndMaintenanceRemarks: String? +) \ No newline at end of file From 7b539509cfd5772466a7a40ed72d64bb7b2bcdd6 Mon Sep 17 00:00:00 2001 From: "Tommy\\2Fi-Staff" Date: Wed, 7 Jan 2026 03:54:50 +0800 Subject: [PATCH 4/5] update shop and truck , item --- .../fpsms/m18/service/M18MasterDataService.kt | 20 +++++++++++++++++-- .../modules/master/service/ItemsService.kt | 13 +++++++++++- .../master/web/models/NewItemRequest.kt | 8 ++++++++ .../pickOrder/entity/TruckRepository.kt | 6 ++---- .../modules/pickOrder/service/TruckService.kt | 4 ++-- .../modules/pickOrder/web/TruckController.kt | 6 +++--- 6 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/m18/service/M18MasterDataService.kt b/src/main/java/com/ffii/fpsms/m18/service/M18MasterDataService.kt index c0110ec..b1a5798 100644 --- a/src/main/java/com/ffii/fpsms/m18/service/M18MasterDataService.kt +++ b/src/main/java/com/ffii/fpsms/m18/service/M18MasterDataService.kt @@ -165,7 +165,15 @@ open class M18MasterDataService( maxQty = null, m18Id = id, m18LastModifyDate = commonUtils.timestampToLocalDateTime(pro.lastModifyDate), - qcCategoryId = null + qcCategoryId = null, + store_id = null, + warehouse = null, + area = null, + slot = null, + LocationCode = null, + isEgg = null, + isFee = null, + isBag = null ) val savedItem = itemsService.saveItem(saveItemRequest) @@ -260,7 +268,15 @@ open class M18MasterDataService( maxQty = null, m18Id = item.id, m18LastModifyDate = commonUtils.timestampToLocalDateTime(pro.lastModifyDate), - qcCategoryId = null + qcCategoryId = null, + store_id = null, + warehouse = null, + area = null, + slot = null, + LocationCode = null, + isEgg = null, + isFee = null, + isBag = null ) val savedItem = itemsService.saveItem(saveItemRequest) diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/ItemsService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/ItemsService.kt index fb01ea7..cd5e335 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/ItemsService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/ItemsService.kt @@ -392,7 +392,10 @@ open class ItemsService( "i.id, " + "i.code, " + "i.name, " + - "i.description " + + "i.description, " + + "i.type, " + + "i.`LocationCode` as LocationCode, " + + "i.`qcCategoryId` as qcCategoryId " + "FROM items i " + "WHERE i.deleted = FALSE" ); @@ -512,6 +515,14 @@ open class ItemsService( this.qcCategory = qcCategory m18Id = request.m18Id ?: this.m18Id m18LastModifyDate = request.m18LastModifyDate ?: this.m18LastModifyDate + store_id = request.store_id + warehouse = request.warehouse + area = request.area + slot = request.slot + LocationCode = request.LocationCode + isEgg = request.isEgg ?: false + isFee = request.isFee ?: false + isBag = request.isBag ?: false } logger.info("saving item: $item") val savedItem = itemsRepository.saveAndFlush(item) diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/models/NewItemRequest.kt b/src/main/java/com/ffii/fpsms/modules/master/web/models/NewItemRequest.kt index 2a92707..32f92dd 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/web/models/NewItemRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/web/models/NewItemRequest.kt @@ -45,6 +45,14 @@ data class NewItemRequest( val m18Id: Long?, val m18LastModifyDate: LocalDateTime?, val qcCategoryId: Long?, + val store_id: String?, + val warehouse: String?, + val area: String?, + val slot: String?, + val LocationCode: String?, + val isEgg: Boolean?, + val isFee: Boolean?, + val isBag: Boolean?, // val type: List?, // val uom: List?, // val weightUnit: List?, diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/TruckRepository.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/TruckRepository.kt index e9797b7..55d0861 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/TruckRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/TruckRepository.kt @@ -61,7 +61,7 @@ interface TruckRepository : AbstractRepository { @Query( nativeQuery = true, value = """ - SELECT s.id as id, s.code as code, s.name as name, s.contactNo as contactNo, + SELECT s.id as id, t.ShopCode as code, s.name as name, s.contactNo as contactNo, s.contactEmail as contactEmail, s.contactName as contactName, s.addr1 as addr1, s.addr2 as addr2, s.addr3 as addr3, s.type as type, t.TruckLanceCode as truckLanceCode, t.DepartureTime as departureTime, @@ -69,11 +69,9 @@ interface TruckRepository : AbstractRepository { t.Store_id as Store_id, t.remark as remark, t.id as truckId FROM shop s INNER JOIN truck t ON s.id = t.shopId WHERE t.TruckLanceCode = :truckLanceCode - AND ((:remark = '' AND (t.remark IS NULL OR t.remark = '')) - OR (:remark != '' AND t.remark = :remark)) AND t.deleted = false AND s.deleted = false; """ ) - fun findAllFromShopAndTruckByTruckLanceCodeAndRemarkAndDeletedFalse(@Param("truckLanceCode") truckLanceCode: String, @Param("remark") remark: String): List + fun findAllFromShopAndTruckByTruckLanceCodeAndDeletedFalse(@Param("truckLanceCode") truckLanceCode: String): List } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/TruckService.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/TruckService.kt index 1045b5c..efe1a08 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/TruckService.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/TruckService.kt @@ -250,8 +250,8 @@ open class TruckService( return truckRepository.findAllUniqueTruckLanceCodeAndRemarkCombinations() } - open fun findAllFromShopAndTruckByTruckLanceCodeAndRemarkAndDeletedFalse(truckLanceCode: String, remark: String): List { - return truckRepository.findAllFromShopAndTruckByTruckLanceCodeAndRemarkAndDeletedFalse(truckLanceCode, remark) + open fun findAllFromShopAndTruckByTruckLanceCodeAndDeletedFalse(truckLanceCode: String): List { + return truckRepository.findAllFromShopAndTruckByTruckLanceCodeAndDeletedFalse(truckLanceCode) } @Transactional diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/TruckController.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/TruckController.kt index e551c44..e68700e 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/TruckController.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/TruckController.kt @@ -182,9 +182,9 @@ class TruckController( } - @GetMapping("/findAllFromShopAndTruckByTruckLanceCodeAndRemarkAndDeletedFalse") - fun findAllFromShopAndTruckByTruckLanceCodeAndRemarkAndDeletedFalse(@RequestParam truckLanceCode: String, @RequestParam remark: String): List { - return truckService.findAllFromShopAndTruckByTruckLanceCodeAndRemarkAndDeletedFalse(truckLanceCode, remark) + @GetMapping("/findAllFromShopAndTruckByTruckLanceCodeAndDeletedFalse") + fun findAllFromShopAndTruckByTruckLanceCodeAndDeletedFalse(@RequestParam truckLanceCode: String): List { + return truckService.findAllFromShopAndTruckByTruckLanceCodeAndDeletedFalse(truckLanceCode) } @PostMapping("/updateLoadingSequence") From 27e34d71ed781eedfd3f045b32424dffad02e292 Mon Sep 17 00:00:00 2001 From: "vluk@2fi-solutions.com.hk" Date: Wed, 7 Jan 2026 19:16:51 +0800 Subject: [PATCH 5/5] no message --- .../entity/ProductionScheduleRepository.kt | 2 + .../entity/projections/ProdScheduleInfo.kt | 2 + .../service/ProductionScheduleService.kt | 105 ++++++++++++------ 3 files changed, 72 insertions(+), 37 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt index 7ffc1e0..bf2e2c7 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/ProductionScheduleRepository.kt @@ -204,6 +204,7 @@ interface ProductionScheduleRepository : AbstractRepository= today + clearTodayAndFutureProdSchedule() + println("needQtyList - " + needQtyList); + //##### The 22000, 10000 machine cap just means the max warehouse storage qty, not production qty cap //##### The total production qty of the date is 10000 due to machine cap //##### search all items with bom to consider need or no need production @@ -1613,5 +1619,30 @@ open class ProductionScheduleService( return jdbcDao.queryForList(sql, args); } + + @Transactional + open fun clearTodayAndFutureProdSchedule() { + val deleteLinesSql = """ + DELETE FROM production_schedule_line + WHERE prodScheduleId IN ( + SELECT id FROM production_schedule + WHERE DATE(produceAt) >= DATE(NOW()) + ) + """.trimIndent() + + val deleteSchedulesSql = """ + DELETE FROM production_schedule + WHERE DATE(produceAt) >= DATE(NOW()) + """.trimIndent() + + // Execute child delete first + jdbcDao.executeUpdate(deleteLinesSql) + + // Then delete parent schedules + jdbcDao.executeUpdate(deleteSchedulesSql) + + // Optional: log the action (if you have logging setup) + // logger.info("Cleared all production schedules with produceAt >= today") + } } \ No newline at end of file