Переглянути джерело

update stock record

master
CANCERYS\kw093 3 тижднів тому
джерело
коміт
6c9e773b47
33 змінених файлів з 893 додано та 165 видалено
  1. +2
    -0
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt
  2. +1
    -1
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/web/DoPickOrderController.kt
  3. +95
    -1
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt
  4. +1
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt
  5. +5
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt
  6. +17
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SearchJobOrderInfoRequest.kt
  7. +1
    -0
      src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickExecutionIssueRepository.kt
  8. +1
    -0
      src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLineRepository.kt
  9. +2
    -0
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt
  10. +41
    -1
      src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt
  11. +4
    -0
      src/main/java/com/ffii/fpsms/modules/productProcess/web/ProductProcessController.kt
  12. +15
    -0
      src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt
  13. +8
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt
  14. +24
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLineRepository.kt
  15. +1
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockInRepository.kt
  16. +12
    -4
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockLedger.kt
  17. +44
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockLedgerRepository.kt
  18. +2
    -1
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockOut.kt
  19. +26
    -1
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutLIneRepository.kt
  20. +4
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutLine.kt
  21. +1
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutRepository.kt
  22. +22
    -11
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRecordRepository.kt
  23. +87
    -2
      src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt
  24. +56
    -13
      src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt
  25. +297
    -120
      src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeRecordService.kt
  26. +2
    -0
      src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt
  27. +45
    -10
      src/main/java/com/ffii/fpsms/modules/stock/web/StockTakeRecordController.kt
  28. +16
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockInRequest.kt
  29. +32
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/model/StockTakeRecordReponse.kt
  30. +7
    -0
      src/main/resources/db/changelog/changes/20260109_enson/02_alter_table.sql
  31. +8
    -0
      src/main/resources/db/changelog/changes/20260112_01_Enson/01_alter_table.sql
  32. +7
    -0
      src/main/resources/db/changelog/changes/20260112_01_Enson/02_alter_table.sql
  33. +7
    -0
      src/main/resources/db/changelog/changes/20260112_01_Enson/03_alter_table.sql

+ 2
- 0
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)
}


+ 1
- 1
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


+ 95
- 1
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<MaterialPickStatusItem> {
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
)
}
}
}

+ 1
- 0
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)
}


+ 5
- 0
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<MaterialPickStatusItem> {
return joPickOrderService.getMaterialPickStatus()
}
}

+ 17
- 0
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?
)

+ 1
- 0
src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickExecutionIssueRepository.kt Переглянути файл

@@ -74,6 +74,7 @@ fun findMaterialIssues(): List<PickExecutionIssue>
ORDER BY p.created DESC
""")
fun getBadItemList(): List<PickExecutionIssue>
fun findByPickOrderId(pickOrderId: Long): List<PickExecutionIssue>

}



+ 1
- 0
src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLineRepository.kt Переглянути файл

@@ -22,4 +22,5 @@ fun findAllPickOrdersByItemId(@Param("itemId") itemId: Long): List<PickOrder>
""")
fun findAllByPickOrderId(@Param("pickOrderId") pickOrderId: Long): List<PickOrderLine>
fun findAllByPickOrderIdAndDeletedFalse(pickOrderId: Long): List<PickOrderLine>
fun findByPickOrderId(pickOrderId: Long): List<PickOrderLine>
}

+ 2
- 0
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++


+ 41
- 1
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<JobProcessStatusResponse> {
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,
)
}
}
)
}
}

}


+ 4
- 0
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<JobProcessStatusResponse> {
return productProcessService.getJobProcessStatus()
}
}

+ 15
- 0
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<ProcessStatusInfo>
)

+ 8
- 0
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>): 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>): Long
}

