@@ -25,6 +25,7 @@ import java.io.FileInputStream
import java.math.BigDecimal
import com.ffii.fpsms.modules.stock.entity.Inventory
import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository
import com.ffii.fpsms.modules.stock.entity.InventoryLotRepository
import com.ffii.fpsms.modules.stock.entity.InventoryRepository
import com.ffii.fpsms.modules.stock.entity.StockIn
import com.ffii.fpsms.modules.stock.entity.StockInLineRepository
@@ -61,6 +62,7 @@ open class ItemsService(
private val stockInLineService: StockInLineService,
private val stockInLineRepository: StockInLineRepository,
private val inventoryLotLineRepository: InventoryLotLineRepository,
private val inventoryLotRepository: InventoryLotRepository,
): AbstractBaseEntityService<Items, Long, ItemsRepository>(jdbcDao, itemsRepository) {
private val excelImportPath: String = System.getProperty("user.home") + "/Downloads/StockTakeImport/"
@@ -142,7 +144,7 @@ open class ItemsService(
}
fun writeInventoryCreated(rowNum: Int, stockTakeCount: BigDecimal) {
logWriter.println(" INVENTORY: CREATED - onHandQty: $stockTakeCount, onHoldQty: 0 ")
logWriter.println(" INVENTORY: CREATED - onHandQty: $stockTakeCount")
}
fun writeInventoryError(rowNum: Int, message: String) {
@@ -160,6 +162,7 @@ open class ItemsService(
fun writeSummary(
successCount: Int,
errorCount: Int,
duplicateSkippedCount: Int,
inventoryCreatedCount: Int,
inventoryFailedCount: Int,
missingInventoryItems: List<String>,
@@ -173,6 +176,7 @@ open class ItemsService(
logWriter.println()
logWriter.println("Items Patched: $successCount")
logWriter.println("Items with Errors: $errorCount")
logWriter.println("Duplicate Items Skipped: $duplicateSkippedCount")
logWriter.println()
logWriter.println("Inventory Records Created: $inventoryCreatedCount")
logWriter.println("Inventory Records Failed: $inventoryFailedCount")
@@ -606,8 +610,9 @@ open class ItemsService(
val sheet: Sheet = workbook.getSheetAt(0)
// Column indices (0-indexed)
val COLUMN_INVENTORY_SHEET_INDEX = 4 // Column E
val COLUMN_CODE_INDEX = 7 // Column H
val COLUMN_COMPANY_INDEX = 1 // Column B
val COLUMN_INVENTORY_SHEET_INDEX = 4 // Column E
val COLUMN_CODE_INDEX = 7 // Column H
val COLUMN_TYPE_INDEX = 9 // Column J
val COLUMN_STORE_ID_INDEX = 13 // Column N
val COLUMN_STORE_LOCATION_INDEX = 14 // Column O
@@ -617,12 +622,13 @@ open class ItemsService(
val COLUMN_MTMS_PICK_ROUTING_ID_INDEX = 18 // Column S
val COLUMN_ON_HOLD_QTY_INDEX = 21 // Column V
val START_ROW_INDEX = 4 // Starting from row 4 (0-indexed)
val START_ROW_INDEX = 3 // Starting from row 4 (0-indexed)
var successCount = 0
var errorCount = 0
var inventoryCreatedCount = 0
var inventoryFailedCount = 0
var duplicateSkippedCount = 0
val errors = mutableListOf<String>()
val missingInventoryItems = mutableListOf<String>()
@@ -664,28 +670,12 @@ open class ItemsService(
continue
}
// Get item code - Debug
// Get item code
val itemCode = try {
val cell = row.getCell(COLUMN_CODE_INDEX)
logger.info("=== DEBUG Row ${i + 1} ===")
logger.info("Reading from Column H (index ${COLUMN_CODE_INDEX})")
if (cell == null) {
logger.warn("Row ${i + 1}: Cell is NULL")
} else {
logger.info("Row ${i + 1}: Cell type = ${cell.cellType}")
val cellI = row.getCell(COLUMN_CODE_INDEX + 1)
logger.info("Column I (index ${COLUMN_CODE_INDEX + 1}) = '${getCellStringValue(cellI)}'")
}
val extractedCode = getCellStringValue(cell)?.trim()
logger.info("Extracted code = '$extractedCode'")
logger.info("=== END DEBUG Row ${i + 1} ===")
extractedCode
getCellStringValue(cell)?.trim()
} catch (e: Exception) {
logger.error("Import Error (Row ${i + 1} - Code Error): ${e.message}")
logger.error("Row ${i + 1}: Failed to read code - ${e.message}")
logWriter.writeRowError(i, "Failed to read code - ${e.message}")
errorCount++
errors.add("Row ${i + 1}: Failed to read code - ${e.message}")
@@ -719,16 +709,18 @@ open class ItemsService(
if (processedItemIds.containsKey(item.id!!)) {
val previousRow = processedItemIds[item.id!!]!! + 1
logger.warn("Row ${i + 1}: Item '$itemCode' was already processed at row $previousRow - Creating duplicate StockTakeLine")
logWriter.writeRowError(i, "WARNING: Item '$itemCode' appears multiple times (first at row $previousRow)")
duplicateSkippedCount++
logger.warn("Row ${i + 1}: Item '$itemCode' was already processed at row $previousRow - Skipping duplicate")
logWriter.writeRowSkipped(i, "Item '$itemCode' already processed at row $previousRow - Skipped")
continue // Skip to next item
}
processedItemIds[item.id!!] = i // Track this item
processedItemIds[item.id!!] = i
// Create StockTakeLine (for each row/item)
// Read quantity from Column V (same as inventory)
val qty = try {
val cell = row.getCell(COLUMN_ON_HOLD_QTY_INDEX)
when {
val extractedValue = when {
cell == null -> BigDecimal.ZERO
cell.cellType == CellType.NUMERIC -> cell.numericCellValue.toBigDecimal()
cell.cellType == CellType.STRING -> {
@@ -738,6 +730,8 @@ open class ItemsService(
}
else -> BigDecimal.ZERO
}
// Add 500 to the extracted value
extractedValue + BigDecimal(500)
} catch (e: Exception) {
logger.warn("Row ${i + 1}: Failed to read quantity from column V - ${e.message}")
BigDecimal.ZERO
@@ -767,27 +761,161 @@ open class ItemsService(
}
logger.info("Row ${i + 1}: Created StockTakeLine (ID: ${savedStockTakeLine.id}) for item '$itemCode'")
try {
// Read Company (Column B)
val company = getCellStringValue(row.getCell(COLUMN_COMPANY_INDEX))?.trim()
if (!company.isNullOrBlank()) {
item.company = company
logger.info("Row ${i + 1}: Company read from Column B: '$company'")
}
// Read InventorySheet (Column E)
val inventorySheet = getCellStringValue(row.getCell(COLUMN_INVENTORY_SHEET_INDEX))?.trim()
if (!inventorySheet.isNullOrBlank()) {
item.inventorySheet = inventorySheet
}
// Read Type (Column J)
val type = getCellStringValue(row.getCell(COLUMN_TYPE_INDEX))?.trim()
if (!type.isNullOrBlank()) {
item.type = type
}
// Read Store ID (Column N) - String type
val storeId = getCellStringValue(row.getCell(COLUMN_STORE_ID_INDEX))?.trim()
if (!storeId.isNullOrBlank()) {
item.store_id = storeId
}
// Read Store Location (Column O)
val storeLocation = getCellStringValue(row.getCell(COLUMN_STORE_LOCATION_INDEX))?.trim()
if (!storeLocation.isNullOrBlank()) {
item.storeLocation = storeLocation
}
// Read Warehouse (Column P)
val warehouseFromExcel = getCellStringValue(row.getCell(COLUMN_WAREHOUSE_INDEX))?.trim()
if (!warehouseFromExcel.isNullOrBlank()) {
item.warehouse = warehouseFromExcel
}
// Read Area (Column Q)
val area = getCellStringValue(row.getCell(COLUMN_AREA_INDEX))?.trim()
if (!area.isNullOrBlank()) {
item.area = area
}
// Read Slot (Column R)
val slot = getCellStringValue(row.getCell(COLUMN_SLOT_INDEX))?.trim()
if (!slot.isNullOrBlank()) {
item.slot = slot
}
// Combine Column N (Store ID), Column P (Warehouse), Column Q (Area), and Column R (Slot) to form LocationCode
// Format: "N-P-Q-R" (e.g., "ST01-WH01-A01-S01")
val locationCodeParts = mutableListOf<String>()
if (!storeId.isNullOrBlank()) {
locationCodeParts.add(storeId)
}
if (!warehouseFromExcel.isNullOrBlank()) {
locationCodeParts.add(warehouseFromExcel)
}
if (!area.isNullOrBlank()) {
locationCodeParts.add(area)
}
if (!slot.isNullOrBlank()) {
locationCodeParts.add(slot)
}
val locationCodeFromExcel = if (locationCodeParts.isNotEmpty()) {
locationCodeParts.joinToString("-")
} else {
null
}
if (!locationCodeFromExcel.isNullOrBlank()) {
item.LocationCode = locationCodeFromExcel
logger.info("Row ${i + 1}: LocationCode created: '$locationCodeFromExcel' (Format: N-P-Q-R, N='$storeId', P='$warehouseFromExcel', Q='$area', R='$slot')")
} else {
logger.warn("Row ${i + 1}: LocationCode cannot be created - Column N='$storeId', P='$warehouseFromExcel', Q='$area', R='$slot' (all empty or null)")
}
// Read MTMS Pick Routing ID (Column S) - Int type
val mtmsPickRoutingId = try {
val cell = row.getCell(COLUMN_MTMS_PICK_ROUTING_ID_INDEX)
when {
cell == null -> null
cell.cellType == CellType.NUMERIC -> cell.numericCellValue.toInt()
cell.cellType == CellType.STRING -> {
val strValue = cell.stringCellValue.trim()
if (strValue.isNotBlank()) strValue.toIntOrNull() else null
}
else -> null
}
} catch (e: Exception) {
logger.warn("Row ${i + 1}: Failed to read MTMS Pick Routing ID from column S - ${e.message}")
null
}
if (mtmsPickRoutingId != null) {
item.MTMSPickRoutingID = mtmsPickRoutingId
}
} catch (e: Exception) {
logger.error("Row ${i + 1}: Failed to read Excel columns for item '$itemCode' - ${e.message}")
logWriter.writeRowError(i, "Failed to read Excel columns - ${e.message}")
// Continue processing - don't fail the entire row
}
//Create StockInLine (for each row/item)
// Get warehouse from item's LocationCode
// Get warehouse from item's LocationCode, inventorySheet, company, and storeLocation
// Match: item.LocationCode -> warehouse.code AND item.inventorySheet -> warehouse.stockTakeTable AND item.company -> warehouse.company AND item.storeLocation -> warehouse.storeLocation
val locationCode = item.LocationCode
val warehouse = if (locationCode != null) {
val inventorySheet = item.inventorySheet
val company = item.company
val storeLocation = item.storeLocation
logger.info("Row ${i + 1}: Looking up warehouse with LocationCode: '$locationCode', inventorySheet: '$inventorySheet', company: '$company', storeLocation: '$storeLocation'")
val warehouse = if (locationCode != null && locationCode.isNotBlank() &&
inventorySheet != null && inventorySheet.isNotBlank() &&
company != null && company.isNotBlank() &&
storeLocation != null && storeLocation.isNotBlank()) {
try {
warehouseService.findByCode(locationCode)
val foundWarehouse = warehouseService.findByCodeAndStockTakeTableAndCompanyAndStoreLocation(locationCode, inventorySheet, company, storeLocation)
if (foundWarehouse != null) {
logger.info("Row ${i + 1}: Found warehouse: code='${foundWarehouse.code}', stockTakeTable='${foundWarehouse.stockTakeTable}', company='${foundWarehouse.company}', storeLocation='${foundWarehouse.storeLocation}', id=${foundWarehouse.id}")
} else {
logger.warn("Row ${i + 1}: Warehouse not found in database for LocationCode: '$locationCode', inventorySheet: '$inventorySheet', company: '$company', storeLocation: '$storeLocation'")
}
foundWarehouse
} catch (e: Exception) {
logger.warn("Row ${i + 1}: Failed to find warehouse with LocationCode '$locationCode' - ${e.message}")
logger.warn("Row ${i + 1}: Failed to find warehouse with LocationCode '$locationCode', inventorySheet '$inventorySheet', company '$company', storeLocation '$storeLocation' - ${e.message}")
null
}
} else {
logger.warn("Row ${i + 1}: Item '$itemCode' has no LocationCode, warehouse will be null")
if (locationCode.isNullOrBlank()) {
logger.warn("Row ${i + 1}: Item '$itemCode' has no LocationCode (null or blank), warehouse will be null")
}
if (inventorySheet.isNullOrBlank()) {
logger.warn("Row ${i + 1}: Item '$itemCode' has no inventorySheet (null or blank), warehouse will be null")
}
if (company.isNullOrBlank()) {
logger.warn("Row ${i + 1}: Item '$itemCode' has no company (null or blank), warehouse will be null")
}
if (storeLocation.isNullOrBlank()) {
logger.warn("Row ${i + 1}: Item '$itemCode' has no storeLocation (null or blank), warehouse will be null")
}
null
}
// Calculate expiry date (+30 days)
val expiryDate = LocalDateTime.now().plusDays(30).toLocalDate()
// Generate lotNo: LT-YYYYMMDD-itemCode
// Generate productLotNo: LT-YYYYMMDD-itemCode (for productLotNo field)
val dateStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))
val lotNo = "LT-$dateStr-$itemCode"
val productLotNo = "LT-$dateStr-$itemCode"
// Generate lotNo: LT-YYYYMMDD-XXXX (sequential number, same for StockInLine and InventoryLot)
val lotNo = stockInLineService.assignLotNo()
// Generate dnNo: DN-YYYYMMDD-itemCode
val dnNo = "DN-$dateStr-$itemCode"
@@ -801,7 +929,8 @@ open class ItemsService(
expiryDate = expiryDate,
warehouseId = warehouse?.id,
stockTakeLineId = savedStockTakeLine.id,
dnNo = dnNo, // Set dnNo
dnNo = dnNo,
productLotNo = productLotNo, // LT-YYYYMMDD-itemCode
qcAccept = true,
status = StockInLineStatus.PENDING.status
)
@@ -841,62 +970,148 @@ open class ItemsService(
null
}
// Update StockInLine to RECEIVED status (this will trigger InventoryLot & InventoryLotLine creation)
// Step 7a: First update to PENDING with qcAccept=true to create InventoryLot
// This will create InventoryLot and automatically set status to RECEIVED
saveStockInLineReq.apply {
id = savedStockInLine.id
status = StockInLineStatus.RECEIVED .status
status = StockInLineStatus.PENDING .status
this.dnNo = dnNo
this.inventoryLotLines = inventoryLotLines
this.productLotNo = lotNo
this.qcAccept = true
this.acceptedQty = qty
this.acceptQty = qty
// Don't set inventoryLotLines yet
}
val finalStockInLine = try {
val updatedForLot = try {
stockInLineService.update(saveStockInLineReq)
} catch (e: Exception) {
logger.error("Row ${i + 1}: Failed to update StockInLine to RECEIVED for item '$itemCode' - ${e.message}")
logWriter.writeRowError(i, "Failed to update StockInLine to RECEIVED - ${e.message}")
logger.error("Row ${i + 1}: Failed to update StockInLine to PENDING (for InventoryLot) for item '$itemCode' - ${e.message}")
logWriter.writeRowError(i, "Failed to update StockInLine to PENDING - ${e.message}")
errorCount++
errors.add("Row ${i + 1}: Failed to update StockInLine to RECEIVED - ${e.message}")
errors.add("Row ${i + 1}: Failed to update StockInLine to PENDING - ${e.message}")
continue
}
logger.info("Row ${i + 1}: Updated StockInLine to RECEIVED (ID: ${finalStockInLine.id}) for item '$itemCode' with lotNo: $lotNo, dnNo: $dnNo")
val inventoryLotLine = if (finalStockInLine.id != null && warehouse?.id != null) {
try {
// Check if update was successful
if (updatedForLot?.id == null) {
logger.error("Row ${i + 1}: StockInLine update returned null ID for item '$itemCode'")
logWriter.writeRowError(i, "StockInLine update returned null")
errorCount++
errors.add("Row ${i + 1}: StockInLine update returned null")
continue
}
logger.info("Row ${i + 1}: Updated StockInLine to PENDING (ID: ${updatedForLot.id}) - InventoryLot should be created")
// Refresh StockInLine entity to get the InventoryLot that was just created
val refreshedStockInLine = stockInLineRepository.findById(updatedForLot.id!!).orElse(null)
if (refreshedStockInLine?.inventoryLot == null) {
logger.warn("Row ${i + 1}: InventoryLot was not created for StockInLine ${updatedForLot.id}")
logWriter.writeRowError(i, "InventoryLot was not created")
} else {
// Update InventoryLot's lotNo to match StockInLine's lotNo (LT-YYYYMMDD-XXXX format)
val inventoryLot = refreshedStockInLine.inventoryLot!!
if (inventoryLot.lotNo != lotNo) {
inventoryLot.lotNo = lotNo
inventoryLotRepository.saveAndFlush(inventoryLot)
logger.info("Row ${i + 1}: Updated InventoryLot lotNo to '$lotNo' to match StockInLine (InventoryLot ID: ${inventoryLot.id})")
}
logger.info("Row ${i + 1}: InventoryLot created (ID: ${inventoryLot.id}, lotNo: ${inventoryLot.lotNo})")
}
// Step 7b: Second update to RECEIVED with inventoryLotLines to create InventoryLotLine
// Only proceed if warehouse is available (InventoryLotLine requires warehouse)
if (warehouse?.id == null) {
logger.warn("Row ${i + 1}: Warehouse is null (no LocationCode), skipping InventoryLotLine creation for item '$itemCode'")
logWriter.writeRowError(i, "Warehouse is null - InventoryLotLine will not be created")
// Continue without InventoryLotLine - StockTakeLine will not be updated with inventoryLotLineId
} else {
// Warehouse is available, proceed with InventoryLotLine creation
saveStockInLineReq.apply {
id = savedStockInLine.id
status = StockInLineStatus.RECEIVED.status // Explicitly set to RECEIVED
this.dnNo = dnNo
this.productLotNo = productLotNo
this.acceptedQty = qty
this.acceptQty = qty
this.inventoryLotLines = inventoryLotLines // REQUIRED for InventoryLotLine creation
}
val finalStockInLine = try {
stockInLineService.update(saveStockInLineReq)
} catch (e: Exception) {
logger.error("Row ${i + 1}: Failed to update StockInLine to RECEIVED (for InventoryLotLine) for item '$itemCode' - ${e.message}")
logWriter.writeRowError(i, "Failed to update StockInLine to RECEIVED - ${e.message}")
errorCount++
errors.add("Row ${i + 1}: Failed to update StockInLine to RECEIVED - ${e.message}")
continue
}
if (finalStockInLine?.id == null) {
logger.error("Row ${i + 1}: StockInLine update to RECEIVED returned null ID for item '$itemCode'")
logWriter.writeRowError(i, "StockInLine update to RECEIVED returned null")
errorCount++
errors.add("Row ${i + 1}: StockInLine update to RECEIVED returned null")
continue
}
logger.info("Row ${i + 1}: Updated StockInLine to RECEIVED (ID: ${finalStockInLine.id}) for item '$itemCode' with lotNo: $lotNo, dnNo: $dnNo")
// Step 8: Find InventoryLotLine and update StockTakeLine
// Flush first to ensure InventoryLotLine is persisted
stockInLineRepository.flush()
// Verify InventoryLotLine was created by querying directly
val inventoryLotLine = try {
inventoryLotLineRepository.findByInventoryLotStockInLineIdAndWarehouseId(
inventoryLotStockInLineId = finalStockInLine.id!!,
warehouseId = warehouse.id!!
)
} catch (e: Exception) {
logger.warn("Row ${i + 1}: Failed to find InventoryLotLine for StockInLine ${finalStockInLine.id} and warehouse ${warehouse.id} - ${e.message}")
null
logger.warn("Row ${i + 1}: Failed to find InventoryLotLine - ${e.message}")
// Try alternative: find by InventoryLot
try {
val refreshedStockInLine = stockInLineRepository.findById(finalStockInLine.id!!).orElse(null)
refreshedStockInLine?.inventoryLot?.id?.let { inventoryLotId ->
inventoryLotLineRepository.findAllByInventoryLotId(inventoryLotId)
.firstOrNull { it.warehouse?.id == warehouse.id }
}
} catch (e2: Exception) {
logger.warn("Row ${i + 1}: Alternative search failed - ${e2.message}")
null
}
}
} else {
logger.warn("Row ${i + 1}: Cannot find InventoryLotLine - finalStockInLine.id: ${finalStockInLine.id}, warehouse.id: ${warehouse?.id}")
null
}
if (inventoryLotLine != null) {
val updateStockTakeLineReq = SaveStockTakeLineRequest(
id = savedStockTakeLine.id,
stockTakeId = savedStockTake.id,
initialQty = savedStockTakeLine.initialQty,
finalQty = savedStockTakeLine.finalQty,
uomId = savedStockTakeLine.uom?.id,
status = StockTakeLineStatus.COMPLETED.value,
completeDate = LocalDateTime.now(),
inventoryLotLineId = inventoryLotLine.id,
remarks = savedStockTakeLine.remarks
)
try {
stockTakeLineService.saveStockTakeLine(updateStockTakeLineReq)
logger.info("Row ${i + 1}: Updated StockTakeLine (ID: ${savedStockTakeLine.id}) to COMPLETED with inventoryLotLineId: ${inventoryLotLine.id}")
} catch (e: Exception) {
logger.error("Row ${i + 1}: Failed to update StockTakeLine for item '$itemCode' - ${e.message}")
logWriter.writeRowError(i, "Failed to update StockTakeLine - ${e.message}")
// Don't continue here - this is not critical, just log the error
if (inventoryLotLine != null) {
logger.info("Row ${i + 1}: InventoryLotLine found (ID: ${inventoryLotLine.id}, qty: ${inventoryLotLine.inQty})")
} else {
logger.warn("Row ${i + 1}: InventoryLotLine not found for StockInLine ${finalStockInLine.id}")
logWriter.writeRowError(i, "InventoryLotLine not found")
}
if (inventoryLotLine != null) {
val updateStockTakeLineReq = SaveStockTakeLineRequest(
id = savedStockTakeLine.id,
stockTakeId = savedStockTake.id,
initialQty = savedStockTakeLine.initialQty,
finalQty = savedStockTakeLine.finalQty,
uomId = savedStockTakeLine.uom?.id,
status = StockTakeLineStatus.COMPLETED.value,
completeDate = LocalDateTime.now(),
inventoryLotLineId = inventoryLotLine.id,
remarks = savedStockTakeLine.remarks
)
try {
stockTakeLineService.saveStockTakeLine(updateStockTakeLineReq)
logger.info("Row ${i + 1}: Updated StockTakeLine (ID: ${savedStockTakeLine.id}) to COMPLETED with inventoryLotLineId: ${inventoryLotLine.id}")
} catch (e: Exception) {
logger.error("Row ${i + 1}: Failed to update StockTakeLine for item '$itemCode' - ${e.message}")
logWriter.writeRowError(i, "Failed to update StockTakeLine - ${e.message}")
}
} else {
logger.warn("Row ${i + 1}: InventoryLotLine not found, StockTakeLine will not be updated with inventoryLotLineId")
logWriter.writeRowError(i, "InventoryLotLine not found for StockTakeLine update")
}
} else {
logger.warn("Row ${i + 1}: InventoryLotLine not found, StockTakeLine will not be updated with inventoryLotLineId")
logWriter.writeRowError(i, "InventoryLotLine not found for StockTakeLine update")
}
// Update fields - Replace existing data with Excel data
@@ -936,21 +1151,27 @@ open class ItemsService(
missingInventoryItems.add("Row ${i + 1}: Item '$itemCode' - Item ID is null")
inventoryFailedCount++
} else {
// Check if inventory exists
val existingInventory = try {
inventoryRepository.findByItemId(itemId).orElse(null)
// Check if inventory exists - Find ALL inventory records for this item
val existingInventories = try {
inventoryRepository.findAll ByItemIdAndDeletedIsFalse (itemId)
} catch (e: Exception) {
logger.warn("Row ${i + 1}: Failed to find existing inventory for item '$itemCode' (ID: $itemId) - ${e.message}")
null
emptyList()
}
// Delete existing inventory if it exists
if (existingInventory != null ) {
// Delete ALL existing inventory records if any exist
if (existingInventories.isNotEmpty() ) {
try {
inventoryRepository.delete(existingInventory)
val deletedCount = existingInventories.size
existingInventories.forEach { inventory ->
inventoryRepository.delete(inventory)
}
inventoryRepository.flush()
logger.info("Row ${i + 1}: Deleted existing inventory record for item '${item.name}' (code: $itemCode)")
logWriter.writeInventoryDeleted(i, existingInventory.id)
logger.info("Row ${i + 1}: Deleted $deletedCount existing inventory record(s) for item '${item.name}' (code: $itemCode)")
if (deletedCount > 1) {
logger.warn("Row ${i + 1}: Found $deletedCount duplicate inventory records for item '$itemCode' - all deleted")
}
logWriter.writeInventoryDeleted(i, existingInventories.first().id)
} catch (e: Exception) {
logger.warn("Row ${i + 1}: Failed to delete existing inventory for item '$itemCode' - ${e.message}")
logWriter.writeInventoryDeleteError(i, e.message ?: "Unknown error")
@@ -960,16 +1181,18 @@ open class ItemsService(
// Read quantity from Column V (will be used for onHandQty)
val stockTakeCount = try {
val cell = row.getCell(COLUMN_ON_HOLD_QTY_INDEX)
when {
val extractedValue = when {
cell == null -> BigDecimal.ZERO
cell.cellType == CellType.NUMERIC -> cell.numericCellValue.toBigDecimal()
cell.cellType == CellType.STRING -> {
val strValue = cell.stringCellValue.trim()
if (strValue.isNotBlank()) strValue.toBigDecimalOrNull() ?: BigDecimal.ZERO
else BigDecimal.ZERO
else strValue.toBigDecimalOrNull() ?: BigDecimal.ZERO
}
else -> BigDecimal.ZERO
}
// Add 500 to the extracted value
extractedValue + BigDecimal(500)
} catch (e: Exception) {
logger.warn("Row ${i + 1}: Failed to read quantity from column V - ${e.message}")
BigDecimal.ZERO
@@ -1032,12 +1255,13 @@ open class ItemsService(
errorCount = errorCount,
inventoryCreatedCount = inventoryCreatedCount,
inventoryFailedCount = inventoryFailedCount,
duplicateSkippedCount = duplicateSkippedCount,
missingInventoryItems = missingInventoryItems,
errors = errors
)
logger.info("--------- End - Patch Items from Excel -------")
logger.info("Success: $successCount, Errors: $errorCount")
logger.info("Success: $successCount, Errors: $errorCount, Duplicates Skipped: $duplicateSkippedCount ")
logger.info("Inventory Created: $inventoryCreatedCount, Inventory Failed: $inventoryFailedCount")
logger.info("Log file saved to: ${logWriter.getLogFilePath()}")