Browse Source

update bom excel import

production_process
MSI\derek 2 months ago
parent
commit
8910219a17
14 changed files with 315 additions and 42 deletions
  1. +24
    -4
      src/main/java/com/ffii/fpsms/modules/master/entity/BomProcess.kt
  2. +2
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/BomProcessRepository.kt
  3. +3
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/EquipmentRepository.kt
  4. +0
    -1
      src/main/java/com/ffii/fpsms/modules/master/entity/Process.kt
  5. +3
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/ProcessRepository.kt
  6. +219
    -35
      src/main/java/com/ffii/fpsms/modules/master/service/BomService.kt
  7. +16
    -2
      src/main/java/com/ffii/fpsms/modules/master/web/models/SaveBomRequest.kt
  8. +12
    -0
      src/main/java/com/ffii/fpsms/modules/master/web/models/SaveEquipmentRequest.kt
  9. +12
    -0
      src/main/java/com/ffii/fpsms/modules/master/web/models/SaveProcessRequest.kt
  10. +5
    -0
      src/main/resources/db/changelog/changes/20250612_01_derek/01_update_bom_process_remove_uomId.sql
  11. +4
    -0
      src/main/resources/db/changelog/changes/20250612_01_derek/02_update_bom_process_add_desc.sql
  12. +5
    -0
      src/main/resources/db/changelog/changes/20250612_01_derek/03_update_bom_process_add_equipment.sql
  13. +5
    -0
      src/main/resources/db/changelog/changes/20250612_01_derek/04_update_bom_process_add_prepTime_postProdTime.sql
  14. +5
    -0
      src/main/resources/db/changelog/changes/20250612_01_derek/05_update_bom_process_modify_time_col.sql

+ 24
- 4
src/main/java/com/ffii/fpsms/modules/master/entity/BomProcess.kt View File

@@ -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<Long>() {
@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<Long>() {
@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<BomProcessMaterial> = HashSet()
}

+ 2
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/BomProcessRepository.kt View File

@@ -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<BomProcess, Long> {
fun findBySeqNoAndBomIdAndDeletedIsFalse(seqNo: Int, bomId: Long): BomProcess?
}

+ 3
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/EquipmentRepository.kt View File

@@ -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<Equipment, Long> {
fun findByNameAndDeletedIsFalse(name: String): Equipment?
fun findByCodeAndDeletedIsFalse(code: String): Equipment?
}

+ 0
- 1
src/main/java/com/ffii/fpsms/modules/master/entity/Process.kt View File

@@ -26,7 +26,6 @@ open class Process : BaseEntity<Long>() {
open var description: String? = null

@Size(max = 1000)
@NotNull
@Column(name = "remarks", nullable = true, length = 1000)
open var remarks: String? = null
}

+ 3
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/ProcessRepository.kt View File

@@ -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<Process, Long> {
fun findByCodeAndDeletedIsFalse(code: String): Process?
fun findByNameAndDeletedIsFalse(name: String): Process?
}

+ 219
- 35
src/main/java/com/ffii/fpsms/modules/master/service/BomService.kt View File

@@ -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<Bom> {
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
}
}

+ 16
- 2
src/main/java/com/ffii/fpsms/modules/master/web/models/SaveBomRequest.kt View File

@@ -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,
)

+ 12
- 0
src/main/java/com/ffii/fpsms/modules/master/web/models/SaveEquipmentRequest.kt View File

@@ -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,
)

+ 12
- 0
src/main/java/com/ffii/fpsms/modules/master/web/models/SaveProcessRequest.kt View File

@@ -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
)

+ 5
- 0
src/main/resources/db/changelog/changes/20250612_01_derek/01_update_bom_process_remove_uomId.sql View File

@@ -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`;

+ 4
- 0
src/main/resources/db/changelog/changes/20250612_01_derek/02_update_bom_process_add_desc.sql View File

@@ -0,0 +1,4 @@
-- liquibase formatted sql
-- changeset derek:update_bom_process_add_desc
ALTER TABLE `bom_process`
ADD COLUMN `description` VARCHAR(255) NULL;

+ 5
- 0
src/main/resources/db/changelog/changes/20250612_01_derek/03_update_bom_process_add_equipment.sql View File

@@ -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);

+ 5
- 0
src/main/resources/db/changelog/changes/20250612_01_derek/04_update_bom_process_add_prepTime_postProdTime.sql View File

@@ -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`;

+ 5
- 0
src/main/resources/db/changelog/changes/20250612_01_derek/05_update_bom_process_modify_time_col.sql View File

@@ -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;

Loading…
Cancel
Save