Browse Source

stock take block any other in/out while stock taking

stockTakeUpdate
CANCERYS\kw093 3 weeks ago
parent
commit
75ac1849df
12 changed files with 272 additions and 5 deletions
  1. +7
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLine.kt
  2. +3
    -1
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLineRepository.kt
  3. +36
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRepository.kt
  4. +3
    -1
      src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotLineService.kt
  5. +99
    -0
      src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt
  6. +15
    -0
      src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt
  7. +73
    -0
      src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeLocationGuard.kt
  8. +6
    -2
      src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeRecordService.kt
  9. +3
    -0
      src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt
  10. +3
    -1
      src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockInRequest.kt
  11. +18
    -0
      src/main/resources/db/changelog/changes/20261013_close_stock_take_rounds/01_close_march_open_rounds.sql
  12. +6
    -0
      src/main/resources/db/changelog/changes/20261013_pending_auto_putaway/01_stock_in_line_pending_auto_putaway.sql

+ 7
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLine.kt View File

@@ -114,6 +114,13 @@ open class StockInLine : BaseEntity<Long>() {
@Column(name = "remarks")
open var remarks: String? = null

/** Set when job-order auto putaway is waiting for an open stock-take round to close. */
@Column(name = "pendingAutoPutawayWarehouseId")
open var pendingAutoPutawayWarehouseId: Long? = null

@Column(name = "pendingAutoPutawayQty", precision = 14, scale = 2)
open var pendingAutoPutawayQty: BigDecimal? = null

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



+ 3
- 1
src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLineRepository.kt View File

@@ -63,7 +63,9 @@ fun searchStockInLines(
//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>
fun findFirstByJobOrder_IdAndDeletedFalse(jobOrderId: Long): StockInLine?
fun findFirstByJobOrder_IdAndDeletedFalse(jobOrderId: Long): StockInLine?

fun findAllByPendingAutoPutawayWarehouseIdIsNotNullAndDeletedIsFalse(): List<StockInLine>

/** Loads stock-in lines with item and purchaseOrderLine.item so bDesc/bDesc_en (item name) are available for M18 GRN. */
@Query("""


+ 36
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRepository.kt View File

@@ -21,4 +21,40 @@ interface StockTakeRepository : AbstractRepository<StockTake, Long> {
"SELECT COALESCE(MAX(st.stockTakeRoundId), 0) FROM StockTake st WHERE st.deleted = false"
)
fun findMaxStockTakeRoundId(): Long

@Query(
value = """
SELECT DISTINCT stockTakeSection FROM stock_take
WHERE deleted = 0
AND status IN ('pending', 'stockTaking', 'approving')
AND stockTakeSection IS NOT NULL
AND stockTakeSection <> ''
""",
nativeQuery = true,
)
fun findOpenStockTakeSections(): List<String>

@Query(
value = """
SELECT
id AS id,
stockTakeSection AS stockTakeSection,
stockTakeRoundMode AS stockTakeRoundMode,
stockTakeRoundId AS stockTakeRoundId
FROM stock_take
WHERE deleted = 0
AND status IN ('pending', 'stockTaking', 'approving')
AND stockTakeSection IS NOT NULL
AND stockTakeSection <> ''
""",
nativeQuery = true,
)
fun findOpenStockTakeRows(): List<OpenStockTakeRow>
}

interface OpenStockTakeRow {
fun getId(): Long
fun getStockTakeSection(): String?
fun getStockTakeRoundMode(): String?
fun getStockTakeRoundId(): Long?
}

+ 3
- 1
src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotLineService.kt View File

@@ -61,7 +61,8 @@ open class InventoryLotLineService(
private val inventoryRepository: InventoryRepository,
private val printerService: PrinterService,
@Lazy
private val jobOrderService: JobOrderService
private val jobOrderService: JobOrderService,
private val stockTakeLocationGuard: StockTakeLocationGuard,
) {
@Transactional
open fun markExpiredLotLinesAsUnavailable(): Int {
@@ -387,6 +388,7 @@ open class InventoryLotLineService(
open fun updateInventoryLotLineQuantities(request: UpdateInventoryLotLineQuantitiesRequest): MessageResponse {
try {
val inventoryLotLine = inventoryLotLineRepository.findById(request.inventoryLotLineId).orElseThrow()
stockTakeLocationGuard.assertLotLineMovable(inventoryLotLine)

// Handle quantity updates based on operation
var newHoldQty = inventoryLotLine.holdQty ?: BigDecimal.ZERO


+ 99
- 0
src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt View File

@@ -111,6 +111,7 @@ open class StockInLineService(
@Value("\${scheduler.m18Grn.createEnabled:false}") private val m18GrnCreateEnabled: Boolean,
/** Recent window for PO stock-in alerts (pending / receiving) in nav / list UI. */
@Value("\${fpsms.purchase-stock-in-alert.lookback-days:7}") private val purchaseStockInAlertLookbackDays: Int,
private val stockTakeLocationGuard: StockTakeLocationGuard,
// private val bomOutputQtyService: BomOutputQtyService,
) : AbstractBaseEntityService<StockInLine, Long, StockInLineRepository>(jdbcDao, stockInLineRepository) {

@@ -1032,6 +1033,10 @@ open class StockInLineService(
}
_logStep("apply_fields", _tApplyFields)
if (request.status == StockInLineStatus.RECEIVED.status) {
val held = holdAutoPutawayIfSectionInStockTake(request, stockInLine)
if (held != null) {
return held
}
val _tPutAwayTotal = System.nanoTime()
// Putaway
val _tSaveLotLines = System.nanoTime()
@@ -1439,8 +1444,99 @@ open class StockInLineService(
logger.info("[SIL.ledger timing] TOTAL took ${String.format("%.3f", (System.nanoTime() - _t0) / 1_000_000.0)} ms")
}

/**
* Job-order WIP / FG / FA auto putaway into a section that is still in an open stock-take round
* is stored on the stock-in line and posted when that round closes.
* Any other putaway into that section is rejected.
*/
private fun holdAutoPutawayIfSectionInStockTake(
request: SaveStockInLineRequest,
stockInLine: StockInLine,
): MessageResponse? {
if (request.releaseHeldPutaway) return null
if (request.stockTakeLineId != null || stockInLine.stockTakeLine != null) return null
if (stockInLine.type.equals("TKE", ignoreCase = true)) return null
val targets = (request.inventoryLotLines ?: emptyList())
.mapNotNull { line ->
val warehouseId = line.warehouseId ?: return@mapNotNull null
if (line.qty <= BigDecimal.ZERO) return@mapNotNull null
warehouseId to line.qty
}
if (targets.isEmpty()) return null
val itemId = stockInLine.item?.id ?: request.itemId
val blocked = targets.firstOrNull { stockTakeLocationGuard.blockMessage(it.first, itemId) != null }
?: return null
if (isJobOrderAutoPutaway(stockInLine) && targets.size == 1) {
val (warehouseId, qty) = targets.first()
stockInLine.pendingAutoPutawayWarehouseId = warehouseId
stockInLine.pendingAutoPutawayQty = qty
stockInLine.jobOrder?.let { jo ->
jo.status = JobOrderStatus.COMPLETED
jobOrderRepository.save(jo)
}
val saved = saveAndFlush(stockInLine)
val lineInfo = stockInLineRepository.findStockInLineInfoByIdAndDeletedFalse(saved.id!!)
return MessageResponse(
id = saved.id,
code = "PUTAWAY_HELD",
name = null,
type = "putaway_held",
message = "盤點進行中,自動上架將於盤點結束後入庫",
errorPosition = null,
entity = listOf(lineInfo),
)
}
stockTakeLocationGuard.assertWarehouseMovable(blocked.first, itemId)
return null
}

private fun isJobOrderAutoPutaway(stockInLine: StockInLine): Boolean {
if (stockInLine.jobOrder == null) return false
val bom = stockInLine.jobOrder?.bom?.description
if (bom == "WIP" || bom == "FG") return true
val itemNo = (stockInLine.itemNo ?: stockInLine.item?.code ?: "").uppercase()
return itemNo.contains("FA")
}

/** Post auto putaways that were waiting on sections closed with this round. */
@Transactional
open fun releasePendingAutoPutaways(sections: Collection<String>) {
val wanted = sections.map { it.trim().lowercase() }.filter { it.isNotEmpty() }.toSet()
if (wanted.isEmpty()) return
val pending = stockInLineRepository.findAllByPendingAutoPutawayWarehouseIdIsNotNullAndDeletedIsFalse()
for (sil in pending) {
val warehouseId = sil.pendingAutoPutawayWarehouseId ?: continue
val warehouse = warehouseRepository.findById(warehouseId).orElse(null) ?: continue
val section = warehouse.stockTakeSection?.trim()?.lowercase() ?: continue
if (section !in wanted) continue
val qty = sil.pendingAutoPutawayQty ?: sil.acceptedQty ?: continue
val itemId = sil.item?.id ?: continue
sil.pendingAutoPutawayWarehouseId = null
sil.pendingAutoPutawayQty = null
saveAndFlush(sil)
update(
SaveStockInLineRequest(
id = sil.id,
itemId = itemId,
acceptedQty = sil.acceptedQty ?: qty,
acceptQty = qty,
status = StockInLineStatus.RECEIVED.status,
expiryDate = sil.expiryDate,
receiptDate = sil.receiptDate?.toLocalDate(),
productionDate = sil.productionDate?.toLocalDate(),
warehouseId = warehouseId,
releaseHeldPutaway = true,
inventoryLotLines = listOf(
SaveInventoryLotLineForSil(qty = qty, warehouseId = warehouseId),
),
)
)
}
}

@Transactional
open fun createStockIn(request: StockInRequest): StockInLine {
stockTakeLocationGuard.assertWarehouseMovable(request.warehouseId, request.itemId)
// Step 1: Create a row of stock_in
val prefix = "SI"
val midfix = CodeGenerator.DEFAULT_MIDFIX
@@ -1531,6 +1627,9 @@ open class StockInLineService(
request: StockInRequest,
existingInventoryLotLine: InventoryLotLine
): StockInLine {
val movingItemId = existingInventoryLotLine.inventoryLot?.item?.id ?: request.itemId
stockTakeLocationGuard.assertWarehouseMovable(existingInventoryLotLine.warehouse?.id, movingItemId)
stockTakeLocationGuard.assertWarehouseMovable(request.warehouseId, movingItemId)
// Step 1: Create StockIn
val prefix = "SI"
val midfix = CodeGenerator.DEFAULT_MIDFIX


+ 15
- 0
src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt View File

@@ -82,6 +82,7 @@ private val inventoryLotLineService: InventoryLotLineService,
private val pickExecutionIssueRepository: PickExecutionIssueRepository,
private val itemUomService: ItemUomService,
@Lazy private val doReplenishmentService: DoReplenishmentService,
private val stockTakeLocationGuard: StockTakeLocationGuard,
): AbstractBaseEntityService<StockOutLine, Long, StockOutLIneRepository>(jdbcDao, stockOutLineRepository) {

@PersistenceContext
@@ -208,6 +209,7 @@ val existingStockOutLine = stockOutLineRepository.findByPickOrderLineIdAndInvent
)
val item = itemRepository.findById(pickOrderLine.item!!.id!!).orElseThrow()
val inventoryLotLine = inventoryLotLineRepository.findById(request.inventoryLotLineId).orElseThrow()
stockTakeLocationGuard.assertLotLineMovable(inventoryLotLine)
val stockOutLine = StockOutLine()
.apply {
@@ -257,6 +259,7 @@ fun handleQc(stockOutLine: StockOutLine, request: UpdateStockOutLineRequest): Li
val zero = BigDecimal.ZERO
val one = BigDecimal.ONE
val targetLotLine = inventoryLotLineRepository.findById(request.inventoryLotLineId!!).orElseThrow()
stockTakeLocationGuard.assertLotLineMovable(targetLotLine)
val salesUnit = inventoryLotLine?.inventoryLot?.item?.id?.let {_itemId -> itemUomRespository.findByItemIdAndSalesUnitIsTrueAndDeletedIsFalse(_itemId) }
val ratio = 1.0 //(salesUnit?.ratioN ?: zero).divide(salesUnit?.ratioD ?: one).toDouble()
val targetLotLineEntry = targetLotLine.apply {
@@ -306,6 +309,7 @@ open fun createWithoutConso(request: CreateStockOutLineWithoutConsoRequest): Mes
val inventoryLotLine = inventoryLotLineRepository.findById(request.inventoryLotLineId).orElseThrow {
IllegalArgumentException("InventoryLotLine not found with ID: ${request.inventoryLotLineId}")
}
stockTakeLocationGuard.assertLotLineMovable(inventoryLotLine)
println("Found inventoryLotLine: ${inventoryLotLine.id}")
val stockOutLine = StockOutLine()
@@ -490,6 +494,7 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long {

/** Batch pick: same qty rules as [InventoryLotLineService.updateInventoryLotLineQuantities] pick, without double findById / extra service layers. */
private fun applyPickToInventoryLotLineInBatch(ill: InventoryLotLine, submitQty: BigDecimal) {
stockTakeLocationGuard.assertLotLineMovable(ill)
val zero = BigDecimal.ZERO
val newHold = (ill.holdQty ?: zero).minus(submitQty)
val newOut = (ill.outQty ?: zero).plus(submitQty)
@@ -1673,6 +1678,7 @@ open fun newBatchSubmit(request: QrPickBatchSubmitRequest): MessageResponse {
*/
open fun createStockOutForStockTransfer(inventoryLotLineId: Long, qty: Double): StockOutLine {
val inventoryLotLine = inventoryLotLineRepository.findById(inventoryLotLineId).orElseThrow()
stockTakeLocationGuard.assertLotLineMovable(inventoryLotLine)
val qtyBd = BigDecimal.valueOf(qty)
validateOutOnlyIncrease(inventoryLotLine, qtyBd, OutOnlyLotLineContext.STOCK_TRANSFER)
applyOutOnlyIncreaseToLotLine(inventoryLotLine, qtyBd)
@@ -1688,6 +1694,9 @@ open fun newBatchSubmit(request: QrPickBatchSubmitRequest): MessageResponse {
*/
open fun createStockOutForAdjustment(request: StockOutRequest): StockOutLine {
val inventoryLotLine = inventoryLotLineRepository.findById(request.inventoryLotLineId).orElseThrow()
if (!request.type.equals("TKE", ignoreCase = true)) {
stockTakeLocationGuard.assertLotLineMovable(inventoryLotLine)
}
val qtyBd = BigDecimal.valueOf(request.qty)
validateOutOnlyIncrease(inventoryLotLine, qtyBd, OutOnlyLotLineContext.ADJUSTMENT)
applyOutOnlyIncreaseToLotLine(inventoryLotLine, qtyBd)
@@ -1706,6 +1715,9 @@ open fun newBatchSubmit(request: QrPickBatchSubmitRequest): MessageResponse {
*/
open fun createStockOutForRemoval(inventoryLotLineId: Long, type: String = "ADJ"): StockOutLine {
val inventoryLotLine = inventoryLotLineRepository.findById(inventoryLotLineId).orElseThrow()
if (!type.equals("TKE", ignoreCase = true)) {
stockTakeLocationGuard.assertLotLineMovable(inventoryLotLine)
}
val inQty = inventoryLotLine.inQty ?: BigDecimal.ZERO
val outQty = inventoryLotLine.outQty ?: BigDecimal.ZERO
val deltaOut = inQty.subtract(outQty).coerceAtLeast(BigDecimal.ZERO)
@@ -2159,6 +2171,9 @@ fun applyStockOutLineDelta(
if (!skipInventoryWrite) {
val lotLine = savedSol.inventoryLotLine
if (lotLine != null) {
if (!postingType.equals("tke", ignoreCase = true)) {
stockTakeLocationGuard.assertLotLineMovable(lotLine)
}
if (isIssuePosting) {
val latestLotLine = inventoryLotLineRepository.findById(lotLine.id!!).orElse(null)
if (latestLotLine != null) {


+ 73
- 0
src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeLocationGuard.kt View File

@@ -0,0 +1,73 @@
package com.ffii.fpsms.modules.stock.service

import com.ffii.fpsms.modules.master.entity.WarehouseRepository
import com.ffii.fpsms.modules.stock.entity.InventoryLotLine
import com.ffii.fpsms.modules.stock.entity.StockTakeRecordRepository
import com.ffii.fpsms.modules.stock.entity.StockTakeRepository
import com.ffii.fpsms.modules.stock.enums.StockTakeRoundMode
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Service
import org.springframework.web.server.ResponseStatusException

/**
* Section rounds block every item in that stock-take section.
* Item rounds block only the items on that round's records.
* Stock-take's own variance posting does not call this guard.
*/
@Service
class StockTakeLocationGuard(
private val stockTakeRepository: StockTakeRepository,
private val stockTakeRecordRepository: StockTakeRecordRepository,
private val warehouseRepository: WarehouseRepository,
) {
/** Null when this warehouse and item can still move. */
fun blockMessage(warehouseId: Long?, itemId: Long?): String? {
if (warehouseId == null || warehouseId <= 0L) return null
val warehouse = warehouseRepository.findById(warehouseId).orElse(null) ?: return null
val section = warehouse.stockTakeSection?.trim()?.takeIf { it.isNotBlank() } ?: return null
val open = stockTakeRepository.findOpenStockTakeRows().filter {
section.equals(it.getStockTakeSection()?.trim(), ignoreCase = true)
}
if (open.isEmpty()) return null
if (open.any { !StockTakeRoundMode.ITEM.equals(it.getStockTakeRoundMode(), ignoreCase = true) }) {
return "盤點進行中,區域 $section 暫停出入庫"
}
if (itemId == null || itemId <= 0L) return null
val roundIds = open.mapNotNull { it.getStockTakeRoundId()?.takeIf { id -> id > 0L } }.distinct()
val headerIds = open.map { it.getId() }.distinct()
val records = buildList {
if (roundIds.isNotEmpty()) {
addAll(stockTakeRecordRepository.findAllByStockTakeRoundIdInAndDeletedIsFalse(roundIds))
}
if (headerIds.isNotEmpty()) {
addAll(stockTakeRecordRepository.findAllByStockTakeIdInAndDeletedIsFalse(headerIds))
}
}
val hit = records.firstOrNull { record ->
record.itemId == itemId &&
section.equals(record.stockTakeSection?.trim(), ignoreCase = true)
} ?: return null
val itemLabel = recordLabel(hit.itemCode, hit.itemName)
return "盤點進行中,貨品 $itemLabel 暫停出入庫"
}

fun assertWarehouseMovable(warehouseId: Long?, itemId: Long? = null) {
val message = blockMessage(warehouseId, itemId) ?: return
throw ResponseStatusException(HttpStatus.CONFLICT, message)
}

fun assertLotLineMovable(lotLine: InventoryLotLine?) {
assertWarehouseMovable(lotLine?.warehouse?.id, lotLine?.inventoryLot?.item?.id)
}

private fun recordLabel(code: String?, name: String?): String {
val itemCode = code?.trim().orEmpty()
val itemName = name?.trim().orEmpty()
return when {
itemCode.isNotEmpty() && itemName.isNotEmpty() -> "$itemCode $itemName"
itemCode.isNotEmpty() -> itemCode
itemName.isNotEmpty() -> itemName
else -> "此貨品"
}
}
}

+ 6
- 2
src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeRecordService.kt View File

@@ -11,6 +11,7 @@ import com.ffii.fpsms.modules.stock.enums.StockTakeRoundMode
import com.ffii.fpsms.modules.stock.web.model.*
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.context.annotation.Lazy
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDateTime
@@ -66,7 +67,8 @@ open class StockTakeRecordService(
val stockLedgerRepository: StockLedgerRepository,
val inventoryRepository: InventoryRepository,
val uomConversionRepository: UomConversionRepository,
val itemsRepository: ItemsRepository
val itemsRepository: ItemsRepository,
@Lazy private val stockInLineService: StockInLineService,

) {
private val logger: Logger = LoggerFactory.getLogger(StockTakeRecordService::class.java)
@@ -1213,7 +1215,7 @@ open class StockTakeRecordService(
}
toRoundCard(roundKey, rows, records, floorsBySection)
}
.sortedByDescending { it.stockTakeRoundId }
.sortedBy { it.stockTakeRoundId }
val thisMonth = java.time.YearMonth.now().toString()
val hasOpenRounds = cards.any { it.open }
val completedYearMonths = (
@@ -1248,6 +1250,8 @@ open class StockTakeRecordService(
}
stockTakeRepository.save(st)
}
val sections = rows.mapNotNull { it.stockTakeSection?.trim()?.takeIf { section -> section.isNotBlank() } }
stockInLineService.releasePendingAutoPutaways(sections)
return mapOf(
"success" to true,
"stockTakeRoundId" to roundId,


+ 3
- 0
src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt View File

@@ -60,6 +60,7 @@ open class SuggestedPickLotService(
val itemRepository: ItemsRepository,
val stockOutLineRepository: StockOutLIneRepository,
private val doFloorSupplierSettingsService: DoFloorSupplierSettingsService,
private val stockTakeLocationGuard: StockTakeLocationGuard,
) {

// Calculation Available Qty / Remaining Qty
@@ -1035,6 +1036,7 @@ val basePickOrders = (listOf(pickOrder) + allCompetingPickOrders).distinctBy { i
if (lotId != null && newHoldQty != null && newHoldQty > BigDecimal.ZERO) {
val lot = inventoryLotLineRepository.findById(lotId).orElse(null)
lot?.let {
stockTakeLocationGuard.assertLotLineMovable(it)
val currentHoldQty = it.holdQty ?: BigDecimal.ZERO
val existingHoldQty = existingHoldQtyMap[lotId] ?: BigDecimal.ZERO
val additionalHoldQty = newHoldQty.minus(existingHoldQty)
@@ -1892,6 +1894,7 @@ open fun updateSuggestedLotLineId(suggestedPickLotId: Long, newLotLineId: Long):
}

// Add hold quantity to new lot
stockTakeLocationGuard.assertLotLineMovable(newInventoryLotLine)
val newHoldQty = (newInventoryLotLine.holdQty ?: BigDecimal.ZERO).plus(suggestedQty)
newInventoryLotLine.holdQty = newHoldQty
inventoryLotLineRepository.save(newInventoryLotLine)


+ 3
- 1
src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockInRequest.kt View File

@@ -63,7 +63,9 @@ data class SaveStockInLineRequest(
var escalationLog: SaveEscalationLogRequest? = null,
var warehouseId: Long? = null,
var rejectQty: BigDecimal? = null,
var inventoryLotLines: List<SaveInventoryLotLineForSil>? = null
var inventoryLotLines: List<SaveInventoryLotLineForSil>? = null,
/** True when closing a stock-take round posts a putaway that was held for that round. */
var releaseHeldPutaway: Boolean = false,
)

data class SaveInventoryLotLineForSil (


+ 18
- 0
src/main/resources/db/changelog/changes/20261013_close_stock_take_rounds/01_close_march_open_rounds.sql View File

@@ -0,0 +1,18 @@
--liquibase formatted sql
--changeset fpsms:20261013_close_march_open_stock_take_rounds

-- Close leftover open stock_take headers (planStart Mar 2026).
-- Apr–Sep 2026 was closed by 20261007_close_open_stock_take_rounds.
-- Same as Admin close round: status → completed; fill actualEnd if null.
-- Does not change stock_take_record / stock_take_line.

UPDATE stock_take
SET
status = 'completed',
actualEnd = IFNULL(actualEnd, NOW()),
modified = NOW(),
modifiedBy = 'sql-close-rounds'
WHERE deleted = 0
AND status IN ('pending', 'stockTaking', 'approving')
AND planStart >= '2026-03-01 00:00:00'
AND planStart < '2026-04-01 00:00:00';

+ 6
- 0
src/main/resources/db/changelog/changes/20261013_pending_auto_putaway/01_stock_in_line_pending_auto_putaway.sql View File

@@ -0,0 +1,6 @@
--liquibase formatted sql
--changeset fpsms:20261013_stock_in_line_pending_auto_putaway

ALTER TABLE stock_in_line
ADD COLUMN pendingAutoPutawayWarehouseId BIGINT NULL,
ADD COLUMN pendingAutoPutawayQty DECIMAL(14, 2) NULL;

Loading…
Cancel
Save