diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/Bom.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/Bom.kt index 7c06c21..69a9898 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/Bom.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/Bom.kt @@ -16,7 +16,7 @@ open class Bom : BaseEntity() { open var isDark: Int? = null @Column - open var isFloat: Boolean? = null + open var isFloat: Int? = null @Column open var isDense: Int? = null diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/BomMaterial.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/BomMaterial.kt index 3eca51c..8bdfad6 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/BomMaterial.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/BomMaterial.kt @@ -42,11 +42,11 @@ open class BomMaterial : BaseEntity() { @JsonBackReference open var bom: Bom? = null - @NotNull +// @NotNull @Column(name = "m18Id", nullable = false) open var m18Id: Long? = null - @NotNull +// @NotNull @Column(name = "m18LastModifyDate", nullable = false) open var m18LastModifyDate: LocalDateTime? = null diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/ItemsRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/ItemsRepository.kt index 163ea52..2ed852f 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/ItemsRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/ItemsRepository.kt @@ -5,6 +5,7 @@ import com.ffii.fpsms.modules.master.web.models.ItemType import org.springframework.data.jpa.repository.Query import org.springframework.stereotype.Repository import java.io.Serializable +import java.util.Optional @Repository interface ItemsRepository : AbstractRepository { @@ -13,6 +14,8 @@ interface ItemsRepository : AbstractRepository { fun findByIdAndDeletedFalse(id: Long): Items?; fun findByCodeAndTypeAndDeletedFalse(code: String, type: String): Items?; + fun findByCodeAndDeletedFalse(code: String): Items?; + fun findByNameAndDeletedFalse(name: String): Items?; fun findByM18IdAndDeletedIsFalse(m18Id: Long): Items?; diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/BomService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/BomService.kt index d2099fc..b765124 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/BomService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/BomService.kt @@ -1,18 +1,32 @@ package com.ffii.fpsms.modules.master.service -import com.ffii.fpsms.modules.master.entity.Bom -import com.ffii.fpsms.modules.master.entity.BomRepository +import com.ffii.fpsms.modules.master.entity.* +import com.ffii.fpsms.modules.master.web.models.ImportBomMatRequest +import com.ffii.fpsms.modules.master.web.models.ImportBomRequest import com.ffii.fpsms.modules.master.web.models.SaveBomRequest import com.ffii.fpsms.modules.master.web.models.SaveBomResponse +import org.apache.poi.ss.usermodel.CellType +import org.apache.poi.ss.usermodel.Sheet +import org.apache.poi.ss.usermodel.Workbook +import org.apache.poi.xssf.usermodel.XSSFWorkbook +import org.springframework.core.io.ClassPathResource +import org.springframework.core.io.support.PathMatchingResourcePatternResolver import org.springframework.stereotype.Service -import kotlin.jvm.optionals.getOrNull +import java.io.File + @Service open class BomService( val bomRepository: BomRepository, + val bomMaterialRepository: BomMaterialRepository, val itemsService: ItemsService, - val uomConversionService: UomConversionService + val itemsRepository: ItemsRepository, + val uomConversionService: UomConversionService, + val uomConversionRepository: UomConversionRepository ) { + private val COMPLETION_PROJECT = "templates/report/AR05_Project Completion Report.xlsx" + private val EXCEL_PATH = "bomImport/006 PP1080 咖喱汁 Pings BOM Template v3.xlsx" +// private val EXCEL_PATH = "C:\\Users\\2Fi\\Documents\\MTMS\\Ping's Production MTMS Project Folder\\模組2_生產規劃 (Production Planning)\\PP1080 咖哩汁 Bom\\006 PP1080 咖喱汁 Pings BOM Template v3.xlsx" open fun findAll(): List { return bomRepository.findAll() @@ -65,4 +79,209 @@ open class BomService( return response } + + //////// -------------------------------- for excel import ------------------------------- ///////// + fun saveBomEntity(req: ImportBomRequest): Bom { + val item = if (req.itemId != null) itemsRepository.findById(req.itemId).orElseThrow() else null + val uom = if (req.uomId != null) uomConversionRepository.findById(req.uomId).orElseThrow() else null + val bom = Bom().apply { + this.isDark = req.isDark + this.isFloat = req.isFloat + this.isDense = req.isDense + this.code = req.code + this.name = req.name + this.description = req.description + this.item = item + this.outputQty = req.outputQty + this.outputQtyUom = req.outputQtyUom + this.yield = req.yield + this.uom = uom +// this.excelUom = req.excelUom + } + val savedBom = bomRepository.saveAndFlush(bom) +// println("saved: ${savedBom.id}") + return savedBom + } + + fun saveBomMaterial(req: ImportBomMatRequest): BomMaterial { + val bomMaterial = BomMaterial().apply { + this.item = req.item + this.itemName = req.item!!.name + this.isConsumable = req.isConsumable + this.qty = req.qty + this.uomName = req.uomName + this.bom = req.bom + + } + return bomMaterialRepository.saveAndFlush(bomMaterial) + } + fun importExcelBomMaterial(bom: Bom, sheet: Sheet) { + var request = ImportBomMatRequest( + bom = bom + ) + var startRowIndex = 10 + val endRowIndex = 30 + var startColumnIndex = 0 + val endColumnIndex = 9 + 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++ + break + } + startRowIndex++ + } + while (startRowIndex != endRowIndex || startColumnIndex != endColumnIndex) { + val tempRow = sheet.getRow(startRowIndex) + val tempCell = tempRow.getCell(startColumnIndex) + if (tempCell == null || tempCell.cellType == CellType.BLANK) { + break + } else { + try { + when (startColumnIndex) { + 0 -> { +// println("start") + val nameRow = sheet.getRow(startRowIndex) + val nameCell = nameRow.getCell(1) + val item = itemsRepository.findByCodeAndDeletedFalse(tempCell.stringCellValue.trim()) + ?: itemsRepository.findByNameAndDeletedFalse(nameCell.stringCellValue.trim()) +// println(item) + request.apply { + this.item = item + } + } + 2 -> { + request.apply { + this.qty = tempCell.numericCellValue.toBigDecimal() + } + } + 3 -> { + request.apply { + this.uomName = tempCell.stringCellValue.trim() + } + } + } + } catch(e: Error) { + println(startColumnIndex) + println(startRowIndex) + println(tempCell.stringCellValue.trim()) + } + } + if (startColumnIndex < endColumnIndex) { + startColumnIndex++ + } else if (startRowIndex < endRowIndex) { + startRowIndex++ + // do save + saveBomMaterial(request) + startColumnIndex = 0 + } + } + + } + + fun importExcelBomBasicInfo(sheet: Sheet): Bom { + var request = ImportBomRequest( + code = "", + name = "", + description = "", + ) + var startRowIndex = 0 + val endRowIndex = 15 + var startColumnIndex = 0 + val endColumnIndex = 15 + while (startRowIndex != endRowIndex || startColumnIndex != endColumnIndex) { + val tempRow = sheet.getRow(startRowIndex) + val tempCell = tempRow.getCell(startColumnIndex) + if (tempCell != null && tempCell.cellType == CellType.STRING) { + val tempCellVal = tempCell.stringCellValue.trim() + // --------------------- assigning value that is under a header to request ------------------------ // + val topTargetValueRow = sheet.getRow(startRowIndex + 1) + val topTargetValueCell = topTargetValueRow.getCell(startColumnIndex) + when (tempCellVal) { + "編號" -> { + println(topTargetValueCell.stringCellValue.trim()) + request.apply { + code = topTargetValueCell.stringCellValue.trim() + } + } + "產品名稱" -> request.apply { + name = topTargetValueCell.stringCellValue.trim() + } + "種類" -> request.apply { + description = topTargetValueCell.stringCellValue.trim() + } + "份量 (Qty)" -> request.apply { + outputQty = topTargetValueCell.numericCellValue.toBigDecimal() + } + "單位" -> request.apply { + outputQtyUom = topTargetValueCell.stringCellValue.trim() + } + } + // ----------------------------------------------------------------------------------------------- // + // --------------------- assigning value that is next to a header to request ------------------------ // + fun calculateColourScore(value: String): Int { + var score = -1 + when (value) { + "淺" -> score = 0 + "深" -> score = 1 + } + return score + } + fun calculateFloatScore(value: String): Int { + var score = -1 + when (value) { + "沉" -> score = 0 + "浮" -> score = 1 + } + return score + } + val leftTargetValueRow = sheet.getRow(startRowIndex) + val leftTargetValueCell = leftTargetValueRow.getCell(startColumnIndex + 1) + if (tempCellVal == "顔色深淺度") println("顔色深淺度") + when (tempCellVal) { + "顔色深淺度" -> request.apply { + isDark = calculateColourScore(leftTargetValueCell.stringCellValue.trim()) + } + "浮沉" -> request.apply { + isFloat = calculateFloatScore(leftTargetValueCell.stringCellValue.trim()) + } + "濃淡程度" -> request.apply { + isDense = if (leftTargetValueCell.cellType == CellType.NUMERIC) leftTargetValueCell.numericCellValue.toInt() else 0 + } + } + } + if (startRowIndex < endRowIndex) { + startRowIndex++ + } else if (startColumnIndex < endColumnIndex) { + startColumnIndex++ + startRowIndex = 0 + } + } + return saveBomEntity(request) + } + open fun importBOM() { +// val folderPath = "bomImport" +// val folder = File(folderPath) + val resolver = PathMatchingResourcePatternResolver() + val excels = resolver.getResources("bomImport/*.xlsx") + + for (resource in excels) { + //get sheet + println(resource.filename) + val templateInputStream = resource.inputStream + val workbook: Workbook = XSSFWorkbook(templateInputStream) + val sheet: Sheet = workbook.getSheetAt(0) +// val rowIndex = 1 +// val columnIndex = 0 +// val tempRow = sheet.getRow(rowIndex) +// val tempCell = tempRow.getCell(columnIndex) +// println(tempCell.cellType) +// println(tempCell.toString()) + val bom = importExcelBomBasicInfo(sheet) // updating bom table + val bomMaterial = importExcelBomMaterial(bom, sheet) +// break + } + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/BomController.kt b/src/main/java/com/ffii/fpsms/modules/master/web/BomController.kt index d67cf55..5c4ad86 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/web/BomController.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/web/BomController.kt @@ -3,6 +3,7 @@ package com.ffii.fpsms.modules.master.web import com.ffii.fpsms.modules.master.entity.Bom import com.ffii.fpsms.modules.master.service.BomService import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @@ -15,4 +16,9 @@ class BomController ( fun getBoms(): List { return bomService.findAll() } + + @PostMapping("/import-bom") + fun importBom() { + return bomService.importBOM() + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveBomRequest.kt b/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveBomRequest.kt index 0a98517..915aa59 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveBomRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveBomRequest.kt @@ -1,5 +1,7 @@ package com.ffii.fpsms.modules.master.web.models +import com.ffii.fpsms.modules.master.entity.Bom +import com.ffii.fpsms.modules.master.entity.Items import jakarta.validation.constraints.NotBlank import java.math.BigDecimal import java.time.LocalDateTime @@ -20,4 +22,30 @@ data class SaveBomRequest ( val m18UomId: Long? = null, val m18Id: Long? = null, val m18LastModifyDate: LocalDateTime? = null +) +data class ImportBomRequest ( + @field:NotBlank(message = "bom code cannot be empty") + var code: String, + @field:NotBlank(message = "bom name cannot be empty") + var name: String, + @field:NotBlank(message = "bom description cannot be empty") + var description: String, + var itemId: Long? = null, + var outputQty: BigDecimal? = null, + var outputQtyUom: String? = null, + var yield: BigDecimal? = null, + var uomId: Long? = null, + var isDark: Int? = null, + var isFloat: Int? = null, + var isDense: Int? = null, +// var excelUom: String? = null +) + +data class ImportBomMatRequest ( + var item: Items? = null, + var isConsumable: Boolean? = false, + var qty: BigDecimal? = null, + var uomId: Long? = null, + var uomName: String? = null, + var bom: Bom? = null, ) \ No newline at end of file diff --git a/src/main/resources/bomImport/006 PP1080 咖喱汁 Pings BOM Template v3.xlsx b/src/main/resources/bomImport/006 PP1080 咖喱汁 Pings BOM Template v3.xlsx new file mode 100644 index 0000000..c0b6c45 Binary files /dev/null and b/src/main/resources/bomImport/006 PP1080 咖喱汁 Pings BOM Template v3.xlsx differ diff --git a/src/main/resources/bomImport/200 PP - PP1193 蔥油(1磅) Pings BOM Template.xlsx b/src/main/resources/bomImport/200 PP - PP1193 蔥油(1磅) Pings BOM Template.xlsx new file mode 100644 index 0000000..dbb11f3 Binary files /dev/null and b/src/main/resources/bomImport/200 PP - PP1193 蔥油(1磅) Pings BOM Template.xlsx differ diff --git a/src/main/resources/bomImport/201 PP - PP1188 咖喱膽 Pings BOM Template.xlsx b/src/main/resources/bomImport/201 PP - PP1188 咖喱膽 Pings BOM Template.xlsx new file mode 100644 index 0000000..4c6c12c Binary files /dev/null and b/src/main/resources/bomImport/201 PP - PP1188 咖喱膽 Pings BOM Template.xlsx differ diff --git a/src/main/resources/bomImport/202 PP - PP1096 白麵撈(1磅) Pings BOM Template.xlsx b/src/main/resources/bomImport/202 PP - PP1096 白麵撈(1磅) Pings BOM Template.xlsx new file mode 100644 index 0000000..346a337 Binary files /dev/null and b/src/main/resources/bomImport/202 PP - PP1096 白麵撈(1磅) Pings BOM Template.xlsx differ diff --git a/src/main/resources/bomImport/801 PP1080 咖哩汁箱料粉 Pings BOM Template v3.xlsx b/src/main/resources/bomImport/801 PP1080 咖哩汁箱料粉 Pings BOM Template v3.xlsx new file mode 100644 index 0000000..0be0bd2 Binary files /dev/null and b/src/main/resources/bomImport/801 PP1080 咖哩汁箱料粉 Pings BOM Template v3.xlsx differ diff --git a/src/main/resources/db/changelog/changes/20250611_01_derek/01_update_bom_material_col.sql b/src/main/resources/db/changelog/changes/20250611_01_derek/01_update_bom_material_col.sql new file mode 100644 index 0000000..0702359 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250611_01_derek/01_update_bom_material_col.sql @@ -0,0 +1,5 @@ +-- liquibase formatted sql +-- changeset derek:modify bom material m18 related column +ALTER TABLE `bom_material` +MODIFY COLUMN `m18Id` INT(11) NULL, +MODIFY COLUMN `m18LastModifyDate` DATETIME NULL; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20250611_01_derek/02_update_bom_score_col.sql b/src/main/resources/db/changelog/changes/20250611_01_derek/02_update_bom_score_col.sql new file mode 100644 index 0000000..ea3c97b --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250611_01_derek/02_update_bom_score_col.sql @@ -0,0 +1,4 @@ +-- liquibase formatted sql +-- changeset derek:update_bom_score_col +ALTER TABLE `bom` +MODIFY COLUMN `isFloat` INT(11) NULL; \ No newline at end of file