|
|
|
@@ -21,6 +21,8 @@ import org.springframework.transaction.annotation.Transactional |
|
|
|
import com.ffii.fpsms.modules.master.web.models.MessageResponse |
|
|
|
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository |
|
|
|
import java.math.RoundingMode |
|
|
|
import com.ffii.fpsms.modules.stock.entity.InventoryRepository |
|
|
|
|
|
|
|
@Service |
|
|
|
open class SuggestedPickLotService( |
|
|
|
val suggestedPickLotRepository: SuggestPickLotRepository, |
|
|
|
@@ -29,7 +31,8 @@ open class SuggestedPickLotService( |
|
|
|
val pickOrderLineRepository: PickOrderLineRepository, |
|
|
|
val inventoryLotLineService: InventoryLotLineService, |
|
|
|
val itemUomService: ItemUomService, |
|
|
|
val pickOrderRepository: PickOrderRepository |
|
|
|
val pickOrderRepository: PickOrderRepository, |
|
|
|
val inventoryRepository: InventoryRepository |
|
|
|
) { |
|
|
|
// Calculation Available Qty / Remaining Qty |
|
|
|
open fun calculateRemainingQtyForInfo(inventoryLotLine: InventoryLotLineInfo?): BigDecimal { |
|
|
|
@@ -198,100 +201,114 @@ open class SuggestedPickLotService( |
|
|
|
return suggestedPickLotRepository.saveAllAndFlush(request) |
|
|
|
} |
|
|
|
@Transactional(rollbackFor = [Exception::class]) |
|
|
|
open fun resuggestPickOrder(pickOrderId: Long): MessageResponse { |
|
|
|
try { |
|
|
|
val pickOrder = pickOrderRepository.findById(pickOrderId).orElseThrow() |
|
|
|
|
|
|
|
// Step 1: Find ALL suggestions for this pick order (not just problematic ones) |
|
|
|
val allSuggestions = findAllSuggestionsForPickOrder(pickOrderId) |
|
|
|
|
|
|
|
if (allSuggestions.isEmpty()) { |
|
|
|
return MessageResponse( |
|
|
|
id = pickOrderId, |
|
|
|
name = "No suggestions found", |
|
|
|
code = "SUCCESS", |
|
|
|
type = "resuggest", |
|
|
|
message = "No suggestions to resuggest", |
|
|
|
errorPosition = null |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
// Step 2: Calculate total excess quantity from problematic suggestions |
|
|
|
val problematicSuggestions = findProblematicSuggestions(pickOrderId) |
|
|
|
val totalExcessQty = problematicSuggestions.sumOf { suggestion -> |
|
|
|
val pickOrderLine = pickOrder.pickOrderLines.find { it.id == suggestion.pickOrderLine?.id } |
|
|
|
val inventoryLotLine = suggestion.suggestedLotLine |
|
|
|
|
|
|
|
if (pickOrderLine == null || inventoryLotLine == null) { |
|
|
|
BigDecimal.ZERO |
|
|
|
} else { |
|
|
|
val salesUnit = pickOrderLine.item?.id?.let { itemUomService.findSalesUnitByItemId(it) } |
|
|
|
val ratio = (salesUnit?.ratioN ?: BigDecimal.ONE).divide(salesUnit?.ratioD ?: BigDecimal.ONE, 10, RoundingMode.HALF_UP) |
|
|
|
|
|
|
|
val availableQtyInBaseUnits = (inventoryLotLine.inQty ?: BigDecimal.ZERO) |
|
|
|
.minus(inventoryLotLine.outQty ?: BigDecimal.ZERO) |
|
|
|
.minus(inventoryLotLine.holdQty ?: BigDecimal.ZERO) |
|
|
|
|
|
|
|
val suggestionQtyInBaseUnits = (suggestion.qty ?: BigDecimal.ZERO).multiply(ratio) |
|
|
|
val excessQtyInBaseUnits = suggestionQtyInBaseUnits.minus(availableQtyInBaseUnits) |
|
|
|
|
|
|
|
if (excessQtyInBaseUnits > BigDecimal.ZERO) { |
|
|
|
excessQtyInBaseUnits |
|
|
|
} else { |
|
|
|
BigDecimal.ZERO |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Step 3: Remove ALL existing suggestions and reset holdQty |
|
|
|
val affectedLotLineIds = allSuggestions.mapNotNull { it.suggestedLotLine?.id }.distinct() |
|
|
|
|
|
|
|
val inventoryLotLines = inventoryLotLineRepository.findAllByIdIn(affectedLotLineIds) |
|
|
|
inventoryLotLines.forEach { lotLine -> |
|
|
|
// ✅ FIX: Reset holdQty to 0 for lots being resuggested |
|
|
|
lotLine.holdQty = BigDecimal.ZERO |
|
|
|
} |
|
|
|
inventoryLotLineRepository.saveAll(inventoryLotLines) |
|
|
|
|
|
|
|
// Delete ALL suggestions for this pick order |
|
|
|
suggestedPickLotRepository.deleteAllById(allSuggestions.mapNotNull { it.id }) |
|
|
|
|
|
|
|
// Step 4: Generate completely new suggestions following FEFO |
|
|
|
val newSuggestions = generateCorrectSuggestionsWithExistingHolds(pickOrder) |
|
|
|
|
|
|
|
// Step 5: Save new suggestions and update holdQty |
|
|
|
val savedSuggestions = suggestedPickLotRepository.saveAll(newSuggestions) |
|
|
|
|
|
|
|
val newInventoryLotLines = inventoryLotLineRepository.findAllByIdIn( |
|
|
|
savedSuggestions.mapNotNull { it.suggestedLotLine?.id } |
|
|
|
) |
|
|
|
/* |
|
|
|
savedSuggestions.forEach { suggestion -> |
|
|
|
val lotLine = newInventoryLotLines.find { it.id == suggestion.suggestedLotLine?.id } |
|
|
|
lotLine?.let { |
|
|
|
it.holdQty = (it.holdQty ?: BigDecimal.ZERO).plus(suggestion.qty ?: BigDecimal.ZERO) |
|
|
|
} |
|
|
|
} |
|
|
|
inventoryLotLineRepository.saveAll(newInventoryLotLines) |
|
|
|
*/ |
|
|
|
open fun resuggestPickOrder(pickOrderId: Long): MessageResponse { |
|
|
|
try { |
|
|
|
val pickOrder = pickOrderRepository.findById(pickOrderId).orElseThrow() |
|
|
|
|
|
|
|
// Step 1: Find ALL suggestions for this pick order |
|
|
|
val allSuggestions = findAllSuggestionsForPickOrder(pickOrderId) |
|
|
|
|
|
|
|
if (allSuggestions.isEmpty()) { |
|
|
|
return MessageResponse( |
|
|
|
id = pickOrderId, |
|
|
|
name = "Pick order resuggested successfully", |
|
|
|
name = "No suggestions found", |
|
|
|
code = "SUCCESS", |
|
|
|
type = "resuggest", |
|
|
|
message = "Redistributed ${allSuggestions.size} suggestions following FEFO order", |
|
|
|
message = "No suggestions to resuggest", |
|
|
|
errorPosition = null |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
// Step 2: Calculate total excess quantity from problematic suggestions |
|
|
|
val problematicSuggestions = findProblematicSuggestions(pickOrderId) |
|
|
|
|
|
|
|
// Step 3: Store original holdQty before resetting |
|
|
|
val affectedLotLineIds = allSuggestions.mapNotNull { it.suggestedLotLine?.id }.distinct() |
|
|
|
val inventoryLotLines = inventoryLotLineRepository.findAllByIdIn(affectedLotLineIds) |
|
|
|
|
|
|
|
// ✅ FIX: Store original holdQty values before resetting |
|
|
|
val originalHoldQtyMap = inventoryLotLines.associate { it.id to (it.holdQty ?: BigDecimal.ZERO) } |
|
|
|
|
|
|
|
// Reset holdQty to 0 for all affected lots |
|
|
|
inventoryLotLines.forEach { lotLine -> |
|
|
|
lotLine.holdQty = BigDecimal.ZERO |
|
|
|
} |
|
|
|
inventoryLotLineRepository.saveAll(inventoryLotLines) |
|
|
|
|
|
|
|
// Delete ALL suggestions for this pick order |
|
|
|
suggestedPickLotRepository.deleteAllById(allSuggestions.mapNotNull { it.id }) |
|
|
|
|
|
|
|
// Step 4: Generate completely new suggestions with original holdQty info |
|
|
|
val newSuggestions = generateCorrectSuggestionsWithOriginalHolds(pickOrder, originalHoldQtyMap) |
|
|
|
|
|
|
|
// Step 5: Save new suggestions and update holdQty |
|
|
|
val savedSuggestions = suggestedPickLotRepository.saveAll(newSuggestions) |
|
|
|
|
|
|
|
val newInventoryLotLines = inventoryLotLineRepository.findAllByIdIn( |
|
|
|
savedSuggestions.mapNotNull { it.suggestedLotLine?.id } |
|
|
|
) |
|
|
|
savedSuggestions.forEach { suggestion -> |
|
|
|
val lotLine = newInventoryLotLines.find { it.id == suggestion.suggestedLotLine?.id } |
|
|
|
lotLine?.let { |
|
|
|
val salesUnit = suggestion.pickOrderLine?.item?.id?.let { itemUomService.findSalesUnitByItemId(it) } |
|
|
|
val ratio = BigDecimal.ONE |
|
|
|
val suggestionQtyInBaseUnits = (suggestion.qty ?: BigDecimal.ZERO).multiply(ratio) |
|
|
|
|
|
|
|
it.holdQty = suggestionQtyInBaseUnits |
|
|
|
println("Setting holdQty for lot ${it.id}: suggestion.qty=${suggestion.qty}, ratio=$ratio, holdQty=$suggestionQtyInBaseUnits") |
|
|
|
} |
|
|
|
} |
|
|
|
inventoryLotLineRepository.saveAll(newInventoryLotLines) |
|
|
|
updateInventoryTableAfterResuggest(pickOrder) |
|
|
|
|
|
|
|
return MessageResponse( |
|
|
|
id = pickOrderId, |
|
|
|
name = "Pick order resuggested successfully", |
|
|
|
code = "SUCCESS", |
|
|
|
type = "resuggest", |
|
|
|
message = "Redistributed ${allSuggestions.size} suggestions following FEFO order", |
|
|
|
errorPosition = null |
|
|
|
) |
|
|
|
|
|
|
|
} catch (e: Exception) { |
|
|
|
return MessageResponse( |
|
|
|
id = pickOrderId, |
|
|
|
name = "Failed to resuggest pick order", |
|
|
|
code = "ERROR", |
|
|
|
type = "resuggest", |
|
|
|
message = "Error: ${e.message}", |
|
|
|
errorPosition = null |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private fun updateInventoryTableAfterResuggest(pickOrder: PickOrder) { |
|
|
|
try { |
|
|
|
// Get all item IDs from the pick order |
|
|
|
val itemIds = pickOrder.pickOrderLines.mapNotNull { it.item?.id }.distinct() |
|
|
|
|
|
|
|
itemIds.forEach { itemId -> |
|
|
|
// Calculate onHoldQty (sum of holdQty from available lots only) |
|
|
|
val onHoldQty = inventoryLotLineRepository.findAllByInventoryLotItemIdAndStatus(itemId, "available") |
|
|
|
.sumOf { it.holdQty ?: BigDecimal.ZERO } |
|
|
|
|
|
|
|
// Calculate unavailableQty (sum of inQty from unavailable lots only) |
|
|
|
val unavailableQty = inventoryLotLineRepository.findAllByInventoryLotItemIdAndStatus(itemId, "unavailable") |
|
|
|
.sumOf { it.inQty ?: BigDecimal.ZERO } |
|
|
|
|
|
|
|
// Update the inventory table |
|
|
|
val inventory = inventoryRepository.findByItemId(itemId).orElse(null) |
|
|
|
if (inventory != null) { |
|
|
|
inventory.onHoldQty = onHoldQty |
|
|
|
inventory.unavailableQty = unavailableQty |
|
|
|
inventoryRepository.save(inventory) |
|
|
|
|
|
|
|
println("Updated inventory for item $itemId after resuggest: onHoldQty=$onHoldQty, unavailableQty=$unavailableQty") |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (e: Exception) { |
|
|
|
return MessageResponse( |
|
|
|
id = pickOrderId, |
|
|
|
name = "Failed to resuggest pick order", |
|
|
|
code = "ERROR", |
|
|
|
type = "resuggest", |
|
|
|
message = "Error: ${e.message}", |
|
|
|
errorPosition = null |
|
|
|
) |
|
|
|
println("Error updating inventory table after resuggest: ${e.message}") |
|
|
|
e.printStackTrace() |
|
|
|
} |
|
|
|
} |
|
|
|
private fun findAllSuggestionsForPickOrder(pickOrderId: Long): List<SuggestedPickLot> { |
|
|
|
@@ -313,7 +330,7 @@ open class SuggestedPickLotService( |
|
|
|
|
|
|
|
// Get sales unit conversion ratio |
|
|
|
val salesUnit = itemUomService.findSalesUnitByItemId(itemId) |
|
|
|
val ratio = (salesUnit?.ratioN ?: one).divide(salesUnit?.ratioD ?: one, 10, RoundingMode.HALF_UP) |
|
|
|
val ratio = one |
|
|
|
|
|
|
|
// Convert excess quantity to sales units |
|
|
|
val excessQtyInSalesUnits = excessQtyInBaseUnits.divide(ratio, 0, RoundingMode.HALF_UP) |
|
|
|
@@ -424,7 +441,8 @@ private fun generateCorrectSuggestionsWithExistingHolds(pickOrder: PickOrder): L |
|
|
|
val suggestions = mutableListOf<SuggestedPickLot>() |
|
|
|
val zero = BigDecimal.ZERO |
|
|
|
val one = BigDecimal.ONE |
|
|
|
|
|
|
|
val today = LocalDate.now() |
|
|
|
|
|
|
|
pickOrder.pickOrderLines.forEach { orderLine -> |
|
|
|
val itemId = orderLine.item?.id ?: return@forEach |
|
|
|
val requiredQty = orderLine.qty ?: zero // This is in sales units |
|
|
|
@@ -434,60 +452,170 @@ private fun generateCorrectSuggestionsWithExistingHolds(pickOrder: PickOrder): L |
|
|
|
|
|
|
|
// Get sales unit conversion ratio |
|
|
|
val salesUnit = itemUomService.findSalesUnitByItemId(itemId) |
|
|
|
val ratio = (salesUnit?.ratioN ?: one).divide(salesUnit?.ratioD ?: one, 10, RoundingMode.HALF_UP) |
|
|
|
val ratio = one |
|
|
|
|
|
|
|
// ✅ FIX: Get ALL inventory lots (both available and unavailable) |
|
|
|
val allLots = inventoryLotLineService |
|
|
|
.allInventoryLotLinesByItemIdIn(listOf(itemId)) |
|
|
|
.filter { it.expiryDate.isAfter(today) || it.expiryDate.isEqual(today) } |
|
|
|
.sortedBy { it.expiryDate } |
|
|
|
|
|
|
|
// ✅ FIX: Separate available and unavailable lots |
|
|
|
val availableLots = allLots.filter { it.status == InventoryLotLineStatus.AVAILABLE.value } |
|
|
|
val unavailableLots = allLots.filter { it.status == InventoryLotLineStatus.UNAVAILABLE.value } |
|
|
|
|
|
|
|
// ✅ FIX: Calculate total quantity that was previously held by unavailable lots |
|
|
|
var totalUnavailableHoldQty = BigDecimal.ZERO |
|
|
|
val modifiedUnavailableLots = mutableListOf<InventoryLotLine>() |
|
|
|
|
|
|
|
unavailableLots.forEach { lotInfo -> |
|
|
|
val lot = lotInfo.id?.let { inventoryLotLineRepository.findById(it).orElse(null) } |
|
|
|
lot?.let { |
|
|
|
totalUnavailableHoldQty = totalUnavailableHoldQty.plus(it.holdQty ?: zero) |
|
|
|
// ✅ Reset holdQty for unavailable lots |
|
|
|
it.holdQty = BigDecimal.ZERO |
|
|
|
modifiedUnavailableLots.add(it) // ✅ Keep reference to modified entity |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// ✅ FIX: Save the modified entities (not fresh ones from database) |
|
|
|
if (modifiedUnavailableLots.isNotEmpty()) { |
|
|
|
inventoryLotLineRepository.saveAll(modifiedUnavailableLots) |
|
|
|
println("Reset holdQty for ${modifiedUnavailableLots.size} unavailable lots") |
|
|
|
} |
|
|
|
|
|
|
|
// ✅ FIX: Add the unavailable hold quantity to the required quantity |
|
|
|
//val totalRequiredQty = requiredQty.plus(totalUnavailableHoldQty.divide(ratio, 2, RoundingMode.HALF_UP)) |
|
|
|
val totalRequiredQty = requiredQty |
|
|
|
var remainingQtyInSalesUnits = totalRequiredQty |
|
|
|
|
|
|
|
println("Total required qty (including unavailable): $totalRequiredQty") |
|
|
|
println("Available lots: ${availableLots.size}, Unavailable lots: ${unavailableLots.size}") |
|
|
|
println("Total unavailable hold qty to redistribute: $totalUnavailableHoldQty") |
|
|
|
|
|
|
|
// Generate suggestions from available lots ONLY |
|
|
|
availableLots.forEach { lotInfo -> |
|
|
|
if (remainingQtyInSalesUnits <= zero) return@forEach |
|
|
|
|
|
|
|
val lot = lotInfo.id?.let { inventoryLotLineRepository.findById(it).orElse(null) } |
|
|
|
?: return@forEach |
|
|
|
|
|
|
|
val totalQty = lot.inQty ?: zero |
|
|
|
val outQty = lot.outQty ?: zero |
|
|
|
val holdQty = lot.holdQty ?: zero |
|
|
|
val availableQtyInBaseUnits = totalQty.minus(outQty).minus(holdQty) |
|
|
|
|
|
|
|
val availableQtyInSalesUnits = availableQtyInBaseUnits.divide(ratio, 2, RoundingMode.HALF_UP) |
|
|
|
|
|
|
|
println("Available lot ${lot.id}: availableQtyInBaseUnits=$availableQtyInBaseUnits, availableQtyInSalesUnits=$availableQtyInSalesUnits") |
|
|
|
|
|
|
|
if (availableQtyInSalesUnits <= zero) return@forEach |
|
|
|
|
|
|
|
val assignQtyInSalesUnits = minOf(availableQtyInSalesUnits, remainingQtyInSalesUnits) |
|
|
|
remainingQtyInSalesUnits = remainingQtyInSalesUnits.minus(assignQtyInSalesUnits) |
|
|
|
|
|
|
|
println("Assigning $assignQtyInSalesUnits to lot ${lot.id}, remaining: $remainingQtyInSalesUnits") |
|
|
|
|
|
|
|
if (assignQtyInSalesUnits > zero) { |
|
|
|
suggestions.add(SuggestedPickLot().apply { |
|
|
|
type = SuggestedPickLotType.PICK_ORDER |
|
|
|
suggestedLotLine = lot |
|
|
|
pickOrderLine = orderLine |
|
|
|
qty = assignQtyInSalesUnits |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
println("Ratio: $ratio") |
|
|
|
// If we still have remaining qty, create a suggestion with null lot (insufficient stock) |
|
|
|
if (remainingQtyInSalesUnits > zero) { |
|
|
|
println("Creating insufficient stock suggestion for remaining qty: $remainingQtyInSalesUnits") |
|
|
|
suggestions.add(SuggestedPickLot().apply { |
|
|
|
type = SuggestedPickLotType.PICK_ORDER |
|
|
|
suggestedLotLine = null |
|
|
|
pickOrderLine = orderLine |
|
|
|
qty = remainingQtyInSalesUnits |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return suggestions |
|
|
|
} |
|
|
|
private fun generateCorrectSuggestionsWithOriginalHolds( |
|
|
|
pickOrder: PickOrder, |
|
|
|
originalHoldQtyMap: Map<Long?, BigDecimal> |
|
|
|
): List<SuggestedPickLot> { |
|
|
|
val suggestions = mutableListOf<SuggestedPickLot>() |
|
|
|
val zero = BigDecimal.ZERO |
|
|
|
val one = BigDecimal.ONE |
|
|
|
val today = LocalDate.now() |
|
|
|
|
|
|
|
pickOrder.pickOrderLines.forEach { orderLine -> |
|
|
|
val itemId = orderLine.item?.id ?: return@forEach |
|
|
|
val requiredQty = orderLine.qty ?: zero |
|
|
|
|
|
|
|
println("=== DEBUG: generateCorrectSuggestionsWithOriginalHolds ===") |
|
|
|
println("Item ID: $itemId, Required Qty: $requiredQty") |
|
|
|
|
|
|
|
val salesUnit = itemUomService.findSalesUnitByItemId(itemId) |
|
|
|
val ratio = one |
|
|
|
|
|
|
|
// Get available inventory lots for this item (FEFO order) |
|
|
|
val availableLots = inventoryLotLineService |
|
|
|
.allInventoryLotLinesByItemIdIn(listOf(itemId)) |
|
|
|
.filter { it.status == InventoryLotLineStatus.AVAILABLE.value } |
|
|
|
.filter { it.expiryDate.isAfter(today) || it.expiryDate.isEqual(today) } |
|
|
|
.sortedBy { it.expiryDate } |
|
|
|
|
|
|
|
var remainingQtyInSalesUnits = requiredQty |
|
|
|
|
|
|
|
|
|
|
|
// ✅ Calculate total quantity that needs to be redistributed |
|
|
|
var totalRedistributeQty = requiredQty |
|
|
|
originalHoldQtyMap.forEach { (lotId, originalHoldQty) -> |
|
|
|
if (originalHoldQty > zero) { |
|
|
|
// Check if this lot is now unavailable |
|
|
|
val lot = lotId?.let { inventoryLotLineRepository.findById(it).orElse(null) } |
|
|
|
if (lot?.status == InventoryLotLineStatus.UNAVAILABLE) { |
|
|
|
totalRedistributeQty = totalRedistributeQty.plus(originalHoldQty.divide(ratio, 2, RoundingMode.HALF_UP)) |
|
|
|
println("Adding ${originalHoldQty.divide(ratio, 2, RoundingMode.HALF_UP)} from unavailable lot $lotId") |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var remainingQtyInSalesUnits = totalRedistributeQty |
|
|
|
println("Total qty to redistribute: $totalRedistributeQty") |
|
|
|
|
|
|
|
// Generate suggestions from available lots |
|
|
|
availableLots.forEach { lotInfo -> |
|
|
|
if (remainingQtyInSalesUnits <= zero) return@forEach |
|
|
|
|
|
|
|
// Get the actual InventoryLotLine entity |
|
|
|
val lot = lotInfo.id?.let { inventoryLotLineRepository.findById(it).orElse(null) } |
|
|
|
?: return@forEach |
|
|
|
|
|
|
|
// ✅ FIX: Calculate available qty directly in sales units |
|
|
|
val totalQty = lot.inQty ?: zero |
|
|
|
val outQty = lot.outQty ?: zero |
|
|
|
val holdQty = lot.holdQty ?: zero |
|
|
|
val availableQtyInBaseUnits = totalQty.minus(outQty).minus(holdQty) |
|
|
|
|
|
|
|
// ✅ FIX: Convert to sales units with proper rounding |
|
|
|
val availableQtyInSalesUnits = availableQtyInBaseUnits.divide(ratio, 2, RoundingMode.HALF_UP) |
|
|
|
|
|
|
|
println("Lot ${lot.id}: availableQtyInBaseUnits=$availableQtyInBaseUnits, availableQtyInSalesUnits=$availableQtyInSalesUnits") |
|
|
|
|
|
|
|
if (availableQtyInSalesUnits <= zero) return@forEach |
|
|
|
|
|
|
|
// ✅ FIX: Take what we can from this lot (constrained by available AND remaining) |
|
|
|
val assignQtyInSalesUnits = minOf(availableQtyInSalesUnits, remainingQtyInSalesUnits) |
|
|
|
remainingQtyInSalesUnits = remainingQtyInSalesUnits.minus(assignQtyInSalesUnits) |
|
|
|
|
|
|
|
println("Lot ${lot.id}: assignQtyInSalesUnits=$assignQtyInSalesUnits, remainingQtyInSalesUnits=$remainingQtyInSalesUnits") |
|
|
|
|
|
|
|
if (assignQtyInSalesUnits > zero) { |
|
|
|
suggestions.add(SuggestedPickLot().apply { |
|
|
|
type = SuggestedPickLotType.PICK_ORDER |
|
|
|
suggestedLotLine = lot |
|
|
|
pickOrderLine = orderLine |
|
|
|
qty = assignQtyInSalesUnits // Store in sales units |
|
|
|
qty = assignQtyInSalesUnits |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// If we still have remaining qty, create a suggestion with null lot (insufficient stock) |
|
|
|
if (remainingQtyInSalesUnits > zero) { |
|
|
|
println("Remaining Qty in Sales Units: $remainingQtyInSalesUnits") |
|
|
|
suggestions.add(SuggestedPickLot().apply { |
|
|
|
type = SuggestedPickLotType.PICK_ORDER |
|
|
|
suggestedLotLine = null // No lot available |
|
|
|
suggestedLotLine = null |
|
|
|
pickOrderLine = orderLine |
|
|
|
qty = remainingQtyInSalesUnits |
|
|
|
}) |
|
|
|
|