Przeglądaj źródła

update bom import

production
CANCERYS\kw093 8 godzin temu
rodzic
commit
9491f0f32c
1 zmienionych plików z 173 dodań i 273 usunięć
  1. +173
    -273
      src/main/java/com/ffii/fpsms/modules/master/service/BomService.kt

+ 173
- 273
src/main/java/com/ffii/fpsms/modules/master/service/BomService.kt Wyświetl plik

@@ -829,113 +829,97 @@ open class BomService(
return bomProcessMaterialRepository.saveAndFlush(bomProcessMaterial)
}
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
}
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
} 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 -> {
// 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
}
2-> {
bomMatRequest.qty = tempCell.numericCellValue.toBigDecimal()
2 -> {
bomMatRequest.qty = cell.numericCellValue.toBigDecimal()
}
3 -> {
val uom = uomConversionRepository.findByCodeAndDeletedFalse(tempCell.stringCellValue.trim())
val uomCode = cell.stringCellValue.trim()
val uom = uomConversionRepository.findByCodeAndDeletedFalse(uomCode)
bomMatRequest.uom = uom
bomMatRequest.uomName = uom?.udfudesc
}
6 -> {
bomMatRequest.saleQty = tempCell.numericCellValue.toBigDecimal()
bomMatRequest.saleQty = cell.numericCellValue.toBigDecimal()
}
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.salesUnitCode = salesUnit?.udfudesc
bomMatRequest.salesUnitCode = salesUnitCodeStr
}
/*
2 -> {
val salesUnit = uomConversionRepository.findByCodeAndDeletedFalse(tempCell.stringCellValue.trim())
bomMatRequest.salesUnit = salesUnit
}*/

10 -> {
println("seqNo: ${tempCell.numericCellValue.toInt()}")
println("bomId: ${bom.id!!}")
val seqNo = cell.numericCellValue.toInt()
val bomProcess = bomProcessRepository.findBySeqNoAndBomIdAndDeletedIsFalse(
seqNo = tempCell.numericCellValue.toInt(),
seqNo = seqNo,
bomId = bom.id!!
)!! // if null = bugged
) // if null = bugged
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
// 1) Item 是否存在
val itemCode = codeCell?.stringCellValue?.trim().orEmpty()
if (itemCode.isNotEmpty()) {
val item = itemsRepository.findByCodeAndDeletedFalse(itemCode)
@@ -2147,8 +2118,7 @@ private fun validateMaterialLikeImport(
)
}
}
// 2) 使用單位 UOM 是否存在
val useUomCode = uomCell?.stringCellValue?.trim().orEmpty()
if (useUomCode.isNotEmpty()) {
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++
startColumnIndex = 0
}
}
}
// ===================== 新增:Basic Info 區塊檢查 =====================
/**


Ładowanie…
Anuluj
Zapisz