| @@ -13,8 +13,6 @@ import org.springframework.stereotype.Service | |||||
| import org.springframework.transaction.annotation.Transactional | import org.springframework.transaction.annotation.Transactional | ||||
| import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository | import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository | ||||
| import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus | import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus | ||||
| import org.springframework.http.HttpStatus | |||||
| import org.springframework.web.server.ResponseStatusException | |||||
| @Service | @Service | ||||
| @@ -30,6 +28,8 @@ open class StockTransferRecordService( | |||||
| ) { | ) { | ||||
| private enum class TransferStockInMode { | private enum class TransferStockInMode { | ||||
| MERGED_EXISTING_LOT, | MERGED_EXISTING_LOT, | ||||
| /** Merged into smallest-id AVAILABLE line among several with same expiry (same lot key at target). */ | |||||
| MERGED_EXISTING_LOT_AMBIGUOUS, | |||||
| CREATED_NEW_LOT | CREATED_NEW_LOT | ||||
| } | } | ||||
| @@ -49,12 +49,17 @@ open class StockTransferRecordService( | |||||
| name = "Stock Transfer Record", | name = "Stock Transfer Record", | ||||
| code = when (stockInMode) { | code = when (stockInMode) { | ||||
| TransferStockInMode.MERGED_EXISTING_LOT -> "MERGED_EXISTING_LOT" | TransferStockInMode.MERGED_EXISTING_LOT -> "MERGED_EXISTING_LOT" | ||||
| TransferStockInMode.MERGED_EXISTING_LOT_AMBIGUOUS -> "MERGED_EXISTING_LOT_AMBIGUOUS" | |||||
| TransferStockInMode.CREATED_NEW_LOT -> "CREATED_NEW_LOT" | TransferStockInMode.CREATED_NEW_LOT -> "CREATED_NEW_LOT" | ||||
| }, | }, | ||||
| type = "success", | type = "success", | ||||
| message = when (stockInMode) { | message = when (stockInMode) { | ||||
| TransferStockInMode.MERGED_EXISTING_LOT -> "Stock transfer completed successfully (merged into existing target lot)" | |||||
| TransferStockInMode.CREATED_NEW_LOT -> "Stock transfer completed successfully (created new target lot)" | |||||
| TransferStockInMode.MERGED_EXISTING_LOT -> | |||||
| "Stock transfer completed successfully (merged into existing target lot)" | |||||
| TransferStockInMode.MERGED_EXISTING_LOT_AMBIGUOUS -> | |||||
| "已併入較早建立的批次(同批號有多筆可用)" | |||||
| TransferStockInMode.CREATED_NEW_LOT -> | |||||
| "Stock transfer completed successfully (created new target lot)" | |||||
| }, | }, | ||||
| errorPosition = null | errorPosition = null | ||||
| ) | ) | ||||
| @@ -120,20 +125,29 @@ open class StockTransferRecordService( | |||||
| status = InventoryLotLineStatus.AVAILABLE, | status = InventoryLotLineStatus.AVAILABLE, | ||||
| ).filter { it.id != inventoryLotLine.id } | ).filter { it.id != inventoryLotLine.id } | ||||
| // Same expiry as source required to merge; repository orders by ill.id ASC — first = earliest-created line | |||||
| val sameExpiryMergeable = mergeCandidates.filter { | |||||
| val targetExpiry = it.inventoryLot?.expiryDate | |||||
| targetExpiry != null && targetExpiry == expiryDate | |||||
| } | |||||
| return when { | return when { | ||||
| mergeCandidates.isEmpty() -> | mergeCandidates.isEmpty() -> | ||||
| stockInLineService.createStockIn(stockInRequest) to TransferStockInMode.CREATED_NEW_LOT | stockInLineService.createStockIn(stockInRequest) to TransferStockInMode.CREATED_NEW_LOT | ||||
| mergeCandidates.size == 1 -> | |||||
| sameExpiryMergeable.isEmpty() -> | |||||
| stockInLineService.createStockIn(stockInRequest) to TransferStockInMode.CREATED_NEW_LOT | |||||
| sameExpiryMergeable.size == 1 -> | |||||
| stockInLineService.createStockInForExistingInventoryLotLine( | stockInLineService.createStockInForExistingInventoryLotLine( | ||||
| stockInRequest, | stockInRequest, | ||||
| mergeCandidates.single(), | |||||
| sameExpiryMergeable.single(), | |||||
| ) to TransferStockInMode.MERGED_EXISTING_LOT | ) to TransferStockInMode.MERGED_EXISTING_LOT | ||||
| else -> | |||||
| throw ResponseStatusException( | |||||
| HttpStatus.BAD_REQUEST, | |||||
| "目標倉在同一品項與批號下有多筆可用的庫存批號行,無法自動併批;請檢查資料。" + | |||||
| " (Multiple AVAILABLE inventory lot lines for same warehouse, item, lotNo.)", | |||||
| ) | |||||
| else -> { | |||||
| val targetLine = sameExpiryMergeable.first() | |||||
| stockInLineService.createStockInForExistingInventoryLotLine( | |||||
| stockInRequest, | |||||
| targetLine, | |||||
| ) to TransferStockInMode.MERGED_EXISTING_LOT_AMBIGUOUS | |||||
| } | |||||
| } | } | ||||
| } | } | ||||