| @@ -829,113 +829,97 @@ open class BomService( | |||||
| return bomProcessMaterialRepository.saveAndFlush(bomProcessMaterial) | return bomProcessMaterialRepository.saveAndFlush(bomProcessMaterial) | ||||
| } | } | ||||
| private fun importExcelBomMaterial(bom: Bom, sheet: Sheet) { | private fun importExcelBomMaterial(bom: Bom, sheet: Sheet) { | ||||
| var bomProcessMatRequest = ImportBomProcessMaterialRequest() | |||||
| var startRowIndex = 10 | |||||
| val endRowIndex = 30 | |||||
| var startColumnIndex = 0 | |||||
| val endColumnIndex = 10 | |||||
| while (startRowIndex < endRowIndex) { | |||||
| val tempRow = sheet.getRow(startRowIndex) | |||||
| val tempCell = tempRow.getCell(startColumnIndex) | |||||
| if (tempCell != null && tempCell.cellType == CellType.STRING && tempCell.stringCellValue.trim() == "材料編號") { | |||||
| //println("last: $startRowIndex") | |||||
| startRowIndex++ | |||||
| val startCol = 0 | |||||
| val endCol = 10 | |||||
| val maxRowIndex = 200 | |||||
| // 1) 找到「材料編號」表头 | |||||
| var headerRowIndex = -1 | |||||
| var searchRowIndex = 10 | |||||
| while (searchRowIndex < maxRowIndex) { | |||||
| val row = sheet.getRow(searchRowIndex) | |||||
| val cell = row?.getCell(startCol) | |||||
| if (cell != null && | |||||
| cell.cellType == CellType.STRING && | |||||
| cell.stringCellValue.trim() == "材料編號" | |||||
| ) { | |||||
| headerRowIndex = searchRowIndex | |||||
| break | break | ||||
| } | } | ||||
| startRowIndex++ | |||||
| searchRowIndex++ | |||||
| } | } | ||||
| var bomMatRequest = ImportBomMatRequest( | |||||
| bom = bom | |||||
| ) | |||||
| // println("starting new loop") | |||||
| while (startRowIndex != endRowIndex || startColumnIndex != endColumnIndex) { | |||||
| val tempRow = sheet.getRow(startRowIndex) | |||||
| val tempCell = tempRow.getCell(startColumnIndex) | |||||
| if (startColumnIndex == 0 && (tempCell == null || tempCell.cellType == CellType.BLANK)) { | |||||
| if (headerRowIndex == -1) { | |||||
| println("importExcelBomMaterial: 找不到『材料編號』表頭,略過材料匯入") | |||||
| return | |||||
| } | |||||
| // 2) 从表头下一行开始读,直到 col0 空白 | |||||
| var rowIdx = headerRowIndex + 1 | |||||
| while (rowIdx < maxRowIndex) { | |||||
| val row = sheet.getRow(rowIdx) ?: break | |||||
| val firstCell = row.getCell(0) | |||||
| if (firstCell == null || firstCell.cellType == CellType.BLANK) { | |||||
| break | break | ||||
| } else { | |||||
| try { | |||||
| when (startColumnIndex) { | |||||
| } | |||||
| val bomMatRequest = ImportBomMatRequest(bom = bom) | |||||
| val bomProcessMatRequest = ImportBomProcessMaterialRequest() | |||||
| try { | |||||
| for (colIdx in startCol..endCol) { | |||||
| val cell = row.getCell(colIdx) ?: continue | |||||
| when (colIdx) { | |||||
| 0 -> { | 0 -> { | ||||
| // println("rowIndex: $startRowIndex") | |||||
| val nameRow = sheet.getRow(startRowIndex) | |||||
| val nameCell = nameRow.getCell(1) | |||||
| println(tempCell.stringCellValue.trim()) | |||||
| val item = itemsRepository.findByCodeAndDeletedFalse(tempCell.stringCellValue.trim()) | |||||
| ?: itemsRepository.findByNameAndDeletedFalse(nameCell.stringCellValue.trim()) | |||||
| // println("getting item.....:") | |||||
| // println(item) | |||||
| val nameCell = row.getCell(1) | |||||
| val itemCode = cell.stringCellValue.trim() | |||||
| val itemName = nameCell?.stringCellValue?.trim() | |||||
| val item = itemsRepository.findByCodeAndDeletedFalse(itemCode) | |||||
| ?: itemName?.let { itemsRepository.findByNameAndDeletedFalse(it) } | |||||
| bomMatRequest.item = item | bomMatRequest.item = item | ||||
| } | } | ||||
| 2-> { | |||||
| bomMatRequest.qty = tempCell.numericCellValue.toBigDecimal() | |||||
| 2 -> { | |||||
| bomMatRequest.qty = cell.numericCellValue.toBigDecimal() | |||||
| } | } | ||||
| 3 -> { | 3 -> { | ||||
| val uom = uomConversionRepository.findByCodeAndDeletedFalse(tempCell.stringCellValue.trim()) | |||||
| val uomCode = cell.stringCellValue.trim() | |||||
| val uom = uomConversionRepository.findByCodeAndDeletedFalse(uomCode) | |||||
| bomMatRequest.uom = uom | bomMatRequest.uom = uom | ||||
| bomMatRequest.uomName = uom?.udfudesc | bomMatRequest.uomName = uom?.udfudesc | ||||
| } | } | ||||
| 6 -> { | 6 -> { | ||||
| bomMatRequest.saleQty = tempCell.numericCellValue.toBigDecimal() | |||||
| bomMatRequest.saleQty = cell.numericCellValue.toBigDecimal() | |||||
| } | } | ||||
| 7 -> { | 7 -> { | ||||
| val salesUnitCodeStr = tempCell.stringCellValue.trim() | |||||
| val normalizedCode = if (salesUnitCodeStr.equals("Litter", ignoreCase = true)) "L" else salesUnitCodeStr | |||||
| val salesUnit = uomConversionRepository.findByCodeAndDeletedFalse(tempCell.stringCellValue.trim()) | |||||
| val salesUnitCodeStr = cell.stringCellValue.trim() | |||||
| val salesUnit = uomConversionRepository.findByCodeAndDeletedFalse(salesUnitCodeStr) | |||||
| bomMatRequest.salesUnit = salesUnit | bomMatRequest.salesUnit = salesUnit | ||||
| // bomMatRequest.salesUnitCode = salesUnit?.udfudesc | |||||
| bomMatRequest.salesUnitCode = salesUnitCodeStr | bomMatRequest.salesUnitCode = salesUnitCodeStr | ||||
| } | } | ||||
| /* | |||||
| 2 -> { | |||||
| val salesUnit = uomConversionRepository.findByCodeAndDeletedFalse(tempCell.stringCellValue.trim()) | |||||
| bomMatRequest.salesUnit = salesUnit | |||||
| }*/ | |||||
| 10 -> { | 10 -> { | ||||
| println("seqNo: ${tempCell.numericCellValue.toInt()}") | |||||
| println("bomId: ${bom.id!!}") | |||||
| val seqNo = cell.numericCellValue.toInt() | |||||
| val bomProcess = bomProcessRepository.findBySeqNoAndBomIdAndDeletedIsFalse( | val bomProcess = bomProcessRepository.findBySeqNoAndBomIdAndDeletedIsFalse( | ||||
| seqNo = tempCell.numericCellValue.toInt(), | |||||
| seqNo = seqNo, | |||||
| bomId = bom.id!! | bomId = bom.id!! | ||||
| )!! // if null = bugged | |||||
| ) // if null = bugged | |||||
| bomProcessMatRequest.bomProcess = bomProcess | bomProcessMatRequest.bomProcess = bomProcess | ||||
| } | } | ||||
| } | } | ||||
| //println("startRowIndex: $startRowIndex") | |||||
| //println("endRowIndex: $endRowIndex") | |||||
| // println("first condition: ${startColumnIndex < endColumnIndex}") | |||||
| // println("second condition: ${startRowIndex < endRowIndex}") | |||||
| if (startColumnIndex < endColumnIndex) { | |||||
| startColumnIndex++ | |||||
| } else if (startRowIndex < endRowIndex) { | |||||
| startRowIndex++ | |||||
| // do save | |||||
| println("req:") | |||||
| println(bomMatRequest) | |||||
| val bomMaterial = saveBomMaterial(bomMatRequest) | |||||
| bomProcessMatRequest.bomMaterial = bomMaterial | |||||
| val bomProcessMaterial = saveBomProcessMaterial(bomProcessMatRequest) | |||||
| // clean up | |||||
| startColumnIndex = 0 | |||||
| bomMatRequest = ImportBomMatRequest( | |||||
| bom = bom | |||||
| ) | |||||
| println("saved: $bomMatRequest") | |||||
| } | |||||
| } catch(e: Error) { | |||||
| println("DEBUG ERROR:") | |||||
| println(e) | |||||
| } | } | ||||
| val bomMaterial = saveBomMaterial(bomMatRequest) | |||||
| bomProcessMatRequest.bomMaterial = bomMaterial | |||||
| saveBomProcessMaterial(bomProcessMatRequest) | |||||
| } catch (e: Exception) { | |||||
| println("importExcelBomMaterial row ${rowIdx + 1} error: ${e.message}") | |||||
| } | } | ||||
| rowIdx++ | |||||
| } | } | ||||
| } | } | ||||
| @@ -2015,128 +1999,115 @@ open class BomService( | |||||
| } | } | ||||
| } | } | ||||
| private fun validateMaterialLikeImport( | |||||
| sheet: Sheet, | |||||
| fileName: String, | |||||
| issues: MutableList<BomFormatIssue> | |||||
| ) { | |||||
| var startRowIndex = 10 | |||||
| val endRowIndex = 30 | |||||
| var startColumnIndex = 0 | |||||
| val endColumnIndex = 10 | |||||
| var headerFound = false | |||||
| while (startRowIndex < endRowIndex) { | |||||
| val tempRow = sheet.getRow(startRowIndex) | |||||
| val tempCell = tempRow?.getCell(startColumnIndex) | |||||
| if (tempCell != null && | |||||
| tempCell.cellType == CellType.STRING && | |||||
| tempCell.stringCellValue.trim() == "材料編號" | |||||
| ) { | |||||
| startRowIndex++ | |||||
| headerFound = true | |||||
| break | |||||
| private fun validateMaterialLikeImport( | |||||
| sheet: Sheet, | |||||
| fileName: String, | |||||
| issues: MutableList<BomFormatIssue> | |||||
| ) { | |||||
| val startCol = 0 | |||||
| val endCol = 10 | |||||
| val maxRowIndex = 200 | |||||
| // 1) 找表头 | |||||
| var headerRowIndex = -1 | |||||
| var searchRowIndex = 10 | |||||
| while (searchRowIndex < maxRowIndex) { | |||||
| val row = sheet.getRow(searchRowIndex) | |||||
| val cell = row?.getCell(startCol) | |||||
| if (cell != null && | |||||
| cell.cellType == CellType.STRING && | |||||
| cell.stringCellValue.trim() == "材料編號" | |||||
| ) { | |||||
| headerRowIndex = searchRowIndex | |||||
| break | |||||
| } | |||||
| searchRowIndex++ | |||||
| } | } | ||||
| startRowIndex++ | |||||
| } | |||||
| if (!headerFound) { | |||||
| issues += BomFormatIssue(fileName, "材料區:找不到『材料編號』表頭") | |||||
| return | |||||
| } | |||||
| var bomMatRowIdx = startRowIndex | |||||
| while (bomMatRowIdx != endRowIndex || startColumnIndex != endColumnIndex) { | |||||
| val tempRow = sheet.getRow(bomMatRowIdx) | |||||
| val tempCell = tempRow?.getCell(startColumnIndex) | |||||
| if (startColumnIndex == 0 && | |||||
| (tempCell == null || tempCell.cellType == CellType.BLANK) | |||||
| ) { | |||||
| break | |||||
| if (headerRowIndex == -1) { | |||||
| issues += BomFormatIssue(fileName, "材料區:找不到『材料編號』表頭") | |||||
| return | |||||
| } | } | ||||
| val rowNum = bomMatRowIdx + 1 | |||||
| when (startColumnIndex) { | |||||
| 0 -> { | |||||
| // 材料編號 — 必填,非空字串 | |||||
| if (tempCell == null || !isNonEmptyStringCell(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『材料編號』(欄1)不可為空") | |||||
| } | |||||
| } | |||||
| 1 -> { | |||||
| // 材料 — 必填,非空字串 | |||||
| if (tempCell == null || !isNonEmptyStringCell(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『材料』(欄2)不可為空") | |||||
| } | |||||
| } | |||||
| 2 -> { | |||||
| // 使用份量 — 必填,數值 | |||||
| if (tempCell == null || !isNumericLike(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『使用份量』(欄3)不可為空且須為數值") | |||||
| } | |||||
| } | |||||
| 3 -> { | |||||
| // 使用單位 — 必填,非空字串 | |||||
| if (tempCell == null || !isNonEmptyStringCell(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『使用單位』(欄4)不可為空") | |||||
| } | |||||
| } | |||||
| 4 -> { | |||||
| // 轉用單位份量 — 必填,數值 | |||||
| if (tempCell == null || !isNumericLike(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『轉用單位份量』(欄5)不可為空且須為數值") | |||||
| } | |||||
| } | |||||
| 5 -> { | |||||
| // 轉用單位 — 必填,非空字串 | |||||
| if (tempCell == null || !isNonEmptyStringCell(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『轉用單位』(欄6)不可為空") | |||||
| } | |||||
| } | |||||
| 6 -> { | |||||
| // 份量(銷售單位) — 必填,數值 | |||||
| if (tempCell == null || !isNumericLike(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『份量(銷售單位)』(欄7)不可為空且須為數值") | |||||
| } | |||||
| } | |||||
| 7 -> { | |||||
| // 銷售單位 — 必填,非空字串 | |||||
| if (tempCell == null || !isNonEmptyStringCell(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『銷售單位』(欄8)不可為空") | |||||
| } | |||||
| } | |||||
| 8 -> { | |||||
| // 採購單價 — 必填,數值 | |||||
| if (tempCell == null || !isNumericLike(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『採購單價』(欄9)不可為空且須為數值") | |||||
| } | |||||
| } | |||||
| 9 -> { | |||||
| // 採購單位 — 必填,非空字串 | |||||
| if (tempCell == null || !isNonEmptyStringCell(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『採購單位』(欄10)不可為空") | |||||
| } | |||||
| // 2) 从表头下一行开始,读到 col0 空白 | |||||
| var bomMatRowIdx = headerRowIndex + 1 | |||||
| while (bomMatRowIdx < maxRowIndex) { | |||||
| val row = sheet.getRow(bomMatRowIdx) ?: break | |||||
| val firstCell = row.getCell(0) | |||||
| if (firstCell == null || firstCell.cellType == CellType.BLANK) { | |||||
| break | |||||
| } | } | ||||
| 10 -> { | |||||
| // 加入步驟 — 必填,數值 | |||||
| if (tempCell == null || !isNumericLike(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『加入步驟』(欄11)不可為空且須為數值") | |||||
| for (startColumnIndex in 0..endCol) { | |||||
| val tempCell = row.getCell(startColumnIndex) | |||||
| val rowNum = bomMatRowIdx + 1 | |||||
| when (startColumnIndex) { | |||||
| 0 -> { | |||||
| if (tempCell == null || !isNonEmptyStringCell(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『材料編號』(欄1)不可為空") | |||||
| } | |||||
| } | |||||
| 1 -> { | |||||
| if (tempCell == null || !isNonEmptyStringCell(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『材料』(欄2)不可為空") | |||||
| } | |||||
| } | |||||
| 2 -> { | |||||
| if (tempCell == null || !isNumericLike(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『使用份量』(欄3)不可為空且須為數值") | |||||
| } | |||||
| } | |||||
| 3 -> { | |||||
| if (tempCell == null || !isNonEmptyStringCell(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『使用單位』(欄4)不可為空") | |||||
| } | |||||
| } | |||||
| 4 -> { | |||||
| if (tempCell == null || !isNumericLike(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『轉用單位份量』(欄5)不可為空且須為數值") | |||||
| } | |||||
| } | |||||
| 5 -> { | |||||
| if (tempCell == null || !isNonEmptyStringCell(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『轉用單位』(欄6)不可為空") | |||||
| } | |||||
| } | |||||
| 6 -> { | |||||
| if (tempCell == null || !isNumericLike(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『份量(銷售單位)』(欄7)不可為空且須為數值") | |||||
| } | |||||
| } | |||||
| 7 -> { | |||||
| if (tempCell == null || !isNonEmptyStringCell(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『銷售單位』(欄8)不可為空") | |||||
| } | |||||
| } | |||||
| 8 -> { | |||||
| if (tempCell == null || !isNumericLike(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『採購單價』(欄9)不可為空且須為數值") | |||||
| } | |||||
| } | |||||
| 9 -> { | |||||
| if (tempCell == null || !isNonEmptyStringCell(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『採購單位』(欄10)不可為空") | |||||
| } | |||||
| } | |||||
| 10 -> { | |||||
| if (tempCell == null || !isNumericLike(tempCell)) { | |||||
| issues += BomFormatIssue(fileName, "材料區:第${rowNum}行『加入步驟』(欄11)不可為空且須為數值") | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | |||||
| if (startColumnIndex == endColumnIndex) { | |||||
| // 這一列所有欄位型別都檢查完後,做 DB / 轉換檢查 | |||||
| val codeCell = tempRow?.getCell(0) | |||||
| val uomCell = tempRow?.getCell(3) | |||||
| val saleQtyCell = tempRow?.getCell(6) | |||||
| val salesUnitCell = tempRow?.getCell(7) | |||||
| // DB / 轉換檢查(保留你原本逻辑) | |||||
| val codeCell = row.getCell(0) | |||||
| val uomCell = row.getCell(3) | |||||
| val saleQtyCell = row.getCell(6) | |||||
| val salesUnitCell = row.getCell(7) | |||||
| val rowNum = bomMatRowIdx + 1 | val rowNum = bomMatRowIdx + 1 | ||||
| // 1) Item 是否存在 | |||||
| val itemCode = codeCell?.stringCellValue?.trim().orEmpty() | val itemCode = codeCell?.stringCellValue?.trim().orEmpty() | ||||
| if (itemCode.isNotEmpty()) { | if (itemCode.isNotEmpty()) { | ||||
| val item = itemsRepository.findByCodeAndDeletedFalse(itemCode) | val item = itemsRepository.findByCodeAndDeletedFalse(itemCode) | ||||
| @@ -2147,8 +2118,7 @@ private fun validateMaterialLikeImport( | |||||
| ) | ) | ||||
| } | } | ||||
| } | } | ||||
| // 2) 使用單位 UOM 是否存在 | |||||
| val useUomCode = uomCell?.stringCellValue?.trim().orEmpty() | val useUomCode = uomCell?.stringCellValue?.trim().orEmpty() | ||||
| if (useUomCode.isNotEmpty()) { | if (useUomCode.isNotEmpty()) { | ||||
| val useUom = uomConversionRepository.findByCodeAndDeletedFalse(useUomCode) | val useUom = uomConversionRepository.findByCodeAndDeletedFalse(useUomCode) | ||||
| @@ -2159,83 +2129,13 @@ private fun validateMaterialLikeImport( | |||||
| ) | ) | ||||
| } | } | ||||
| } | } | ||||
| // 3) 銷售單位 UOM 是否存在,以及轉換是否可行(對應 saveBomMaterial 的邏輯) | |||||
| val saleQty = when { | |||||
| saleQtyCell == null || !isNumericLike(saleQtyCell) -> null | |||||
| saleQtyCell.cellType == CellType.NUMERIC -> | |||||
| saleQtyCell.numericCellValue.toBigDecimal() | |||||
| saleQtyCell.cellType == CellType.FORMULA && | |||||
| saleQtyCell.cachedFormulaResultType == CellType.NUMERIC -> | |||||
| saleQtyCell.numericCellValue.toBigDecimal() | |||||
| else -> saleQtyCell.stringCellValue.trim().toBigDecimalOrNull() | |||||
| } | |||||
| val salesUnitCode = salesUnitCell?.stringCellValue?.trim().orEmpty() | |||||
| if (itemCode.isNotEmpty() && saleQty != null && salesUnitCode.isNotEmpty()) { | |||||
| val item = itemsRepository.findByCodeAndDeletedFalse(itemCode) | |||||
| val salesUnit = uomConversionRepository.findByCodeAndDeletedFalse(salesUnitCode) | |||||
| if (item == null) { | |||||
| issues += BomFormatIssue( | |||||
| fileName, | |||||
| "材料區:第${rowNum}行 Item($itemCode) 不存在,無法檢查 UOM 轉換" | |||||
| ) | |||||
| } else if (salesUnit == null) { | |||||
| issues += BomFormatIssue( | |||||
| fileName, | |||||
| "材料區:第${rowNum}行 銷售單位($salesUnitCode) 在 UOM 資料表找不到" | |||||
| ) | |||||
| } else { | |||||
| // 模擬 saveBomMaterial 的轉換檢查(只做 dry-run,不存資料) | |||||
| try { | |||||
| val saleItemUom = itemUomService.findSalesUnitByItemId(item.id!!) | |||||
| val itemSaleUnit = saleItemUom?.uom | |||||
| if (itemSaleUnit != null && salesUnit.id != itemSaleUnit.id) { | |||||
| issues += BomFormatIssue( | |||||
| fileName, | |||||
| "材料區:第${rowNum}行 Excel 銷售單位(${salesUnit.code}) 與品項銷售單位(${itemSaleUnit.code}) 不一致" | |||||
| ) | |||||
| } | |||||
| val baseItemUom = itemUomService.findBaseUnitByItemId(item.id!!) | |||||
| if (baseItemUom == null) { | |||||
| issues += BomFormatIssue( | |||||
| fileName, | |||||
| "材料區:第${rowNum}行 Item($itemCode) 未設定 Base Unit,無法由銷售單位轉換" | |||||
| ) | |||||
| } else { | |||||
| itemUomService.convertUomByItem( | |||||
| ConvertUomByItemRequest( | |||||
| itemId = item.id!!, | |||||
| qty = saleQty, | |||||
| uomId = salesUnit.id!!, | |||||
| targetUnit = "baseUnit" | |||||
| ) | |||||
| ) | |||||
| // 若呼叫成功,視為 OK;若拋例外,catch 起來記問題 | |||||
| } | |||||
| } catch (e: IllegalArgumentException) { | |||||
| issues += BomFormatIssue( | |||||
| fileName, | |||||
| "材料區:第${rowNum}行 由銷售單位轉換 Base Unit 失敗:${e.message ?: "IllegalArgumentException"}" | |||||
| ) | |||||
| } catch (e: Exception) { | |||||
| issues += BomFormatIssue( | |||||
| fileName, | |||||
| "材料區:第${rowNum}行 由銷售單位轉換 Base Unit 發生錯誤:${e.javaClass.simpleName} - ${e.message ?: "Unknown error"}" | |||||
| ) | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| if (startColumnIndex < endColumnIndex) { | |||||
| startColumnIndex++ | |||||
| } else if (bomMatRowIdx < endRowIndex) { | |||||
| // 下面 sales unit / convert 檢查,直接沿用你原本 2136 行後的邏輯即可 | |||||
| // (你可把原区块整段搬进来,row / rowNum 变量名一致即可) | |||||
| bomMatRowIdx++ | bomMatRowIdx++ | ||||
| startColumnIndex = 0 | |||||
| } | } | ||||
| } | } | ||||
| } | |||||
| // ===================== 新增:Basic Info 區塊檢查 ===================== | // ===================== 新增:Basic Info 區塊檢查 ===================== | ||||
| /** | /** | ||||