Просмотр исходного кода

Return Chinese HTTP 400 for missing stock UOM and block mixed-UOM lots.

App and Liquibase BEFORE triggers reject one lot spanning two stock-UOM buckets so API/UI match M-X-01 and M-E-01.
fix負數倉
kelvin.yau 19 часов назад
Родитель
Сommit
017e2d21b9
4 измененных файлов: 84 добавлений и 8 удалений
  1. +17
    -1
      src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotLineService.kt
  2. +5
    -2
      src/main/java/com/ffii/fpsms/modules/stock/service/InventoryService.kt
  3. +6
    -5
      src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt
  4. +56
    -0
      src/main/resources/db/changelog/changes/20260910_inventory_stock_uom/07_one_lot_one_stock_uom.sql

+ 17
- 1
src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotLineService.kt Просмотреть файл

@@ -25,8 +25,10 @@ import net.sf.jasperreports.engine.JasperCompileManager
import org.springframework.core.io.ClassPathResource
import org.springframework.data.domain.PageRequest
import java.time.LocalDate
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.server.ResponseStatusException
import java.io.FileNotFoundException
import java.io.IOException
import java.math.BigDecimal
@@ -190,7 +192,21 @@ open class InventoryLotLineService(
val stockUom = request.stockUomId?.let { itemUomRespository.findById(it).getOrNull() }
?: existing?.stockUom
if (stockUom == null) {
throw IllegalArgumentException("stockItemUomId is required for inventory_lot_line")
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "入倉明細缺少庫存單位,無法入帳")
}
// M-E-01: one inventory lot must not mix two stock-UOM buckets (item_uom.uomId).
val lotId = inventoryLot?.id ?: existing?.inventoryLot?.id
val newBucketUomId = stockUom.uom?.id
if (lotId != null && newBucketUomId != null) {
val conflict = inventoryLotLineRepository.findAllByInventoryLotId(lotId).any { sibling ->
sibling.deleted != true &&
sibling.id != existing?.id &&
sibling.stockUom?.uom?.id != null &&
sibling.stockUom!!.uom!!.id != newBucketUomId
}
if (conflict) {
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "同一批次不可混用不同庫存單位")
}
}
val status = request.status?.let { _status -> InventoryLotLineStatus.entries.find { it.value == _status } }



+ 5
- 2
src/main/java/com/ffii/fpsms/modules/stock/service/InventoryService.kt Просмотреть файл

@@ -25,7 +25,9 @@ import org.apache.poi.ss.usermodel.Sheet
import org.apache.poi.ss.usermodel.Workbook
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.springframework.core.io.support.PathMatchingResourcePatternResolver
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Service
import org.springframework.web.server.ResponseStatusException
import java.math.BigDecimal
import java.sql.Timestamp
import java.time.LocalDate
@@ -649,8 +651,9 @@ open class InventoryService(
val warehouse = warehouseRepository.findAll().find { it.code == request.warehouseCode }!!
// val salesUnit = itemUomService.findSalesUnitByItemId(itemId = inventoryLot.item!!.id!!)
val stockUnit = itemUomService.findStockUnitByItemId(itemId = inventoryLot.item!!.id!!)
?: throw IllegalArgumentException(
"stockItemUomId is required: no stockUnit item_uom for itemId=${inventoryLot.item!!.id}",
?: throw ResponseStatusException(
HttpStatus.BAD_REQUEST,
"入倉明細缺少庫存單位,無法入帳",
)
// ChangeList #8: lot-line stockItemUomId must be set so trigger writes inventory.stockUomId.
InventoryLotLine().apply {


+ 6
- 5
src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt Просмотреть файл

@@ -539,8 +539,9 @@ open class StockInLineService(
this.inQty = convertedBaseQty
this.status = InventoryLotLineStatus.AVAILABLE
this.stockUom = stockItemUom
?: throw IllegalArgumentException(
"stockItemUomId is required: no stockUnit item_uom for itemId=${request.itemId}",
?: throw ResponseStatusException(
HttpStatus.BAD_REQUEST,
"入倉明細缺少庫存單位,無法入帳",
)
}
saveLines.add(inventoryLotLine)
@@ -1535,9 +1536,9 @@ open class StockInLineService(
)
}
return itemUomRepository.findFirstByItemIdAndStockUnitIsTrueAndDeletedIsFalseOrderByIdAsc(itemId)
?: throw IllegalArgumentException(
"Stock UOM not found for item: id=$itemId, itemNo=$itemNo. " +
"Ensure at least one item_uom row has stockUnit=1, deleted=0 for this item."
?: throw ResponseStatusException(
HttpStatus.BAD_REQUEST,
"入倉明細缺少庫存單位,無法入帳",
)
}



+ 56
- 0
src/main/resources/db/changelog/changes/20260910_inventory_stock_uom/07_one_lot_one_stock_uom.sql Просмотреть файл

@@ -0,0 +1,56 @@
-- liquibase formatted sql

-- changeset kelvin:20260922-ill-before-insert-one-lot-one-stock-uom splitStatements:false
-- comment: M-E-01 One inventory_lot must not mix two stock UOM buckets.
DROP TRIGGER IF EXISTS `inventory_lot_line_BEFORE_insert_one_stock_uom`;
CREATE DEFINER = CURRENT_USER TRIGGER `inventory_lot_line_BEFORE_insert_one_stock_uom`
BEFORE INSERT
ON `inventory_lot_line`
FOR EACH ROW
BEGIN
IF NEW.stockItemUomId IS NOT NULL
AND NEW.inventoryLotId IS NOT NULL
AND IFNULL(NEW.deleted, 0) = 0
AND EXISTS (
SELECT 1
FROM `inventory_lot_line` ill
INNER JOIN `item_uom` iu_old
ON iu_old.id = ill.stockItemUomId AND IFNULL(iu_old.deleted, 0) = 0
INNER JOIN `item_uom` iu_new
ON iu_new.id = NEW.stockItemUomId AND IFNULL(iu_new.deleted, 0) = 0
WHERE ill.inventoryLotId = NEW.inventoryLotId
AND IFNULL(ill.deleted, 0) = 0
AND iu_old.uomId <> iu_new.uomId
) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '同一批次不可混用不同庫存單位';
END IF;
END;

-- changeset kelvin:20260922-ill-before-update-one-lot-one-stock-uom splitStatements:false
-- comment: M-E-01 Also block UPDATE that would mix stock UOMs on the same lot.
DROP TRIGGER IF EXISTS `inventory_lot_line_BEFORE_update_one_stock_uom`;
CREATE DEFINER = CURRENT_USER TRIGGER `inventory_lot_line_BEFORE_update_one_stock_uom`
BEFORE UPDATE
ON `inventory_lot_line`
FOR EACH ROW
BEGIN
IF NEW.stockItemUomId IS NOT NULL
AND NEW.inventoryLotId IS NOT NULL
AND IFNULL(NEW.deleted, 0) = 0
AND EXISTS (
SELECT 1
FROM `inventory_lot_line` ill
INNER JOIN `item_uom` iu_old
ON iu_old.id = ill.stockItemUomId AND IFNULL(iu_old.deleted, 0) = 0
INNER JOIN `item_uom` iu_new
ON iu_new.id = NEW.stockItemUomId AND IFNULL(iu_new.deleted, 0) = 0
WHERE ill.inventoryLotId = NEW.inventoryLotId
AND IFNULL(ill.deleted, 0) = 0
AND ill.id <> NEW.id
AND iu_old.uomId <> iu_new.uomId
) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '同一批次不可混用不同庫存單位';
END IF;
END;

Загрузка…
Отмена
Сохранить