diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/BomProcess.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/BomProcess.kt index c3178a1..a892daf 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/BomProcess.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/BomProcess.kt @@ -1,5 +1,7 @@ package com.ffii.fpsms.modules.master.entity +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonManagedReference import com.ffii.core.entity.BaseEntity import jakarta.persistence.* import jakarta.validation.constraints.NotNull @@ -12,10 +14,17 @@ open class BomProcess : BaseEntity() { @JoinColumn(name = "processId", nullable = false) open var process: Process? = null - @NotNull - @ManyToOne(optional = false) - @JoinColumn(name = "uomId", nullable = false) - open var uom: UomConversion? = null + @ManyToOne + @JoinColumn(name = "equipmentId", nullable = false) + open var equipment: Equipment? = null + + @Column(name = "description", nullable = false) + open var description: String? = null + +// @NotNull +// @ManyToOne(optional = false) +// @JoinColumn(name = "uomId", nullable = false) +// open var uom: UomConversion? = null @NotNull @Column(name = "seqNo", nullable = false) @@ -25,8 +34,19 @@ open class BomProcess : BaseEntity() { @Column(name = "duration", nullable = false) open var duration: Int? = null + @Column(name = "prepTimeInMinute", nullable = false) + open var prepTimeInMinute: Int? = null + + @Column(name = "postProdTimeInMinute", nullable = false) + open var postProdTimeInMinute: Int? = null + @NotNull @ManyToOne(optional = false) @JoinColumn(name = "bomId", nullable = false) open var bom: Bom? = null + + @OneToMany(mappedBy = "bomProcess", cascade = [CascadeType.ALL], orphanRemoval = true) + @JsonManagedReference("bomProcess-material") + @JsonInclude(JsonInclude.Include.NON_NULL) + private val bomProcessMaterial: Set = HashSet() } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/BomProcessRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/BomProcessRepository.kt index 2511afd..2cd9f59 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/BomProcessRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/BomProcessRepository.kt @@ -2,7 +2,9 @@ package com.ffii.fpsms.modules.master.entity import com.ffii.core.support.AbstractRepository import org.springframework.stereotype.Repository +import java.util.Optional @Repository interface BomProcessRepository : AbstractRepository { + fun findBySeqNoAndBomIdAndDeletedIsFalse(seqNo: Int, bomId: Long): BomProcess? } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/EquipmentRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/EquipmentRepository.kt index be87864..53ba008 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/EquipmentRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/EquipmentRepository.kt @@ -2,7 +2,10 @@ package com.ffii.fpsms.modules.master.entity import com.ffii.core.support.AbstractRepository import org.springframework.stereotype.Repository +import java.util.Optional @Repository interface EquipmentRepository : AbstractRepository { + fun findByNameAndDeletedIsFalse(name: String): Equipment? + fun findByCodeAndDeletedIsFalse(code: String): Equipment? } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/Process.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/Process.kt index 4aed4af..7971860 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/Process.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/Process.kt @@ -26,7 +26,6 @@ open class Process : BaseEntity() { open var description: String? = null @Size(max = 1000) - @NotNull @Column(name = "remarks", nullable = true, length = 1000) open var remarks: String? = null } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/ProcessRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/ProcessRepository.kt index 682df1d..9b3c912 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/ProcessRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/ProcessRepository.kt @@ -2,7 +2,10 @@ package com.ffii.fpsms.modules.master.entity import com.ffii.core.support.AbstractRepository import org.springframework.stereotype.Repository +import java.util.* @Repository interface ProcessRepository: AbstractRepository { + fun findByCodeAndDeletedIsFalse(code: String): Process? + fun findByNameAndDeletedIsFalse(name: String): Process? } \ No newline at end of file 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 b765124..37cf64d 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,10 +1,7 @@ package com.ffii.fpsms.modules.master.service 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 com.ffii.fpsms.modules.master.web.models.* import org.apache.poi.ss.usermodel.CellType import org.apache.poi.ss.usermodel.Sheet import org.apache.poi.ss.usermodel.Workbook @@ -17,17 +14,17 @@ import java.io.File @Service open class BomService( - val bomRepository: BomRepository, - val bomMaterialRepository: BomMaterialRepository, - val itemsService: ItemsService, - val itemsRepository: ItemsRepository, - val uomConversionService: UomConversionService, - val uomConversionRepository: UomConversionRepository + private val bomRepository: BomRepository, + private val bomMaterialRepository: BomMaterialRepository, + private val bomProcessRepository: BomProcessRepository, + private val bomProcessMaterialRepository: BomProcessMaterialRepository, + private val itemsService: ItemsService, + private val itemsRepository: ItemsRepository, + private val uomConversionService: UomConversionService, + private val uomConversionRepository: UomConversionRepository, + private val equipmentRepository: EquipmentRepository, + private val processRepository: ProcessRepository, ) { - 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() } @@ -81,7 +78,7 @@ open class BomService( } //////// -------------------------------- for excel import ------------------------------- ///////// - fun saveBomEntity(req: ImportBomRequest): Bom { + private 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 { @@ -115,14 +112,35 @@ open class BomService( } return bomMaterialRepository.saveAndFlush(bomMaterial) } - fun importExcelBomMaterial(bom: Bom, sheet: Sheet) { - var request = ImportBomMatRequest( + fun saveBomProcess(req: ImportBomProcessRequest): BomProcess { + val bomProcess = BomProcess().apply { + this.process = req.process + this.equipment = req.equipment + this.description = req.description + this.seqNo = req.seqNo + this.duration = req.duration + this.prepTimeInMinute = req.prepTimeInMinute + this.postProdTimeInMinute = req.postProdTimeInMinute + this.bom = req.bom + } + return bomProcessRepository.saveAndFlush(bomProcess) + } + fun saveBomProcessMaterial(req: ImportBomProcessMaterialRequest): BomProcessMaterial { + val bomProcessMaterial = BomProcessMaterial().apply { + this.bomProcess = req.bomProcess + this.bomMaterial = req.bomMaterial + } + return bomProcessMaterialRepository.saveAndFlush(bomProcessMaterial) + } + private fun importExcelBomMaterial(bom: Bom, sheet: Sheet) { + var bomMatRequest = ImportBomMatRequest( bom = bom ) + var bomProcessMatRequest = ImportBomProcessMaterialRequest() var startRowIndex = 10 val endRowIndex = 30 var startColumnIndex = 0 - val endColumnIndex = 9 + val endColumnIndex = 10 while (startRowIndex < endRowIndex) { val tempRow = sheet.getRow(startRowIndex) val tempCell = tempRow.getCell(startColumnIndex) @@ -142,31 +160,41 @@ open class BomService( try { when (startColumnIndex) { 0 -> { -// println("start") +// 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) - request.apply { + bomMatRequest.apply { this.item = item } } 2 -> { - request.apply { + bomMatRequest.apply { this.qty = tempCell.numericCellValue.toBigDecimal() } } 3 -> { - request.apply { + bomMatRequest.apply { this.uomName = tempCell.stringCellValue.trim() } } + 10 -> { + val bomProcess = bomProcessRepository.findBySeqNoAndBomIdAndDeletedIsFalse( + seqNo = tempCell.numericCellValue.toInt(), + bomId = bom.id!! + )!! // if null = bugged + bomProcessMatRequest.apply { + this.bomProcess = bomProcess + } + } } } catch(e: Error) { - println(startColumnIndex) - println(startRowIndex) - println(tempCell.stringCellValue.trim()) + println("DEBUG ERROR:") + println(e) } } if (startColumnIndex < endColumnIndex) { @@ -174,11 +202,14 @@ open class BomService( } else if (startRowIndex < endRowIndex) { startRowIndex++ // do save - saveBomMaterial(request) + val bomMaterial = saveBomMaterial(bomMatRequest) + bomProcessMatRequest.apply { + this.bomMaterial = bomMaterial + } + val bomProcessMaterial = saveBomProcessMaterial(bomProcessMatRequest) startColumnIndex = 0 } } - } fun importExcelBomBasicInfo(sheet: Sheet): Bom { @@ -239,7 +270,6 @@ open class BomService( } val leftTargetValueRow = sheet.getRow(startRowIndex) val leftTargetValueCell = leftTargetValueRow.getCell(startColumnIndex + 1) - if (tempCellVal == "顔色深淺度") println("顔色深淺度") when (tempCellVal) { "顔色深淺度" -> request.apply { isDark = calculateColourScore(leftTargetValueCell.stringCellValue.trim()) @@ -261,6 +291,162 @@ open class BomService( } return saveBomEntity(request) } + + private fun bomGetOrCreateEquipment(name: String): Equipment { + var equipment = equipmentRepository.findByNameAndDeletedIsFalse(name) + ?: equipmentRepository.findByCodeAndDeletedIsFalse(name) + if (equipment == null) { + equipment = Equipment().apply { + this.name = name + this.code = name + this.description = name + } + equipment = equipmentRepository.saveAndFlush(equipment) + } + return equipment!! + } + private fun bomGetOrCreateProcess(name: String): Process { + var process = processRepository.findByNameAndDeletedIsFalse(name) + ?: processRepository.findByCodeAndDeletedIsFalse(name) + if (process == null) { + process = Process().apply { + this.name = name + this.code = name + this.description = name + } + process = processRepository.saveAndFlush(process) + } + return process!! + } + + private fun extractDurationStringToMinutes(str: String): Int { + val regex = """(\d+)(\D+)""".toRegex() + val matchResult = regex.find(str) + + val (number, unit) = matchResult?.let { + val number = it.groupValues[1].toInt() + val unit = it.groupValues[2] + Pair(number, unit) + }!! + var multiplier = 1 + when { + unit.contains("min") -> { + multiplier = 1 + } + unit.contains("hr") -> { + multiplier = 60 + } + } + return number * multiplier + } + private fun importExcelBomProcess(bom: Bom, sheet: Sheet) { + var bomProcessRequest = ImportBomProcessRequest( + bom = bom + ) + var startRowIndex = 30 + val endRowIndex = 70 + var startColumnIndex = 0 + val endColumnIndex = 11 + while (startRowIndex < endRowIndex) { + println("") + val tempRow = sheet.getRow(startRowIndex) + val tempCell = tempRow.getCell(startColumnIndex) + if (tempCell != null && tempCell.cellType == CellType.STRING && tempCell.stringCellValue.trim() == "工序") { + startRowIndex += 2 // skip column header + println("last: $startRowIndex") + break + } + startRowIndex++ + } + while (startRowIndex != endRowIndex || startColumnIndex != endColumnIndex) { + val tempRow = sheet.getRow(startRowIndex) + val tempCell = tempRow.getCell(startColumnIndex) + val checkCell = tempRow.getCell(0) + if (startColumnIndex == 0 && (tempCell == null || tempCell.cellType == CellType.BLANK)) { + println("hi") + break + } else { + println(tempCell.cellType) + println(tempCell.toString()) + try { + when (startColumnIndex) { + 0 -> { + println(tempCell.cellType) + bomProcessRequest.apply { + this.seqNo = tempCell.numericCellValue.toLong() + } + } + 1 -> { + val equipmentName = tempCell.stringCellValue.trim() + if (equipmentName != "不適用") {0 + val equipment = bomGetOrCreateEquipment(equipmentName) + bomProcessRequest.apply { + this.equipment = equipment + } + } + } + 2 -> { + val processName = tempCell.stringCellValue.trim() + val process = bomGetOrCreateProcess(processName) + bomProcessRequest.apply { + this.process = process + } + } + 3 -> { + val description = tempCell.stringCellValue.trim() + bomProcessRequest.apply { + this.description = description + } + } + 6 -> { //duration + val durationString = tempCell.stringCellValue.trim() + bomProcessRequest.apply { + this.duration = extractDurationStringToMinutes(durationString) + } + } + 10 -> { //prepTimeInMinute + if (tempCell.cellType != CellType.BLANK) { + val prepTimeInMinute = tempCell.stringCellValue.trim() + bomProcessRequest.apply { + this.prepTimeInMinute = extractDurationStringToMinutes(prepTimeInMinute) + } + } + } + 11 -> { //prepTimeInMinute + if (tempCell.cellType != CellType.BLANK) { + val prepTimeInMinute = tempCell.stringCellValue.trim() + bomProcessRequest.apply { + this.prepTimeInMinute = extractDurationStringToMinutes(prepTimeInMinute) + } + } + } + } + } catch (e: Error) { + throw e + } + } + println("startRowIndex: $startRowIndex") + println("endRowIndex: $endRowIndex") + println(startRowIndex < endRowIndex) + + println("startColumnIndex: $startColumnIndex") + println("endColumnIndex: $endColumnIndex") + println(startColumnIndex < endColumnIndex) + // moving the loop + if (startColumnIndex < endColumnIndex) { + println("1st") + startColumnIndex++ + } else if (startRowIndex < endRowIndex) { + println("2nd") + // when doesn't meet first condition, falls to this and go new row + startRowIndex++ + // do save + saveBomProcess(bomProcessRequest) + startColumnIndex = 0 + } + } + } + open fun importBOM() { // val folderPath = "bomImport" // val folder = File(folderPath) @@ -273,14 +459,12 @@ open class BomService( 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) + // import bom process / create new process + importExcelBomProcess(bom, sheet) + // import bom material + importExcelBomMaterial(bom, sheet) + // break } } 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 915aa59..d3dd23d 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,7 +1,6 @@ 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 com.ffii.fpsms.modules.master.entity.* import jakarta.validation.constraints.NotBlank import java.math.BigDecimal import java.time.LocalDateTime @@ -48,4 +47,19 @@ data class ImportBomMatRequest ( var uomId: Long? = null, var uomName: String? = null, var bom: Bom? = null, +) + +data class ImportBomProcessRequest( + var process: Process? = null, + var description: String? = null, + var equipment: Equipment? = null, + var seqNo: Long? = null, + var duration: Int? = null, + var prepTimeInMinute: Int? = 0, + var postProdTimeInMinute: Int? = 0, + var bom: Bom? = null, +) +data class ImportBomProcessMaterialRequest( + var bomProcess: BomProcess? = null, + var bomMaterial: BomMaterial? = null, ) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveEquipmentRequest.kt b/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveEquipmentRequest.kt new file mode 100644 index 0000000..286f973 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveEquipmentRequest.kt @@ -0,0 +1,12 @@ +package com.ffii.fpsms.modules.master.web.models + +import jakarta.validation.constraints.NotBlank + +data class SaveEquipmentRequest( + @field:NotBlank(message = "code cannot be empty") + var code: String, + @field:NotBlank(message = "name cannot be empty") + var name: String, + @field:NotBlank(message = "description cannot be empty") + var description: String, +) diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveProcessRequest.kt b/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveProcessRequest.kt new file mode 100644 index 0000000..b8831bf --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveProcessRequest.kt @@ -0,0 +1,12 @@ +package com.ffii.fpsms.modules.master.web.models + +import jakarta.validation.constraints.NotBlank + +data class SaveProcessRequest( + @field:NotBlank(message = "code cannot be empty") + var code: String, + @field:NotBlank(message = "name cannot be empty") + var name: String, + + var description: String? = null +) diff --git a/src/main/resources/db/changelog/changes/20250612_01_derek/01_update_bom_process_remove_uomId.sql b/src/main/resources/db/changelog/changes/20250612_01_derek/01_update_bom_process_remove_uomId.sql new file mode 100644 index 0000000..4a49c5d --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250612_01_derek/01_update_bom_process_remove_uomId.sql @@ -0,0 +1,5 @@ +-- liquibase formatted sql +-- changeset derek:update_bom_process_remove_uomId +ALTER TABLE `bom_process` +DROP CONSTRAINT `FK_BOM_PROCESS_ON_UOMID`, +DROP COLUMN `uomId`; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20250612_01_derek/02_update_bom_process_add_desc.sql b/src/main/resources/db/changelog/changes/20250612_01_derek/02_update_bom_process_add_desc.sql new file mode 100644 index 0000000..af1351f --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250612_01_derek/02_update_bom_process_add_desc.sql @@ -0,0 +1,4 @@ +-- liquibase formatted sql +-- changeset derek:update_bom_process_add_desc +ALTER TABLE `bom_process` +ADD COLUMN `description` VARCHAR(255) NULL; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20250612_01_derek/03_update_bom_process_add_equipment.sql b/src/main/resources/db/changelog/changes/20250612_01_derek/03_update_bom_process_add_equipment.sql new file mode 100644 index 0000000..d936e78 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250612_01_derek/03_update_bom_process_add_equipment.sql @@ -0,0 +1,5 @@ +-- liquibase formatted sql +-- changeset derek:update_bom_process_add_equipment +ALTER TABLE `bom_process` +ADD COLUMN `equipmentId` INT(255) NULL AFTER `processId`, +ADD CONSTRAINT FK_BOM_PROCESS_TO_EQUIPMENT_ON_EQUIPMENT_ID FOREIGN KEY (equipmentId) REFERENCES equipment (id); \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20250612_01_derek/04_update_bom_process_add_prepTime_postProdTime.sql b/src/main/resources/db/changelog/changes/20250612_01_derek/04_update_bom_process_add_prepTime_postProdTime.sql new file mode 100644 index 0000000..5c82d1f --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250612_01_derek/04_update_bom_process_add_prepTime_postProdTime.sql @@ -0,0 +1,5 @@ +-- liquibase formatted sql +-- changeset derek:update_bom_process_add_prepTime_postProdTime +ALTER TABLE `bom_process` +ADD COLUMN `prepTimeInMinute` INT(11) NULL AFTER `duration`, +ADD COLUMN `postProdTimeInMinute` INT(11) NULL AFTER `prepTimeInMinute`; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20250612_01_derek/05_update_bom_process_modify_time_col.sql b/src/main/resources/db/changelog/changes/20250612_01_derek/05_update_bom_process_modify_time_col.sql new file mode 100644 index 0000000..b282647 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250612_01_derek/05_update_bom_process_modify_time_col.sql @@ -0,0 +1,5 @@ +-- liquibase formatted sql +-- changeset derek:update_bom_process_modify_time_col +ALTER TABLE `bom_process` +MODIFY COLUMN `prepTimeInMinute` INT(11) NULL DEFAULT 0, +MODIFY COLUMN `postProdTimeInMinute` INT(11) NULL DEFAULT 0; \ No newline at end of file