+ 24
- 0
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<StockInLine, Long> {
fun findAllStockInLineInfoByStockInIdAndDeletedFalse(stockInId: Long): List<StockInLineInfo>
@@ -31,4 +34,25 @@ interface StockInLineRepository : AbstractRepository<StockInLine, Long> {
AND sil.deleted = false
""")
fun findByReceiptDateAndDeletedFalse(date: LocalDate): List<StockInLine>
@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<StockInLine>
//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<Long>): List<StockInLine>
}

+ 1
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/StockInRepository.kt Переглянути файл

@@ -21,4 +21,5 @@ interface StockInRepository : AbstractRepository<StockIn, Long> {
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?

}

+ 12
- 4
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<Long>() {
// @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<Long>() {

@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
}

+ 44
- 0
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<StockLedger, Long> {
@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<StockLedger>
@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
}

+ 2
- 1
src/main/java/com/ffii/fpsms/modules/stock/entity/StockOut.kt Переглянути файл

@@ -34,5 +34,6 @@ open class StockOut: BaseEntity<Long>(){

@Column(name = "remarks")
open var remarks: String? = null

@Column(name = "stockTakeId")
open var stockTakeId: Long? = null
}

+ 26
- 1
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<StockOutLine, Long> {
@@ -21,5 +24,27 @@ interface StockOutLIneRepository: AbstractRepository<StockOutLine, Long> {
inventoryLotLineId: Long
): List<StockOutLine>
fun existsByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(pickOrderLineId: Long, inventoryLotLineId: Long): Boolean
//fun findAllByPickOrderLineIdAndDeletedFalse(pickOrderLineId: Long): List<StockOutLine>
@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<StockOutLine>
@Query("SELECT sol FROM StockOutLine sol WHERE sol.item.id = :itemId AND sol.deleted = false")
fun findAllByItemIdAndDeletedFalse(itemId: Long): List<StockOutLine>

@Query("SELECT sol FROM StockOutLine sol WHERE sol.item.id IN :itemIds AND sol.deleted = false")
fun findAllByItemIdInAndDeletedFalse(itemIds: List<Long>): List<StockOutLine>
}

+ 4
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutLine.kt Переглянути файл

@@ -48,4 +48,8 @@ open class StockOutLine: BaseEntity<Long>() {

@Column(name = "type")
open var type: String? = null
@Column(name = "startTime")
open var startTime: LocalDateTime? = null
@Column(name = "endTime")
open var endTime: LocalDateTime? = null
}

+ 1
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutRepository.kt Переглянути файл

@@ -7,4 +7,5 @@ import java.util.Optional
@Repository
interface StockOutRepository: AbstractRepository<StockOut, Long> {
fun findByConsoPickOrderCode(consoPickOrderCode: String) : Optional<StockOut>
fun findByStockTakeIdAndDeletedFalse(stockTakeId: Long): StockOut?
}

+ 22
- 11
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<StockTakeRecord, Long> {
fun findAllByStockTakeIdAndDeletedIsFalse(stockTakeId: Long): List<StockTakeRecord>;
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<Array<Any>>
}
fun countStockTakeRecordsBySectionAndStockTakeId(): List<Array<Any>>




}


+ 87
- 2
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<StockInLine, Long, StockInLineRepository>(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)
}
}

+ 56
- 13
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<StockOutLine, Long, StockOutLIneRepository>(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 {
)
)
}
}}
}
@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)
}
}

+ 297
- 120
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<InventoryLotDetailResponse> {
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<InventoryLotDetailResponse> {
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<InventoryLotDetailResponse> {
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<InventoryLotDetailResponse> {
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<StockTransactionResponse> {
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<StockTransactionResponse>(
{ 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())
}
}
}

+ 2
- 0
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)


+ 45
- 10
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<InventoryLotDetailResponse> {
return stockOutRecordService.getInventoryLotDetailsByStockTakeSectionNotMatch(stockTakeSection, stockTakeId)
@RequestParam(required = false) stockTakeId: Long?,
@RequestParam(required = false, defaultValue = "0") pageNum: Int,
@RequestParam(required = false) pageSize: Int?
): RecordsRes<InventoryLotDetailResponse> {
// 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<InventoryLotDetailResponse> {
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<InventoryLotDetailResponse> {
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<StockTransactionResponse> {
val request = SearchStockTransactionRequest(
startDate = startDate,
endDate = endDate,
itemCode = itemCode,
itemName = itemName,
type = type,
pageNum = pageNum,
pageSize = pageSize
)
return stockOutRecordService.searchStockTransactions(request)
}
}

+ 16
- 0
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,
)

+ 32
- 0
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<String>
)
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<StockTransactionResponse>
)

+ 7
- 0
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`;


+ 8
- 0
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`;


+ 7
- 0
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`;



+ 7
- 0
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`;



Завантаження…
Відмінити
Зберегти