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/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 abc59f5..e3b33eb 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 @@ -288,6 +288,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/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/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..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 @@ -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 @@ -200,6 +204,7 @@ interface ProductionScheduleRepository : 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/ProdScheduleInfo.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/projections/ProdScheduleInfo.kt index 5d3c34f..f1e245a 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/projections/ProdScheduleInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/projections/ProdScheduleInfo.kt @@ -18,6 +18,7 @@ interface ProdScheduleInfo { // Detailed Production Schedule With Line interface DetailedProdScheduleWithLineWithJsonString { val id: Long? + val produceAt: LocalDateTime? val scheduleAt: LocalDateTime? val totalEstProdCount: BigDecimal? val totalFGType: Long? @@ -27,6 +28,7 @@ interface DetailedProdScheduleWithLineWithJsonString { data class DetailedProdScheduleWithLine ( val id: Long?, + val produceAt: LocalDateTime?, val scheduleAt: LocalDateTime?, val totalEstProdCount: BigDecimal?, val totalFGType: Long?, 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/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/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/service/ProductionScheduleService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt index 412ec66..88eec5b 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 ) @@ -329,6 +341,7 @@ open class ProductionScheduleService( return DetailedProdScheduleWithLine( id = sqlResult.id, + produceAt = sqlResult.produceAt, scheduleAt = sqlResult.scheduleAt, totalEstProdCount = sqlResult.totalEstProdCount, totalFGType = sqlResult.totalFGType, @@ -397,57 +410,63 @@ 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) + logger.info("jobOrderDetailByItemId ok itemId:$itemId") + } catch (e: NoSuchElementException) { + //only do with no JO is working + logger.info("NoSuchElementException itemId:$itemId") + 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 - prodScheduleLine.apply { - // Use bom.outputQty, ensuring it's treated as Double for prodQty - prodQty = bom.outputQty?.toDouble() - ?: throw IllegalStateException("BOM output quantity is null for Item ID $itemId.") - approverId = approver?.id - } - - productionScheduleLineRepository.save(prodScheduleLine) - - // 5. Logging (optional but kept) - logger.info("prodScheduleLine.prodQty: ${prodScheduleLine.prodQty}") - logger.info("bom?.outputQty: ${bom.outputQty} ${bom.outputQtyUom}") - - logger.info("[releaseProdSchedule] prodScheduleLine.needNoOfJobOrder:" + prodScheduleLine.needNoOfJobOrder) - //repeat(prodScheduleLine.needNoOfJobOrder) { - // 6. Create Job Order - val joRequest = CreateJobOrderRequest( - bomId = bom.id, // bom is guaranteed non-null here - reqQty = bom.outputQty?.multiply(BigDecimal.valueOf(prodScheduleLine.batchNeed.toLong())), - approverId = approver?.id, - - // CRUCIAL FIX: Use the line ID, not the parent schedule ID - prodScheduleLineId = prodScheduleLine.id!! - ) - - // Assuming createJobOrder returns the created Job Order (jo) - val jo = jobOrderService.createJobOrder(joRequest) + // 4. Update Prod Schedule Line fields + prodScheduleLine.apply { + // Use bom.outputQty, ensuring it's treated as Double for prodQty + prodQty = bom.outputQty?.toDouble() + ?: throw IllegalStateException("BOM output quantity is null for Item ID $itemId.") + approverId = approver?.id + } - val createdJobOrderId = jo.id - ?: throw IllegalStateException("Job Order creation failed: returned object ID is null.") - - // 7. Create related job order data - jobOrderBomMaterialService.createJobOrderBomMaterialsByJoId(createdJobOrderId) - jobOrderProcessService.createJobOrderProcessesByJoId(createdJobOrderId) - productProcessService.createProductProcessByJobOrderId(createdJobOrderId, prodScheduleLine.itemPriority.toInt()) - //} + productionScheduleLineRepository.save(prodScheduleLine) + + // 5. Logging (optional but kept) + logger.info("prodScheduleLine.prodQty: ${prodScheduleLine.prodQty}") + logger.info("bom?.outputQty: ${bom.outputQty} ${bom.outputQtyUom}") + + logger.info("[releaseProdSchedule] prodScheduleLine.needNoOfJobOrder:" + prodScheduleLine.needNoOfJobOrder) + //repeat(prodScheduleLine.needNoOfJobOrder) { + // 6. Create Job Order + val joRequest = CreateJobOrderRequest( + bomId = bom.id, // bom is guaranteed non-null here + reqQty = bom.outputQty?.multiply(BigDecimal.valueOf(prodScheduleLine.batchNeed.toLong())), + approverId = approver?.id, + + // CRUCIAL FIX: Use the line ID, not the parent schedule ID + prodScheduleLineId = prodScheduleLine.id!! + ) + + // Assuming createJobOrder returns the created Job Order (jo) + val jo = jobOrderService.createJobOrder(joRequest) + + val createdJobOrderId = jo.id + ?: throw IllegalStateException("Job Order creation failed: returned object ID is null.") + + // 7. Create related job order data + jobOrderBomMaterialService.createJobOrderBomMaterialsByJoId(createdJobOrderId) + jobOrderProcessService.createJobOrderProcessesByJoId(createdJobOrderId) + productProcessService.createProductProcessByJobOrderId(createdJobOrderId, prodScheduleLine.itemPriority.toInt()) + //} + } } @@ -719,7 +738,11 @@ open class ProductionScheduleService( var machineCap = 10000.0 var needQtyList = getNeedQty() + //remove the production schedule >= 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 @@ -1338,60 +1361,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 +1468,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() } @@ -1514,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 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 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 cabcf13..7e6a056 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 @@ -51,4 +51,40 @@ fun findByShopIdAndStoreIdAndDayOfWeek( @Param("storeId") storeId: String, @Param("dayOfWeekAbbr") dayOfWeekAbbr: String ): List + + @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, 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, + 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 t.deleted = false + AND s.deleted = false; + """ + ) + 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 41c5fce..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 @@ -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 findAllFromShopAndTruckByTruckLanceCodeAndDeletedFalse(truckLanceCode: String): List { + return truckRepository.findAllFromShopAndTruckByTruckLanceCodeAndDeletedFalse(truckLanceCode) + } + + @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..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 @@ -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("/findAllFromShopAndTruckByTruckLanceCodeAndDeletedFalse") + fun findAllFromShopAndTruckByTruckLanceCodeAndDeletedFalse(@RequestParam truckLanceCode: String): List { + return truckService.findAllFromShopAndTruckByTruckLanceCodeAndDeletedFalse(truckLanceCode) + } + + @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 +)