From 6c9e773b478be337a971cb09a91a6e5cffc0e728 Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Tue, 13 Jan 2026 10:48:30 +0800 Subject: [PATCH] update stock record --- .../service/DeliveryOrderService.kt | 2 + .../web/DoPickOrderController.kt | 2 +- .../jobOrder/service/JoPickOrderService.kt | 96 +++- .../jobOrder/service/JobOrderService.kt | 1 + .../jobOrder/web/JobOrderController.kt | 5 + .../web/model/SearchJobOrderInfoRequest.kt | 17 + .../entity/PickExecutionIssueRepository.kt | 1 + .../entity/PickOrderLineRepository.kt | 1 + .../pickOrder/service/PickOrderService.kt | 2 + .../service/ProductProcessService.kt | 42 +- .../web/ProductProcessController.kt | 4 + .../web/model/SaveProductProcessRequest.kt | 15 + .../entity/InventoryLotLineRepository.kt | 8 + .../stock/entity/StockInLineRepository.kt | 24 + .../modules/stock/entity/StockInRepository.kt | 1 + .../fpsms/modules/stock/entity/StockLedger.kt | 16 +- .../stock/entity/StockLedgerRepository.kt | 44 ++ .../fpsms/modules/stock/entity/StockOut.kt | 3 +- .../stock/entity/StockOutLIneRepository.kt | 27 +- .../modules/stock/entity/StockOutLine.kt | 4 + .../stock/entity/StockOutRepository.kt | 1 + .../stock/entity/StockTakeRecordRepository.kt | 33 +- .../stock/service/StockInLineService.kt | 89 +++- .../stock/service/StockOutLineService.kt | 69 ++- .../stock/service/StockTakeRecordService.kt | 417 +++++++++++++----- .../stock/service/SuggestedPickLotService.kt | 2 + .../stock/web/StockTakeRecordController.kt | 55 ++- .../stock/web/model/SaveStockInRequest.kt | 16 + .../stock/web/model/StockTakeRecordReponse.kt | 32 ++ .../changes/20260109_enson/02_alter_table.sql | 7 + .../20260112_01_Enson/01_alter_table.sql | 8 + .../20260112_01_Enson/02_alter_table.sql | 7 + .../20260112_01_Enson/03_alter_table.sql | 7 + 33 files changed, 893 insertions(+), 165 deletions(-) create mode 100644 src/main/resources/db/changelog/changes/20260109_enson/02_alter_table.sql create mode 100644 src/main/resources/db/changelog/changes/20260112_01_Enson/01_alter_table.sql create mode 100644 src/main/resources/db/changelog/changes/20260112_01_Enson/02_alter_table.sql create mode 100644 src/main/resources/db/changelog/changes/20260112_01_Enson/03_alter_table.sql diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt index 16f5c81..fe21b4d 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt @@ -510,6 +510,7 @@ open class DeliveryOrderService( this.item = pickOrderLine.item this.status = StockOutLineStatus.PENDING.status this.qty = 0.0 + this.type = "Nor" } stockOutLineRepository.save(line) } @@ -1178,6 +1179,7 @@ open class DeliveryOrderService( StockOutLineStatus.PENDING.status // 有正常库存批次时使用 PENDING } this.qty = 0.0 + this.type = "Nor" } stockOutLineRepository.save(line) } diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DoPickOrderController.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DoPickOrderController.kt index 7894ca8..2126cdf 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DoPickOrderController.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DoPickOrderController.kt @@ -96,7 +96,7 @@ class DoPickOrderController( return doReleaseCoordinatorService.getBatchReleaseProgress(jobId) } - @GetMapping("/ticket-release-table/{startDate}/{endDate}") + @GetMapping("/ticket-release-table/{startDate}&{endDate}") fun getTicketReleaseTable( @PathVariable startDate: LocalDate, @PathVariable endDate: LocalDate diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt index b60e875..cce1b44 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt @@ -49,6 +49,7 @@ import com.ffii.fpsms.modules.jobOrder.web.model.LotDetailResponse import com.ffii.fpsms.modules.stock.entity.enum.StockInLineStatus import com.ffii.fpsms.modules.stock.entity.StockInLineRepository import java.time.LocalDate +import com.ffii.fpsms.modules.jobOrder.web.model.MaterialPickStatusItem @Service open class JoPickOrderService( private val joPickOrderRepository: JoPickOrderRepository, @@ -2249,5 +2250,98 @@ open fun deleteJoPickOrderJobOrderProductProcessPickOrder(jobOrderId: Long): Mes errorPosition = null ) } - +open fun getMaterialPickStatus(): List { + val joPickOrders = joPickOrderRepository.findAll() + + return joPickOrders + .filter { joPickOrder -> + val jobOrder = joPickOrder.jobOrderId?.let { + jobOrderRepository.findById(it).orElse(null) + } + jobOrder?.status != JobOrderStatus.COMPLETED + } + .map { joPickOrder -> + // Get related entities for additional data + val jobOrder = joPickOrder.jobOrderId?.let { + jobOrderRepository.findById(it).orElse(null) + } + val item = joPickOrder.itemId?.let { + itemsRepository.findById(it).orElse(null) + } + val pickOrder = joPickOrder.pickOrderId?.let { + pickOrderRepository.findById(it).orElse(null) + } + + // Get stock out lines for this pick order and item to get start/end times + // First get all pick order lines for this pick order + val pickOrderLines = pickOrder?.let { po -> + pickOrderLineRepository.findByPickOrderId(po.id!!) + } ?: emptyList() + + // Then get stock out lines for each pick order line that matches the item + val stockOutLines = pickOrderLines + .filter { it.item?.id == joPickOrder.itemId } + .flatMap { pol -> + stockOutLineRepository.findAllByPickOrderLineIdAndDeletedFalse(pol.id!!) + .mapNotNull { solInfo -> + // Convert StockOutLineInfo to StockOutLine entity to access startTime/endTime + // We need to fetch the actual entity + stockOutLineRepository.findById(solInfo.id).orElse(null) + } + } + + // Get earliest startTime and latest endTime from stock out lines + val pickStartTime = stockOutLines + .mapNotNull { it.startTime } + .minOrNull() + + val pickEndTime = stockOutLines + .mapNotNull { it.endTime } + .maxOrNull() + + // Count items to pick from pick order lines + val numberOfItemsToPick = pickOrder?.let { po -> + pickOrderLineRepository.findByPickOrderId(po.id!!).size + } ?: 0 + + // Count items with issues from pick execution issues + val numberOfItemsWithIssue = joPickOrder.id?.let { joPickOrderId -> + pickExecutionIssueRepository.findByPickOrderIdAndDeletedFalse(joPickOrder.pickOrderId ?: 0L) + .filter { it.joPickOrderId == joPickOrderId } + .size + } ?: 0 + + // Get job order quantity and UOM - access via bom + val jobOrderQty = jobOrder?.reqQty?.toDouble() + val uom = jobOrder?.bom?.uom?.code + + // Get item name + val itemName = item?.name + + // Determine pick status - prioritize pickOrder.status over matchStatus + val pickStatus = when { + pickOrder?.status != null -> pickOrder.status!!.value + joPickOrder.matchStatus != null -> joPickOrder.matchStatus!!.value + else -> null + } + + MaterialPickStatusItem( + id = joPickOrder.id ?: 0L, + pickOrderId = joPickOrder.pickOrderId, + pickOrderCode = joPickOrder.pickOrderCode, + jobOrderId = joPickOrder.jobOrderId, + jobOrderCode = joPickOrder.jobOrderCode, + itemId = joPickOrder.itemId, + itemCode = joPickOrder.itemCode, + itemName = itemName, + jobOrderQty = jobOrderQty, + uom = uom, + pickStartTime = pickStartTime, + pickEndTime = pickEndTime, + numberOfItemsToPick = numberOfItemsToPick, + numberOfItemsWithIssue = numberOfItemsWithIssue, + pickStatus = pickStatus + ) + } } +} \ 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 e3b33eb..efac3a7 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 @@ -508,6 +508,7 @@ open class JobOrderService( this.item = pickOrderLine.item this.status = StockOutLineStatus.PENDING.status this.qty = 0.0 + this.type = "Nor" } stockOutLineRepository.save(line) } diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt index 108b442..e655215 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt @@ -40,6 +40,7 @@ import com.ffii.fpsms.modules.jobOrder.web.model.UpdateJoPickOrderHandledByReque import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfo import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfoWithTypeName import com.ffii.fpsms.modules.jobOrder.web.model.ExportFGStockInLabelRequest +import com.ffii.fpsms.modules.jobOrder.web.model.MaterialPickStatusItem import java.time.LocalDate @RestController @@ -320,4 +321,8 @@ fun checkJobOrderCreated(@Valid @RequestBody request: CheckJobOrderCreatedReques fun updateJoReqQty(@Valid @RequestBody request: UpdateJoReqQtyRequest): MessageResponse { return jobOrderService.updateJoReqQty(request) } + @GetMapping("/material-pick-status") +fun getMaterialPickStatus(): List { + return joPickOrderService.getMaterialPickStatus() +} } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SearchJobOrderInfoRequest.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SearchJobOrderInfoRequest.kt index d3b1108..6cff306 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SearchJobOrderInfoRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SearchJobOrderInfoRequest.kt @@ -11,3 +11,20 @@ data class SearchJobOrderInfoRequest( val pageNum: Int?, val jobTypeName: String?, ) +data class MaterialPickStatusItem( + val id: Long, + val pickOrderId: Long?, + val pickOrderCode: String?, + val jobOrderId: Long?, + val jobOrderCode: String?, + val itemId: Long?, + val itemCode: String?, + val itemName: String?, + val jobOrderQty: Double?, + val uom: String?, + val pickStartTime: LocalDateTime?, + val pickEndTime: LocalDateTime?, + val numberOfItemsToPick: Int, + val numberOfItemsWithIssue: Int, + val pickStatus: String? +) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickExecutionIssueRepository.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickExecutionIssueRepository.kt index 6e5a270..a2eb52f 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickExecutionIssueRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickExecutionIssueRepository.kt @@ -74,6 +74,7 @@ fun findMaterialIssues(): List ORDER BY p.created DESC """) fun getBadItemList(): List + fun findByPickOrderId(pickOrderId: Long): List } diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLineRepository.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLineRepository.kt index f708c46..e79b31e 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLineRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLineRepository.kt @@ -22,4 +22,5 @@ fun findAllPickOrdersByItemId(@Param("itemId") itemId: Long): List """) fun findAllByPickOrderId(@Param("pickOrderId") pickOrderId: Long): List fun findAllByPickOrderIdAndDeletedFalse(pickOrderId: Long): List +fun findByPickOrderId(pickOrderId: Long): List } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt index 40399e7..b4ba1cb 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt @@ -1147,6 +1147,7 @@ open class PickOrderService( this.status = com.ffii.fpsms.modules.stock.web.model.StockOutLineStatus.PENDING.status this.qty = 0.0 + this.type = "Nor" } stockOutLIneRepository.save(line) precreated++ @@ -1945,6 +1946,7 @@ open class PickOrderService( this.status = com.ffii.fpsms.modules.stock.web.model.StockOutLineStatus.PENDING.status this.qty = 0.0 + this.type = "Nor" } stockOutLIneRepository.save(line) precreated++ diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt index 351a5bc..315931f 100644 --- a/src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt @@ -1587,5 +1587,45 @@ open class ProductProcessService( message = "ProductProcess Line Created successfully with seqNo ${originalSeqNo + 1}", errorPosition = null, ) -}} +} + +open fun getJobProcessStatus(): List { + val productProcesses = productProcessRepository.findAllByDeletedIsFalse() + .filter { it.status != ProductProcessStatus.COMPLETED } + + return productProcesses.map { process -> + val jobOrder = jobOrderRepository.findById(process.jobOrder?.id ?: 0L).orElse(null) + val lines = productProcessLineRepository.findByProductProcess_Id(process.id ?: 0L) + .sortedBy { it.seqNo } + val bom=bomRepository.findById(process.bom?.id ?: 0L).orElse(null) + val bomProcesses = bomProcessRepository.findByBomId(bom?.id ?: 0L).sortedBy { it.seqNo } + JobProcessStatusResponse( + jobOrderId = jobOrder?.id ?: 0L, + jobOrderCode = jobOrder?.code ?: "", + itemCode = process.item?.code ?: "", + itemName = process.item?.name ?: "", + planEndTime = jobOrder?.planEnd, + processes = (0 until 6).map { index -> + if (index < lines.size) { + val line = lines[index] + ProcessStatusInfo( + equipmentCode = bomProcesses[index].equipment?.code ?: "", + startTime = line.startTime, + endTime = line.endTime, + isRequired = true + ) + } else { + ProcessStatusInfo( + startTime = null, + endTime = null, + isRequired = false, + equipmentCode = null, + ) + } + } + ) + } +} + +} diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/web/ProductProcessController.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/web/ProductProcessController.kt index 6eb3e74..6ecf095 100644 --- a/src/main/java/com/ffii/fpsms/modules/productProcess/web/ProductProcessController.kt +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/web/ProductProcessController.kt @@ -225,4 +225,8 @@ class ProductProcessController( fun updateProductProcessLineProcessingTimeSetupTimeChangeoverTime(@PathVariable lineId: Long, @RequestBody request: UpdateProductProcessLineProcessingTimeSetupTimeChangeoverTimeRequest): MessageResponse { return productProcessService.UpdateProductProcessLineProcessingTimeSetupTimeChangeoverTime(request) } + @GetMapping("/Demo/JobProcessStatus") + fun getJobProcessStatus(): List { + return productProcessService.getJobProcessStatus() + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt b/src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt index 0273e8d..4bf6541 100644 --- a/src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt @@ -218,4 +218,19 @@ data class UpdateProductProcessLineProcessingTimeSetupTimeChangeoverTimeRequest( val processingTime: Int?, val setupTime: Int?, val changeoverTime: Int? +) +data class ProcessStatusInfo( + val equipmentCode: String?, + val startTime: LocalDateTime?, + val endTime: LocalDateTime?, + val isRequired: Boolean +) + +data class JobProcessStatusResponse( + val jobOrderId: Long, + val jobOrderCode: String, + val itemCode: String, + val itemName: String, + val planEndTime: LocalDateTime?, + val processes: List ) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt index 2cf5d02..2d58f7d 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt @@ -70,4 +70,12 @@ fun findAllByWarehouseCodeAndDeletedIsFalse(@Param("warehouseCode") warehouseCod AND ill.deleted = false """) fun countDistinctItemsByWarehouseIds(@Param("warehouseIds") warehouseIds: List): Long + +@Query(""" + SELECT COUNT(ill.id) + FROM InventoryLotLine ill + WHERE ill.warehouse.id IN :warehouseIds + AND ill.deleted = false +""") +fun countAllByWarehouseIds(@Param("warehouseIds") warehouseIds: List): Long } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLineRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLineRepository.kt index 12ac003..3f9c3c7 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLineRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLineRepository.kt @@ -7,6 +7,9 @@ import org.springframework.data.jpa.repository.Query import org.springframework.stereotype.Repository import java.util.Optional import java.time.LocalDate +import org.springframework.data.repository.query.Param + + @Repository interface StockInLineRepository : AbstractRepository { fun findAllStockInLineInfoByStockInIdAndDeletedFalse(stockInId: Long): List @@ -31,4 +34,25 @@ interface StockInLineRepository : AbstractRepository { AND sil.deleted = false """) fun findByReceiptDateAndDeletedFalse(date: LocalDate): List +@Query(""" + SELECT sil FROM StockInLine sil + WHERE sil.deleted = false + AND (:type IS NULL OR sil.type IS NOT NULL) + AND (:startDate IS NULL OR DATE(sil.created) >= :startDate) + AND (:endDate IS NULL OR DATE(sil.created) <= :endDate) + AND (:itemCode IS NULL OR sil.item.code LIKE CONCAT('%', :itemCode, '%')) + AND (:itemName IS NULL OR sil.item.name LIKE CONCAT('%', :itemName, '%')) + AND (:type IS NULL OR sil.type = :type) + ORDER BY sil.created DESC +""") +fun searchStockInLines( + @Param("startDate") startDate: LocalDate?, + @Param("endDate") endDate: LocalDate?, + @Param("itemCode") itemCode: String?, + @Param("itemName") itemName: String?, + @Param("type") type: String? +): List +//AND sil.type IS NOT NULL +@Query("SELECT sil FROM StockInLine sil WHERE sil.item.id IN :itemIds AND sil.deleted = false") +fun findAllByItemIdInAndDeletedFalse(itemIds: List): List } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInRepository.kt index 828e15d..6181870 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInRepository.kt @@ -21,4 +21,5 @@ interface StockInRepository : AbstractRepository { select si.code from StockIn si where si.code like :prefix% and si.deleted = false order by si.code desc limit 1 """) fun findLatestCodeByPrefix(prefix: String): String? + } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockLedger.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockLedger.kt index a591910..bbd3790 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockLedger.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockLedger.kt @@ -7,13 +7,13 @@ import jakarta.persistence.JoinColumn import jakarta.persistence.OneToOne import jakarta.persistence.Table import jakarta.validation.constraints.NotNull - +import java.time.LocalDate @Entity @Table(name = "stock_ledger") open class StockLedger: BaseEntity() { -// @OneToOne -// @JoinColumn(name = "stockInLineId") -// open var stockInLine: StockInLine? = null + @OneToOne + @JoinColumn(name = "stockInLineId") + open var stockInLine: StockInLine? = null @OneToOne @JoinColumn(name = "stockOutLineId") @@ -32,4 +32,12 @@ open class StockLedger: BaseEntity() { @Column(name = "balance") open var balance: Double? = null + @Column(name = "type") + open var type: String? = null + @Column(name = "itemId") + open var itemId: Long? = null + @Column(name = "itemCode") + open var itemCode: String? = null + @Column(name = "date") + open var date: LocalDate? = null } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockLedgerRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockLedgerRepository.kt index 8fb12c7..04e4bc7 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockLedgerRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockLedgerRepository.kt @@ -1,8 +1,52 @@ package com.ffii.fpsms.modules.stock.entity import com.ffii.core.support.AbstractRepository +import org.springframework.data.jpa.repository.Query +import org.springframework.data.repository.query.Param import org.springframework.stereotype.Repository +import java.time.LocalDate @Repository interface StockLedgerRepository: AbstractRepository { + + @Query(""" + SELECT sl FROM StockLedger sl + LEFT JOIN sl.stockInLine sil + LEFT JOIN sl.stockOutLine sol + LEFT JOIN sl.inventory inv + LEFT JOIN inv.item i + WHERE sl.deleted = false + AND (:itemCode IS NULL OR sl.itemCode LIKE CONCAT('%', :itemCode, '%')) + AND (:itemName IS NULL OR i.name LIKE CONCAT('%', :itemName, '%')) + AND (:type IS NULL OR sl.type = :type) + AND (:startDate IS NULL OR DATE(sl.created) >= :startDate) + AND (:endDate IS NULL OR DATE(sl.created) <= :endDate) + ORDER BY sl.created ASC, sl.itemId + """) + fun findStockTransactions( + @Param("itemCode") itemCode: String?, + @Param("itemName") itemName: String?, + @Param("type") type: String?, + @Param("startDate") startDate: LocalDate?, + @Param("endDate") endDate: LocalDate? + ): List + + @Query(""" + SELECT COUNT(sl) FROM StockLedger sl + LEFT JOIN sl.inventory inv + LEFT JOIN inv.item i + WHERE sl.deleted = false + AND (:itemCode IS NULL OR sl.itemCode LIKE CONCAT('%', :itemCode, '%')) + AND (:itemName IS NULL OR i.name LIKE CONCAT('%', :itemName, '%')) + AND (:type IS NULL OR sl.type = :type) + AND (:startDate IS NULL OR DATE(sl.created) >= :startDate) + AND (:endDate IS NULL OR DATE(sl.created) <= :endDate) + """) + fun countStockTransactions( + @Param("itemCode") itemCode: String?, + @Param("itemName") itemName: String?, + @Param("type") type: String?, + @Param("startDate") startDate: LocalDate?, + @Param("endDate") endDate: LocalDate? + ): Long } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOut.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOut.kt index 24d7c99..a57eb58 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOut.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOut.kt @@ -34,5 +34,6 @@ open class StockOut: BaseEntity(){ @Column(name = "remarks") open var remarks: String? = null - + @Column(name = "stockTakeId") + open var stockTakeId: Long? = null } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutLIneRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutLIneRepository.kt index aca6f31..507a16c 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutLIneRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutLIneRepository.kt @@ -4,6 +4,9 @@ import com.ffii.core.support.AbstractRepository import com.ffii.fpsms.modules.stock.entity.projection.StockOutLineInfo import com.ffii.fpsms.modules.stock.web.model.StockOutStatus import org.springframework.stereotype.Repository +import org.springframework.data.repository.query.Param +import java.time.LocalDate +import org.springframework.data.jpa.repository.Query @Repository interface StockOutLIneRepository: AbstractRepository { @@ -21,5 +24,27 @@ interface StockOutLIneRepository: AbstractRepository { inventoryLotLineId: Long ): List fun existsByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(pickOrderLineId: Long, inventoryLotLineId: Long): Boolean - //fun findAllByPickOrderLineIdAndDeletedFalse(pickOrderLineId: Long): List + @Query(""" + SELECT sol FROM StockOutLine sol + WHERE sol.deleted = false + AND sol.type IS NOT NULL + AND (:startDate IS NULL OR DATE(sol.created) >= :startDate) + AND (:endDate IS NULL OR DATE(sol.created) <= :endDate) + AND (:itemCode IS NULL OR sol.item.code LIKE %:itemCode%) + AND (:itemName IS NULL OR sol.item.name LIKE %:itemName%) + AND (:type IS NULL OR sol.type = :type) + ORDER BY sol.created DESC +""") +fun searchStockOutLines( + @Param("startDate") startDate: LocalDate?, + @Param("endDate") endDate: LocalDate?, + @Param("itemCode") itemCode: String?, + @Param("itemName") itemName: String?, + @Param("type") type: String? +): List + @Query("SELECT sol FROM StockOutLine sol WHERE sol.item.id = :itemId AND sol.deleted = false") + fun findAllByItemIdAndDeletedFalse(itemId: Long): List + + @Query("SELECT sol FROM StockOutLine sol WHERE sol.item.id IN :itemIds AND sol.deleted = false") +fun findAllByItemIdInAndDeletedFalse(itemIds: List): List } diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutLine.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutLine.kt index f295aca..1370407 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutLine.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutLine.kt @@ -48,4 +48,8 @@ open class StockOutLine: BaseEntity() { @Column(name = "type") open var type: String? = null + @Column(name = "startTime") + open var startTime: LocalDateTime? = null + @Column(name = "endTime") + open var endTime: LocalDateTime? = null } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutRepository.kt index 07ee7d4..1878207 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutRepository.kt @@ -7,4 +7,5 @@ import java.util.Optional @Repository interface StockOutRepository: AbstractRepository { fun findByConsoPickOrderCode(consoPickOrderCode: String) : Optional + fun findByStockTakeIdAndDeletedFalse(stockTakeId: Long): StockOut? } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRecordRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRecordRepository.kt index b7176ad..4b23a13 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRecordRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRecordRepository.kt @@ -5,20 +5,31 @@ import com.ffii.core.support.AbstractRepository import org.springframework.stereotype.Repository import java.io.Serializable import org.springframework.data.jpa.repository.Query +import org.springframework.data.repository.query.Param +import java.time.LocalDate @Repository interface StockTakeRecordRepository : AbstractRepository { fun findAllByStockTakeIdAndDeletedIsFalse(stockTakeId: Long): List; fun findByIdAndDeletedIsFalse(id: Serializable): StockTakeRecord?; + @Query(""" - SELECT w.stockTakeSection, str.stockTake.id, COUNT(str.id) as count - FROM StockTakeRecord str - INNER JOIN Warehouse w ON str.warehouse.id = w.id - WHERE str.deleted = false - AND w.deleted = false - AND w.stockTakeSection IS NOT NULL - AND w.stockTakeSection != '' - AND str.stockTake.id IS NOT NULL - GROUP BY w.stockTakeSection, str.stockTake.id + SELECT sl FROM StockLedger sl + LEFT JOIN sl.stockInLine sil + LEFT JOIN sl.stockOutLine sol + LEFT JOIN sl.inventory inv + LEFT JOIN inv.item i + WHERE sl.deleted = false + AND (:itemCode IS NULL OR sl.itemCode LIKE CONCAT('%', :itemCode, '%')) + AND (:itemName IS NULL OR i.name LIKE CONCAT('%', :itemName, '%')) + AND (:type IS NULL OR sl.type = :type) + AND (:startDate IS NULL OR DATE(sl.created) >= :startDate) + AND (:endDate IS NULL OR DATE(sl.created) <= :endDate) + ORDER BY COALESCE(sl.date, DATE(sl.created)) ASC, sl.created ASC, sl.itemId """) -fun countStockTakeRecordsBySectionAndStockTakeId(): List> -} \ No newline at end of file + fun countStockTakeRecordsBySectionAndStockTakeId(): List> + + + + +} + diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt index c66ea52..00270f3 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt @@ -48,7 +48,9 @@ import java.io.File import kotlin.jvm.optionals.getOrNull import com.ffii.fpsms.modules.deliveryOrder.entity.DeliveryOrderRepository import com.ffii.fpsms.modules.deliveryOrder.web.models.PrintQrCodeForDoRequest - +import com.ffii.fpsms.modules.stock.service.InventoryLotLineService +import com.ffii.fpsms.modules.stock.entity.StockLedgerRepository +import com.ffii.fpsms.modules.stock.entity.InventoryRepository @Serializable data class QrContent(val itemId: Long, val stockInLineId: Long) @@ -72,7 +74,10 @@ open class StockInLineService( private val itemUomRepository: ItemUomRespository, private val printerService: PrinterService, private val stockTakeLineRepository: StockTakeLineRepository, + private val inventoryLotLineService: InventoryLotLineService, private val deliveryOrderRepository: DeliveryOrderRepository, + private val stockLedgerRepository: StockLedgerRepository, + private val inventoryRepository: InventoryRepository ): AbstractBaseEntityService(jdbcDao, stockInLineRepository) { open fun getStockInLineInfo(stockInLineId: Long): StockInLineInfo { @@ -113,7 +118,7 @@ open class StockInLineService( // stockIn = stockInService.create(SaveStockInRequest(purchaseOrderId = request.purchaseOrderId)).entity as StockIn // var stockIn = stockInRepository.findByPurchaseOrderIdAndDeletedFalse(request.purchaseOrderId) } - + createStockLedgerForStockIn(stockInLine) val item = itemRepository.findById(request.itemId).orElseThrow() // If request contains valid POL if (pol != null) { @@ -153,6 +158,7 @@ open class StockInLineService( // if (pol.qty!! < request.acceptedQty) { // throw BadRequestException() // } + stockInLine.apply { this.item = item itemNo = item.code @@ -162,6 +168,7 @@ open class StockInLineService( if (jo != null && jo?.bom != null) { // For job orders, demandQty comes from BOM's outputQty this.demandQty = jo?.bom?.outputQty + } else if (pol != null) { // For purchase orders, demandQty comes from PurchaseOrderLine's qty this.demandQty = pol.qty @@ -169,6 +176,7 @@ open class StockInLineService( dnNo = request.dnNo receiptDate = request.receiptDate?.atStartOfDay() ?: LocalDateTime.now() productLotNo = request.productLotNo + this.type = "Nor" status = StockInLineStatus.PENDING.status } val savedSIL = saveAndFlush(stockInLine) @@ -398,6 +406,7 @@ open class StockInLineService( } // TODO: check all status to prevent reverting progress due to multiple users access to the same po? // return list of stock in line, update data grid with the list + createStockLedgerForStockIn(stockInLine) stockInLine.apply { this.productionDate = request.productionDate?.atStartOfDay() ?: this.productionDate this.productLotNo = request.productLotNo ?: this.productLotNo @@ -646,4 +655,80 @@ open class StockInLineService( } } } + open fun newStockInCreate(request: NewStockInCreateRequest): MessageResponse { + val newLotNo = if (request.lotNo == null) assignLotNo() else request.lotNo + val inventoryLotLine = inventoryLotLineRepository.findByIdAndDeletedIsFalse( + request.inventoryLotLineId ?: throw IllegalArgumentException("Inventory lot ID not found") + ) ?: throw IllegalArgumentException("Inventory lot line not found") + val inventoryLot = inventoryLotRepository.findByIdAndDeletedFalse( + inventoryLotLine?.inventoryLot?.id ?: throw IllegalArgumentException("Inventory lot ID not found") + ) ?: throw IllegalArgumentException("Inventory lot not found") + + val stockIn=StockIn().apply { + this.code=stockTake?.code + this.status=request.stockIntype + //this.stockTake=stockTake + } + stockInRepository.save(stockIn) + val stockInLine = StockInLine().apply { + //this.stockTakeLine=stockTakeLine + this.item=inventoryLot.item + this.itemNo=request.itemNo + this.stockIn = stockIn + this.demandQty=request.demandQty + this.acceptedQty=request.acceptedQty + this.expiryDate=inventoryLot.expiryDate + this.inventoryLot=inventoryLot + this.inventoryLotLine=inventoryLotLine + this.lotNo=newLotNo + this.status = "completed" + this.type = request.stockInLineType + + + } + stockInLineRepository.save(stockInLine) + val updateRequest = SaveInventoryLotLineRequest( + id = inventoryLotLine.id, + inventoryLotId = inventoryLotLine.inventoryLot?.id, + warehouseId = inventoryLotLine.warehouse?.id, + stockUomId = inventoryLotLine.stockUom?.id, + inQty = request.acceptedQty, + outQty = inventoryLotLine.outQty, + holdQty = inventoryLotLine.holdQty, + status = inventoryLotLine.status?.value, + remarks = inventoryLotLine.remarks + ) + inventoryLotLineService.saveInventoryLotLine(updateRequest) + return MessageResponse( + id = stockInLine.id, + code = null, + name = stockInLine.item?.name, + type = "success", + message = "Stock in line created successfully", + errorPosition = null + ) + } + @Transactional + private fun createStockLedgerForStockIn(stockInLine: StockInLine) { + val item = stockInLine.item ?: return + val inventory = inventoryRepository.findByItemId(item.id!!).orElse(null) ?: return + + val inQty = stockInLine.acceptedQty?.toDouble() ?: 0.0 + // 直接使用 inventory.onHandQty 作为 balance(已经是更新后的值) + val newBalance = (inventory.onHandQty ?: BigDecimal.ZERO).toDouble() + + val stockLedger = StockLedger().apply { + this.stockInLine = stockInLine + this.inventory = inventory + this.inQty = inQty + this.outQty = null + this.balance = newBalance + this.type = stockInLine.type + this.itemId = item.id + this.itemCode = item.code + this.date = LocalDate.now() + } + + stockLedgerRepository.saveAndFlush(stockLedger) + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt index c7cea19..f4a5840 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt @@ -37,6 +37,8 @@ import com.ffii.fpsms.modules.bag.web.model.CreateBagLotLineRequest import com.ffii.fpsms.modules.common.CodeGenerator import org.springframework.context.annotation.Lazy import com.ffii.fpsms.modules.bag.service.BagService +import com.ffii.fpsms.modules.stock.entity.StockLedgerRepository +import com.ffii.fpsms.modules.stock.entity.InventoryRepository import java.time.LocalTime @Service @@ -46,7 +48,6 @@ open class StockOutLineService( private val stockOutRepository: StockOutRepository, private val stockOutLineRepository: StockOutLIneRepository, private val itemRepository: ItemsRepository, - private val inventoryRepository: InventoryRepository, private val itemUomRespository: ItemUomRespository, private val pickOrderRepository: PickOrderRepository, private val inventoryLotLineRepository: InventoryLotLineRepository, @@ -59,7 +60,9 @@ private val deliveryOrderRepository: DeliveryOrderRepository, private val doPickOrderLineRepository: DoPickOrderLineRepository, private val doPickOrderLineRecordRepository: DoPickOrderLineRecordRepository, private val inventoryLotLineService: InventoryLotLineService, - private val bagService: BagService + private val bagService: BagService, + private val stockLedgerRepository: StockLedgerRepository, + private val inventoryRepository: InventoryRepository ): AbstractBaseEntityService(jdbcDao, stockOutLineRepository) { @Throws(IOException::class) @Transactional @@ -135,6 +138,7 @@ val existingStockOutLine = stockOutLineRepository.findByPickOrderLineIdAndInvent ) val item = itemRepository.findById(pickOrderLine.item!!.id!!).orElseThrow() val inventoryLotLine = inventoryLotLineRepository.findById(request.inventoryLotLineId).orElseThrow() + val stockOutLine = StockOutLine() .apply { this.item = item @@ -143,8 +147,10 @@ val existingStockOutLine = stockOutLineRepository.findByPickOrderLineIdAndInvent this.inventoryLotLine = inventoryLotLine this.pickOrderLine = pickOrderLine this.status = StockOutLineStatus.PENDING.status + this.type = "Nor" } val savedStockOutLine = saveAndFlush(stockOutLine) + createStockLedgerForStockOut(savedStockOutLine) val mappedSavedStockOutLine = stockOutLineRepository.findStockOutLineInfoById(savedStockOutLine.id!!) // println("triggering") return MessageResponse( @@ -231,7 +237,7 @@ open fun createWithoutConso(request: CreateStockOutLineWithoutConsoRequest): Mes IllegalArgumentException("InventoryLotLine not found with ID: ${request.inventoryLotLineId}") } println("Found inventoryLotLine: ${inventoryLotLine.id}") - + val stockOutLine = StockOutLine() .apply { this.item = item @@ -240,10 +246,12 @@ open fun createWithoutConso(request: CreateStockOutLineWithoutConsoRequest): Mes this.inventoryLotLine = inventoryLotLine this.pickOrderLine = updatedPickOrderLine this.status = StockOutLineStatus.PENDING.status + this.type = "Nor" } println("Created stockOutLine with qty: ${request.qty}") val savedStockOutLine = saveAndFlush(stockOutLine) + createStockLedgerForStockOut(savedStockOutLine) println("Saved stockOutLine with ID: ${savedStockOutLine.id}") val mappedSavedStockOutLine = stockOutLineRepository.findStockOutLineInfoById(savedStockOutLine.id!!) @@ -536,7 +544,12 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long { println("Updating StockOutLine ID: ${request.id}") println("Current status: ${stockOutLine.status}") println("New status: ${request.status}") - + if (request.status == "checked") { + stockOutLine.startTime = LocalDateTime.now() + } + if (request.status == "completed") { + stockOutLine.endTime = LocalDateTime.now() + } // 2. 更新自身 status/qty stockOutLine.status = request.status if (request.qty != null) { @@ -582,14 +595,14 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long { bagService.createBagLotLinesByBagId(createBagLotLineRequest) println(" ✓ BagLotLine created successfully for item ${item.code}") } else { - println(" ⚠️ Warning: lotNo is null, skipping BagLotLine creation") + println(" Warning: lotNo is null, skipping BagLotLine creation") } } else { - println(" ⚠️ Warning: Bag not found for itemId ${item.id}, skipping BagLotLine creation") + println(" Warning: Bag not found for itemId ${item.id}, skipping BagLotLine creation") } } } catch (e: Exception) { - println(" ⚠️ Error creating BagLotLine: ${e.message}") + println(" Error creating BagLotLine: ${e.message}") e.printStackTrace() // 不中断主流程,只记录错误 } @@ -891,7 +904,7 @@ open fun updateStockOutLineStatusByQRCodeAndLotNo(request: UpdateStockOutLineSta ) } - println("🔍 Checking inventory lot:") + println(" Checking inventory lot:") println(" - Found inventory lot:") println(" - Lot No: ${inventoryLot.lotNo}") println(" - Item ID: ${inventoryLot.item?.id}") @@ -906,10 +919,16 @@ open fun updateStockOutLineStatusByQRCodeAndLotNo(request: UpdateStockOutLineSta println(" - Item ID match: $itemIdMatch (Expected: ${inventoryLot.item?.id}, Got: ${request.itemId})") if (lotNoMatch && itemIdMatch) { - // 匹配成功,更新状态 + println(" MATCH SUCCESS: Lot and Item both match!") stockOutLine.status = request.status + if (request.status == "checked") { + stockOutLine.startTime = LocalDateTime.now() + } + if (request.status == "completed") { + stockOutLine.endTime = LocalDateTime.now() + } val savedStockOutLine = stockOutLineRepository.saveAndFlush(stockOutLine) println(" Status updated successfully:") @@ -1099,14 +1118,14 @@ open fun newBatchSubmit(request: QrPickBatchSubmitRequest): MessageResponse { bagService.createBagLotLinesByBagId(createBagLotLineRequest) println(" ✓ BagLotLine created successfully for item ${item.code}") } else { - println(" ⚠️ Warning: lotNo is null, skipping BagLotLine creation") + println(" Warning: lotNo is null, skipping BagLotLine creation") } } else { - println(" ⚠️ Warning: Bag not found for itemId ${item.id}, skipping BagLotLine creation") + println(" Warning: Bag not found for itemId ${item.id}, skipping BagLotLine creation") } } } catch (e: Exception) { - println(" ⚠️ Error creating BagLotLine: ${e.message}") + println(" Error creating BagLotLine: ${e.message}") e.printStackTrace() // 不中断主流程,只记录错误 } @@ -1165,4 +1184,28 @@ open fun newBatchSubmit(request: QrPickBatchSubmitRequest): MessageResponse { ) ) } -}} \ No newline at end of file +} +@Transactional +private fun createStockLedgerForStockOut(stockOutLine: StockOutLine) { + val item = stockOutLine.item ?: return + val inventory = inventoryRepository.findByItemId(item.id!!).orElse(null) ?: return + + val outQty = stockOutLine.qty?.toDouble() ?: 0.0 + // 直接使用 inventory.onHandQty 作为 balance(已经是更新后的值) + val newBalance = (inventory.onHandQty ?: BigDecimal.ZERO).toDouble() + + val stockLedger = StockLedger().apply { + this.stockOutLine = stockOutLine + this.inventory = inventory + this.inQty = null + this.outQty = outQty + this.balance = newBalance + this.type = stockOutLine.type + this.itemId = item.id + this.itemCode = item.code + this.date = LocalDate.now() + } + + stockLedgerRepository.saveAndFlush(stockLedger) +} +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeRecordService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeRecordService.kt index bc14fac..75b217d 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeRecordService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeRecordService.kt @@ -14,7 +14,9 @@ import org.springframework.stereotype.Service import java.time.LocalDateTime import java.math.BigDecimal import com.ffii.fpsms.modules.user.entity.UserRepository - +import org.springframework.data.domain.PageRequest +import com.ffii.core.response.RecordsRes +import java.time.LocalDate import com.ffii.fpsms.modules.stock.service.InventoryLotLineService import com.ffii.fpsms.modules.stock.entity.StockTakeLine import com.ffii.fpsms.modules.stock.entity.StockTakeLineRepository @@ -29,6 +31,16 @@ import com.ffii.fpsms.modules.stock.entity.StockInLine import com.ffii.fpsms.modules.stock.entity.StockInLineRepository import com.ffii.fpsms.modules.stock.entity.InventoryLot import com.ffii.fpsms.modules.stock.entity.InventoryLotRepository +import jakarta.persistence.EntityManager +import jakarta.persistence.PersistenceContext +import org.hibernate.Session +import org.hibernate.jdbc.Work +import java.sql.Connection +import java.sql.PreparedStatement +import java.sql.ResultSet +import com.ffii.fpsms.modules.stock.entity.StockLedgerRepository +import com.ffii.fpsms.modules.stock.entity.InventoryRepository +import com.ffii.fpsms.modules.stock.entity.StockLedger @Service class StockTakeRecordService( val stockTakeRepository: StockTakeRepository, @@ -42,7 +54,10 @@ class StockTakeRecordService( val stockOutLineRepository: StockOutLIneRepository, val stockInRepository: StockInRepository, val stockInLineRepository: StockInLineRepository, - val inventoryLotRepository: InventoryLotRepository + val inventoryLotRepository: InventoryLotRepository, + val stockLedgerRepository: StockLedgerRepository, + val inventoryRepository: InventoryRepository + ) { private val logger: Logger = LoggerFactory.getLogger(StockTakeRecordService::class.java) @@ -138,6 +153,7 @@ class StockTakeRecordService( } // 9. 计算 TotalItemNumber:获取该 section 下所有 InventoryLotLine,按 item 分组,计算不同的 item 数量 val totalItemNumber = inventoryLotLineRepository.countDistinctItemsByWarehouseIds(warehouseIds).toInt() + val totalInventoryLotNumber = inventoryLotLineRepository.countAllByWarehouseIds(warehouseIds).toInt() // 8. 使用 stockTakeSection 作为 stockTakeSession val reStockTakeTrueFalse = if (latestStockTake != null) { // 检查该 stock take 下该 section 的记录中是否有 notMatch 状态 @@ -158,7 +174,7 @@ class StockTakeRecordService( lastStockTakeDate = latestStockTake?.actualStart?.toLocalDate(), status = status?:"", currentStockTakeItemNumber = 0, - totalInventoryLotNumber = 0, + totalInventoryLotNumber = totalInventoryLotNumber, stockTakeId = latestStockTake?.id ?: 0, stockTakerName = stockTakerName, TotalItemNumber = totalItemNumber, @@ -286,6 +302,7 @@ class StockTakeRecordService( } // 9. 计算 TotalItemNumber:获取该 section 下所有 InventoryLotLine,按 item 分组,计算不同的 item 数量 val totalItemNumber = inventoryLotLineRepository.countDistinctItemsByWarehouseIds(warehouseIds).toInt() + val totalInventoryLotNumber = inventoryLotLineRepository.countAllByWarehouseIds(warehouseIds).toInt() // 9. 使用 stockTakeSection 作为 stockTakeSession result.add( AllPickedStockTakeListReponse( @@ -294,7 +311,7 @@ class StockTakeRecordService( lastStockTakeDate = latestStockTake?.actualStart?.toLocalDate(), status = status?:"", currentStockTakeItemNumber = 0, // 临时设为 0,测试性能 - totalInventoryLotNumber = 0, // 临时设为 0,测试性能 + totalInventoryLotNumber = totalInventoryLotNumber, // 临时设为 0,测试性能 stockTakeId = latestStockTake?.id ?: 0, stockTakerName = stockTakerName, approverName = approverName, @@ -376,92 +393,103 @@ class StockTakeRecordService( ) } } - open fun getInventoryLotDetailsByStockTakeSection(stockTakeSection: String, stockTakeId: Long? = null): List { - println("getInventoryLotDetailsByStockTakeSection called with section: $stockTakeSection, stockTakeId: $stockTakeId") - - - val warehouses = warehouseRepository.findAllByStockTakeSectionAndDeletedIsFalse(stockTakeSection) - if (warehouses.isEmpty()) { - logger.warn("No warehouses found for stockTakeSection: $stockTakeSection") - return emptyList() - } - - val warehouseIds = warehouses.mapNotNull { it.id } - println("Found ${warehouses.size} warehouses for section $stockTakeSection") - - - val inventoryLotLines = inventoryLotLineRepository.findAllByWarehouseIdInAndDeletedIsFalse(warehouseIds) - println("Found ${inventoryLotLines.size} inventory lot lines") - - - val stockTakeRecordsMap = if (stockTakeId != null) { - val allStockTakeRecords = stockTakeRecordRepository.findAll() - .filter { - !it.deleted && - it.stockTake?.id == stockTakeId && - it.warehouse?.id in warehouseIds - } - // 按 lotId 和 warehouseId 建立映射 - allStockTakeRecords.associateBy { - Pair(it.lotId ?: 0L, it.warehouse?.id ?: 0L) + open fun getInventoryLotDetailsByStockTakeSection( + stockTakeSection: String, + stockTakeId: Long? = null, + pageNum: Int = 0, + pageSize: Int = 10 +): RecordsRes { + println("getInventoryLotDetailsByStockTakeSection called with section: $stockTakeSection, stockTakeId: $stockTakeId, pageNum: $pageNum, pageSize: $pageSize") + + val warehouses = warehouseRepository.findAllByStockTakeSectionAndDeletedIsFalse(stockTakeSection) + if (warehouses.isEmpty()) { + logger.warn("No warehouses found for stockTakeSection: $stockTakeSection") + return RecordsRes(emptyList(), 0) + } + + val warehouseIds = warehouses.mapNotNull { it.id } + println("Found ${warehouses.size} warehouses for section $stockTakeSection") + + val inventoryLotLines = inventoryLotLineRepository.findAllByWarehouseIdInAndDeletedIsFalse(warehouseIds) + println("Found ${inventoryLotLines.size} inventory lot lines") + + val stockTakeRecordsMap = if (stockTakeId != null) { + val allStockTakeRecords = stockTakeRecordRepository.findAll() + .filter { + !it.deleted && + it.stockTake?.id == stockTakeId && + it.warehouse?.id in warehouseIds } - } else { - emptyMap() + // 按 lotId 和 warehouseId 建立映射 + allStockTakeRecords.associateBy { + Pair(it.lotId ?: 0L, it.warehouse?.id ?: 0L) } + } else { + emptyMap() + } + + val allResults = inventoryLotLines.map { ill -> + val inventoryLot = ill.inventoryLot + val item = inventoryLot?.item + val warehouse = ill.warehouse + val availableQty = (ill.inQty ?: BigDecimal.ZERO) + .subtract(ill.outQty ?: BigDecimal.ZERO) + .subtract(ill.holdQty ?: BigDecimal.ZERO) - - return inventoryLotLines.map { ill -> - val inventoryLot = ill.inventoryLot - val item = inventoryLot?.item - val warehouse = ill.warehouse - val availableQty = (ill.inQty ?: BigDecimal.ZERO) - .subtract(ill.outQty ?: BigDecimal.ZERO) - .subtract(ill.holdQty ?: BigDecimal.ZERO) - - - val stockTakeRecord = if (stockTakeId != null && inventoryLot?.id != null && warehouse?.id != null) { - stockTakeRecordsMap[Pair(inventoryLot.id, warehouse.id)] - } else { - null - } - val inventoryLotLineId = ill.id - val stockTakeLine = stockTakeLineRepository.findByInventoryLotLineIdAndStockTakeIdAndDeletedIsFalse(inventoryLotLineId, stockTakeId!!) - InventoryLotDetailResponse( - id = ill.id ?: 0L, - inventoryLotId = inventoryLot?.id ?: 0L, - itemId = item?.id ?: 0L, - itemCode = item?.code, - itemName = item?.name, - lotNo = inventoryLot?.lotNo, - expiryDate = inventoryLot?.expiryDate, - productionDate = inventoryLot?.productionDate, - stockInDate = inventoryLot?.stockInDate, - inQty = ill.inQty, - remarks = stockTakeRecord?.remarks, - outQty = ill.outQty, - holdQty = ill.holdQty, - availableQty = availableQty, - uom = ill.stockUom?.uom?.udfudesc, - warehouseCode = warehouse?.code, - warehouseName = warehouse?.name, - status = ill.status?.name, - warehouseSlot = warehouse?.slot, - warehouseArea = warehouse?.area, - warehouse = warehouse?.warehouse, - varianceQty = stockTakeRecord?.varianceQty, - stockTakeRecordId = stockTakeRecord?.id, - stockTakeRecordStatus = stockTakeRecord?.status, - firstStockTakeQty = stockTakeRecord?.pickerFirstStockTakeQty, - secondStockTakeQty = stockTakeRecord?.pickerSecondStockTakeQty, - firstBadQty = stockTakeRecord?.pickerFirstBadQty, - secondBadQty = stockTakeRecord?.pickerSecondBadQty, - approverQty = stockTakeRecord?.approverStockTakeQty , - approverBadQty = stockTakeRecord?.approverBadQty, - finalQty = stockTakeLine?.finalQty, - //finalQty = null, - ) + val stockTakeRecord = if (stockTakeId != null && inventoryLot?.id != null && warehouse?.id != null) { + stockTakeRecordsMap[Pair(inventoryLot.id, warehouse.id)] + } else { + null } + val inventoryLotLineId = ill.id + val stockTakeLine = stockTakeLineRepository.findByInventoryLotLineIdAndStockTakeIdAndDeletedIsFalse(inventoryLotLineId, stockTakeId!!) + InventoryLotDetailResponse( + id = ill.id ?: 0L, + inventoryLotId = inventoryLot?.id ?: 0L, + itemId = item?.id ?: 0L, + itemCode = item?.code, + itemName = item?.name, + lotNo = inventoryLot?.lotNo, + expiryDate = inventoryLot?.expiryDate, + productionDate = inventoryLot?.productionDate, + stockInDate = inventoryLot?.stockInDate, + inQty = ill.inQty, + remarks = stockTakeRecord?.remarks, + outQty = ill.outQty, + holdQty = ill.holdQty, + availableQty = availableQty, + uom = ill.stockUom?.uom?.udfudesc, + warehouseCode = warehouse?.code, + warehouseName = warehouse?.name, + status = ill.status?.name, + warehouseSlot = warehouse?.slot, + warehouseArea = warehouse?.area, + warehouse = warehouse?.warehouse, + varianceQty = stockTakeRecord?.varianceQty, + stockTakeRecordId = stockTakeRecord?.id, + stockTakeRecordStatus = stockTakeRecord?.status, + firstStockTakeQty = stockTakeRecord?.pickerFirstStockTakeQty, + secondStockTakeQty = stockTakeRecord?.pickerSecondStockTakeQty, + firstBadQty = stockTakeRecord?.pickerFirstBadQty, + secondBadQty = stockTakeRecord?.pickerSecondBadQty, + approverQty = stockTakeRecord?.approverStockTakeQty , + approverBadQty = stockTakeRecord?.approverBadQty, + finalQty = stockTakeLine?.finalQty, + ) } + + // Apply pagination + val pageable = PageRequest.of(pageNum, pageSize) + val startIndex = pageable.offset.toInt() + val endIndex = minOf(startIndex + pageSize, allResults.size) + val paginatedResult = if (startIndex < allResults.size) { + allResults.subList(startIndex, endIndex) + } else { + emptyList() + } + + return RecordsRes(paginatedResult, allResults.size) +} open fun saveStockTakeRecord( request: SaveStockTakeRecordRequest, stockTakeId: Long, @@ -750,30 +778,32 @@ open fun checkAndUpdateStockTakeStatus(stockTakeId: Long, stockTakeSection: Stri val allRecordsCompleted = stockTakeRecords.isNotEmpty() && stockTakeRecords.all { it.status == "completed" } // 6. 如果所有记录都已创建且都是 "pass",更新 stock take 状态为 "approving" - if (allLinesHaveRecords && allRecordsPassed) { + if (allLinesHaveRecords && allRecordsCompleted) { + stockTake.status = StockTakeStatus.COMPLETED + stockTake.planEnd = java.time.LocalDateTime.now() + stockTakeRepository.save(stockTake) + println("Stock take $stockTakeId status updated to COMPLETED - all records are completed") + return mapOf("success" to true, "message" to "Stock take status updated to COMPLETED", "updated" to true) + } else if (allLinesHaveRecords && allRecordsPassed) { + // 如果所有记录都已创建且都是 "pass" 或 "completed",更新 stock take 状态为 "approving" stockTake.status = StockTakeStatus.APPROVING stockTake.actualEnd = java.time.LocalDateTime.now() stockTakeRepository.save(stockTake) - + println("Stock take $stockTakeId status updated to APPROVING - all records are pass") return mapOf( "success" to true, "message" to "Stock take status updated to APPROVING", "updated" to true ) - } else if (allLinesHaveRecords && allRecordsCompleted) { - stockTake.status = StockTakeStatus.COMPLETED - stockTake.planEnd = java.time.LocalDateTime.now() - stockTakeRepository.save(stockTake) - println("Stock take $stockTakeId status updated to COMPLETED - all records are completed") - return mapOf("success" to true, "message" to "Stock take status updated to COMPLETED", "updated" to true) } else { return mapOf( "success" to true, "message" to "Conditions not met for status update", "updated" to false, "allLinesHaveRecords" to allLinesHaveRecords, - "allRecordsPassed" to allRecordsPassed + "allRecordsPassed" to allRecordsPassed, + "allRecordsCompleted" to allRecordsCompleted ) } } catch (e: Exception) { @@ -835,6 +865,8 @@ open fun saveApproverStockTakeRecord( val inventoryLot = inventoryLotRepository.findByIdAndDeletedFalse( inventoryLotLine?.inventoryLot?.id ?: throw IllegalArgumentException("Inventory lot ID not found") ) ?: throw IllegalArgumentException("Inventory lot not found") + val inventory = inventoryRepository.findByItemId(inventoryLot.item?.id ?: throw IllegalArgumentException("Item ID not found")) + .orElse(null) ?: throw IllegalArgumentException("Inventory not found for item") if (varianceQty !=BigDecimal.ZERO ) { val stockTakeLine = StockTakeLine().apply { @@ -849,13 +881,14 @@ open fun saveApproverStockTakeRecord( stockTakeLineRepository.save(stockTakeLine) if (varianceQty < BigDecimal.ZERO ) { - val stockOut=StockOut().apply { - this.type="stockTake" - this.status="completed" - this.handler=request.approverId - } - stockOutRepository.save(stockOut) - + var stockOut = stockOutRepository.findByStockTakeIdAndDeletedFalse(stockTakeId) + ?: StockOut().apply { + this.type = "stockTake" + this.status = "completed" + this.handler = request.approverId + + }.also { stockOutRepository.save(it) } + val stockOutLine = StockOutLine().apply { this.item=inventoryLot.item this.qty=(-varianceQty)?.toDouble() @@ -866,21 +899,35 @@ open fun saveApproverStockTakeRecord( } stockOutLineRepository.save(stockOutLine) + val newBalance = (inventory.onHandQty ?: BigDecimal.ZERO).toDouble()-(varianceQty).toDouble() + val stockLedger = StockLedger().apply { + this.inventory=inventory + this.itemId=inventoryLot.item?.id + this.itemCode=stockTakeRecord.itemCode + this.inQty=null + this.outQty=(-varianceQty)?.toDouble() + this.stockOutLine=stockOutLine + this.balance=newBalance + this.type="Adj" + this.date = LocalDate.now() + } + stockLedgerRepository.save(stockLedger) } if (varianceQty > BigDecimal.ZERO ) { - val stockIn=StockIn().apply { - this.code=stockTake.code - this.status="completed" - this.stockTake=stockTake - } - stockInRepository.save(stockIn) + var stockIn = stockInRepository.findByStockTakeIdAndDeletedFalse(stockTakeId) + ?: StockIn().apply { + this.code = stockTake.code + this.status = "completed" + this.stockTake = stockTake + }.also { stockInRepository.save(it) } + val stockInLine = StockInLine().apply { this.stockTakeLine=stockTakeLine this.item=inventoryLot.item this.itemNo=stockTakeRecord.itemCode this.stockIn = stockIn - this.demandQty=finalQty - this.acceptedQty=finalQty + this.demandQty=varianceQty + this.acceptedQty=varianceQty this.expiryDate=inventoryLot.expiryDate this.inventoryLot=inventoryLot this.inventoryLotLine=inventoryLotLine @@ -891,9 +938,21 @@ open fun saveApproverStockTakeRecord( } stockInLineRepository.save(stockInLine) + val newBalance = (inventory.onHandQty ?: BigDecimal.ZERO).toDouble()+varianceQty.toDouble() + val stockLedger = StockLedger().apply { + this.inventory=inventory + this.itemId=inventoryLot.item?.id + this.itemCode=inventoryLot.item?.code + this.inQty=varianceQty?.toDouble() + this.stockInLine=stockInLine + this.outQty=null + this.balance=newBalance + this.type="Adj" + this.date = LocalDate.now() + } + stockLedgerRepository.save(stockLedger) } - // val currentInQty = inventoryLotLine.inQty ?: BigDecimal.ZERO - // val newInQty = currentInQty.add(variance) + val updateRequest = SaveInventoryLotLineRequest( id = inventoryLotLine.id, @@ -925,9 +984,7 @@ open fun batchSaveApproverStockTakeRecords( val stockTake = stockTakeRepository.findByIdAndDeletedIsFalse(request.stockTakeId) ?: throw IllegalArgumentException("Stock take not found: ${request.stockTakeId}") - // 2. Get all stock take records for this section where: - // - pickerFirstStockTakeQty is not 0 - // - approverStockTakeQty is null (not yet approved) + val stockTakeRecords = stockTakeRecordRepository.findAll() .filter { !it.deleted && @@ -982,7 +1039,7 @@ open fun batchSaveApproverStockTakeRecords( this.approverName = user?.name this.approverStockTakeQty = qty this.approverBadQty = badQty - this.varianceQty = varianceQty // 应该是 0 + this.varianceQty = varianceQty this.status = "completed" } @@ -1023,13 +1080,18 @@ open fun updateStockTakeRecordStatusToNotMatch(stockTakeRecordId: Long): StockTa return stockTakeRecordRepository.save(stockTakeRecord) } -open fun getInventoryLotDetailsByStockTakeSectionNotMatch(stockTakeSection: String, stockTakeId: Long? = null): List { - println("getInventoryLotDetailsByStockTakeSectionNotMatch called with section: $stockTakeSection, stockTakeId: $stockTakeId") +open fun getInventoryLotDetailsByStockTakeSectionNotMatch( + stockTakeSection: String, + stockTakeId: Long? = null, + pageNum: Int = 0, + pageSize: Int = Int.MAX_VALUE // Default to return all if not specified +): RecordsRes { + println("getInventoryLotDetailsByStockTakeSectionNotMatch called with section: $stockTakeSection, stockTakeId: $stockTakeId, pageNum: $pageNum, pageSize: $pageSize") val warehouses = warehouseRepository.findAllByStockTakeSectionAndDeletedIsFalse(stockTakeSection) if (warehouses.isEmpty()) { logger.warn("No warehouses found for stockTakeSection: $stockTakeSection") - return emptyList() + return RecordsRes(emptyList(), 0) } val warehouseIds = warehouses.mapNotNull { it.id } @@ -1055,7 +1117,7 @@ open fun getInventoryLotDetailsByStockTakeSectionNotMatch(stockTakeSection: Stri } // 只返回有 notMatch 记录的 inventory lot lines - return inventoryLotLines.mapNotNull { ill -> + val allResults = inventoryLotLines.mapNotNull { ill -> val inventoryLot = ill.inventoryLot val item = inventoryLot?.item val warehouse = ill.warehouse @@ -1115,5 +1177,120 @@ open fun getInventoryLotDetailsByStockTakeSectionNotMatch(stockTakeSection: Stri finalQty = stockTakeLine?.finalQty, ) } + + // Apply pagination - if pageSize is Int.MAX_VALUE, return all results + val paginatedResult = if (pageSize >= allResults.size) { + // Return all results if pageSize is large enough + allResults + } else { + val pageable = PageRequest.of(pageNum, pageSize) + val startIndex = pageable.offset.toInt() + val endIndex = minOf(startIndex + pageSize, allResults.size) + if (startIndex < allResults.size) { + allResults.subList(startIndex, endIndex) + } else { + emptyList() + } + } + + return RecordsRes(paginatedResult, allResults.size) +} + + + +fun searchStockTransactions(request: SearchStockTransactionRequest): RecordsRes { + val startTime = System.currentTimeMillis() + + // 添加调试日志 + println("Search request received: itemCode=${request.itemCode}, itemName=${request.itemName}, type=${request.type}, startDate=${request.startDate}, endDate=${request.endDate}") + + // 验证:itemCode 或 itemName 至少一个不为 null 或空字符串 + val itemCode = request.itemCode?.trim()?.takeIf { it.isNotEmpty() } + val itemName = request.itemName?.trim()?.takeIf { it.isNotEmpty() } + + if (itemCode == null && itemName == null) { + println("Search validation failed: both itemCode and itemName are null/empty") + return RecordsRes(emptyList(), 0) + } + + // request.startDate 和 request.endDate 已经是 LocalDate? 类型,不需要转换 + val startDate = request.startDate + val endDate = request.endDate + + println("Processed params: itemCode=$itemCode, itemName=$itemName, startDate=$startDate, endDate=$endDate") + + // 使用 Repository 查询(更简单、更快) + val total = stockLedgerRepository.countStockTransactions( + itemCode = itemCode, + itemName = itemName, + type = request.type, + startDate = startDate, + endDate = endDate + ) + + println("Total count: $total") + + // 如果 pageSize 是默认值(100)或未设置,使用 total 作为 pageSize + val actualPageSize = if (request.pageSize == 100) { + total.toInt().coerceAtLeast(1) + } else { + request.pageSize + } + + // 计算 offset + val offset = request.pageNum * actualPageSize + + // 查询所有符合条件的记录 + val ledgers = stockLedgerRepository.findStockTransactions( + itemCode = itemCode, + itemName = itemName, + type = request.type, + startDate = startDate, + endDate = endDate + ) + + println("Found ${ledgers.size} ledgers") + + val transactions = ledgers.map { ledger -> + val stockInLine = ledger.stockInLine + val stockOutLine = ledger.stockOutLine + + StockTransactionResponse( + id = stockInLine?.id ?: stockOutLine?.id ?: 0L, + transactionType = if (ledger.inQty != null && ledger.inQty!! > 0) "IN" else "OUT", + itemId = ledger.itemId ?: 0L, + itemCode = ledger.itemCode, + itemName = ledger.inventory?.item?.name, + balanceQty = ledger.balance?.let { balance: Double -> BigDecimal(balance.toString()) }, + qty = if (ledger.inQty != null && ledger.inQty!! > 0) { + BigDecimal(ledger.inQty.toString()) + } else { + BigDecimal(ledger.outQty?.toString() ?: "0") + }, + type = ledger.type, + status = stockInLine?.status ?: stockOutLine?.status ?: "", + transactionDate = ledger.created, + date = ledger.date, + lotNo = stockInLine?.lotNo ?: (stockOutLine?.inventoryLotLine?.inventoryLot?.lotNo), + stockInId = stockInLine?.stockIn?.id, + stockOutId = stockOutLine?.stockOut?.id, + remarks = stockInLine?.remarks + ) + } + + // 按 date 排序(从旧到新),如果 date 为 null 则使用 transactionDate 的日期部分 + val sortedTransactions = transactions.sortedWith( + compareBy( + { it.date ?: it.transactionDate?.toLocalDate() }, + { it.transactionDate } + ) + ) + + // 应用分页 + val paginatedTransactions = sortedTransactions.drop(offset).take(actualPageSize) + val totalTime = System.currentTimeMillis() - startTime + println("Total time (Repository query): ${totalTime}ms, count: ${paginatedTransactions.size}, total: $total") + + return RecordsRes(paginatedTransactions, total.toInt()) +} } -} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt index 0e4beef..53666ef 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt @@ -480,6 +480,7 @@ open class SuggestedPickLotService( this.qty = (suggestion.qty ?: BigDecimal.ZERO).toDouble() this.status = StockOutLineStatus.PENDING.status this.deleted = false + this.type = "Nor" } val savedStockOutLine = stockOutLIneRepository.save(stockOutLine) @@ -529,6 +530,7 @@ open class SuggestedPickLotService( this.inventoryLotLine = suggestedLotLine this.pickOrderLine = updatedPickOrderLine this.status = StockOutLineStatus.PENDING.status + this.type = "Nor" } val savedStockOutLine = stockOutLIneRepository.saveAndFlush(stockOutLine) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/StockTakeRecordController.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/StockTakeRecordController.kt index 498596c..cb98a25 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/web/StockTakeRecordController.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/StockTakeRecordController.kt @@ -10,7 +10,8 @@ import org.springframework.web.bind.annotation.RequestBody import org.springframework.http.ResponseEntity import com.ffii.fpsms.modules.stock.web.model.* import org.slf4j.LoggerFactory - +import com.ffii.core.response.RecordsRes +import java.time.LocalDate @RestController @RequestMapping("/stockTakeRecord") class StockTakeRecordController( @@ -29,9 +30,18 @@ class StockTakeRecordController( @GetMapping("/inventoryLotDetailsBySectionNotMatch") fun getInventoryLotDetailsByStockTakeSectionNotMatch( @RequestParam stockTakeSection: String, - @RequestParam(required = false) stockTakeId: Long? - ): List { - return stockOutRecordService.getInventoryLotDetailsByStockTakeSectionNotMatch(stockTakeSection, stockTakeId) + @RequestParam(required = false) stockTakeId: Long?, + @RequestParam(required = false, defaultValue = "0") pageNum: Int, + @RequestParam(required = false) pageSize: Int? + ): RecordsRes { + // If pageSize is null, use a large number to return all records + val actualPageSize = pageSize ?: Int.MAX_VALUE + return stockOutRecordService.getInventoryLotDetailsByStockTakeSectionNotMatch( + stockTakeSection, + stockTakeId, + pageNum, + actualPageSize + ) } @GetMapping("/inventoryLotDetails") @@ -42,12 +52,14 @@ class StockTakeRecordController( } @GetMapping("/inventoryLotDetailsBySection") - fun getInventoryLotDetailsByStockTakeSection( - @RequestParam stockTakeSection: String, - @RequestParam(required = false) stockTakeId: Long? - ): List { - return stockOutRecordService.getInventoryLotDetailsByStockTakeSection(stockTakeSection, stockTakeId) - } +fun getInventoryLotDetailsByStockTakeSection( + @RequestParam stockTakeSection: String, + @RequestParam(required = false) stockTakeId: Long?, + @RequestParam(required = false, defaultValue = "0") pageNum: Int, + @RequestParam(required = false, defaultValue = "10") pageSize: Int +): RecordsRes { + return stockOutRecordService.getInventoryLotDetailsByStockTakeSection(stockTakeSection, stockTakeId, pageNum, pageSize) +} @PostMapping("/saveStockTakeRecord") fun saveStockTakeRecord( @@ -177,4 +189,27 @@ class StockTakeRecordController( )) } } + + + @GetMapping("/searchStockTransactions") + fun searchStockTransactions( + @RequestParam(required = false) itemCode: String?, + @RequestParam(required = false) itemName: String?, + @RequestParam(required = false) type: String?, + @RequestParam(required = false) startDate: LocalDate?, + @RequestParam(required = false) endDate: LocalDate?, + @RequestParam(required = false, defaultValue = "0") pageNum: Int, + @RequestParam(required = false, defaultValue = "100") pageSize: Int + ): RecordsRes { + val request = SearchStockTransactionRequest( + startDate = startDate, + endDate = endDate, + itemCode = itemCode, + itemName = itemName, + type = type, + pageNum = pageNum, + pageSize = pageSize + ) + return stockOutRecordService.searchStockTransactions(request) + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockInRequest.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockInRequest.kt index 8c43d6f..ff26153 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockInRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockInRequest.kt @@ -70,3 +70,19 @@ data class SaveInventoryLotLineForSil ( val qty: BigDecimal, val warehouseId: Long? ) +data class NewStockInCreateRequest( + + val inventoryLotLineId: Long, + val itemId: Long, + val itemNo: String, + + val demandQty: BigDecimal, + val acceptedQty: BigDecimal, + val expiryDate: LocalDate, + val lotNo: String?, + val stockIntype: String, + val stockInLineType: String, + + val warehouseId: Long, + val stockUomId: Long, +) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/model/StockTakeRecordReponse.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/model/StockTakeRecordReponse.kt index bbef40f..4f2cef1 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/web/model/StockTakeRecordReponse.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/model/StockTakeRecordReponse.kt @@ -4,6 +4,8 @@ import com.fasterxml.jackson.annotation.JsonFormat import java.time.LocalDate import java.math.BigDecimal import java.time.LocalDateTime +import com.ffii.core.response.RecordsRes + data class AllPickedStockTakeListReponse( val id: Long, val stockTakeSession: String, @@ -98,4 +100,34 @@ data class BatchSaveApproverStockTakeRecordResponse( val successCount: Int, val errorCount: Int, val errors: List +) +data class SearchStockTransactionRequest( + val startDate: LocalDate? = null, + val endDate: LocalDate? = null, + val itemCode: String? = null, + val itemName: String? = null, + val type: String? = null, + val pageNum: Int = 0, + val pageSize: Int = 100 +) +data class StockTransactionResponse( + val id: Long, + val transactionType: String, + val itemId: Long, + val itemCode: String?, + val itemName: String?, + val balanceQty: BigDecimal? = null, + val qty: BigDecimal, + val type: String?, + val date: LocalDate?, + val status: String, + val transactionDate: LocalDateTime?, + val lotNo: String?, + val stockInId: Long?, + val stockOutId: Long?, + val remarks: String? +) + +data class StockTransactionListResponse( + val records: RecordsRes ) \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20260109_enson/02_alter_table.sql b/src/main/resources/db/changelog/changes/20260109_enson/02_alter_table.sql new file mode 100644 index 0000000..26a48d6 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20260109_enson/02_alter_table.sql @@ -0,0 +1,7 @@ +--liquibase formatted sql +--changeset author:add_time_fields_to_productprocessline + +ALTER TABLE `stock_out_line` +ADD COLUMN `startTime` DATETIME NULL AFTER `type`, +ADD COLUMN `endTime` DATETIME NULL AFTER `startTime`; + diff --git a/src/main/resources/db/changelog/changes/20260112_01_Enson/01_alter_table.sql b/src/main/resources/db/changelog/changes/20260112_01_Enson/01_alter_table.sql new file mode 100644 index 0000000..e54766c --- /dev/null +++ b/src/main/resources/db/changelog/changes/20260112_01_Enson/01_alter_table.sql @@ -0,0 +1,8 @@ +--liquibase formatted sql +--changeset author:add_type_and_item_id_to_stock_ledger + +ALTER TABLE `stock_ledger` +ADD COLUMN `type` varchar(255) NULL AFTER `balance`, +ADD COLUMN `itemId` INT NULL AFTER `inventoryId`, +ADD COLUMN `itemCode` varchar(255) NULL AFTER `itemId`; + diff --git a/src/main/resources/db/changelog/changes/20260112_01_Enson/02_alter_table.sql b/src/main/resources/db/changelog/changes/20260112_01_Enson/02_alter_table.sql new file mode 100644 index 0000000..c9bb584 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20260112_01_Enson/02_alter_table.sql @@ -0,0 +1,7 @@ +--liquibase formatted sql +--changeset author:add_type_and_item_id_to_stock_ledger + +ALTER TABLE `stock_ledger` +ADD COLUMN `date` varchar(255) NULL AFTER `deleted`; + + diff --git a/src/main/resources/db/changelog/changes/20260112_01_Enson/03_alter_table.sql b/src/main/resources/db/changelog/changes/20260112_01_Enson/03_alter_table.sql new file mode 100644 index 0000000..12e4ea0 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20260112_01_Enson/03_alter_table.sql @@ -0,0 +1,7 @@ +--liquibase formatted sql +--changeset author:add_type_and_item_id_to_stock_ledger + +ALTER TABLE `stock_out` +ADD COLUMN `stockTakeId` INT NULL AFTER `remarks`; + +