| @@ -7,10 +7,15 @@ 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.Resource | |||
| import org.springframework.core.io.support.PathMatchingResourcePatternResolver | |||
| import org.springframework.http.ResponseEntity | |||
| import org.springframework.stereotype.Service | |||
| import java.io.File | |||
| import java.io.ByteArrayOutputStream | |||
| import java.nio.file.Path | |||
| import java.nio.file.Paths | |||
| import java.nio.file.Files; | |||
| import java.nio.file.StandardCopyOption | |||
| @Service | |||
| open class BomService( | |||
| @@ -106,6 +111,9 @@ open class BomService( | |||
| private fun saveBomMaterial(req: ImportBomMatRequest): BomMaterial { | |||
| // val uom = uomConversionRepository.findByCodeAndDeletedFalse() | |||
| println("printing") | |||
| println(req) | |||
| println(req.item) | |||
| val bomMaterial = BomMaterial().apply { | |||
| this.item = req.item | |||
| this.itemName = req.item!!.name | |||
| @@ -203,8 +211,8 @@ open class BomService( | |||
| println("startRowIndex: $startRowIndex") | |||
| println("endRowIndex: $endRowIndex") | |||
| println("first condition: ${startColumnIndex < endColumnIndex}") | |||
| println("second condition: ${startRowIndex < endRowIndex}") | |||
| // println("first condition: ${startColumnIndex < endColumnIndex}") | |||
| // println("second condition: ${startRowIndex < endRowIndex}") | |||
| if (startColumnIndex < endColumnIndex) { | |||
| startColumnIndex++ | |||
| } else if (startRowIndex < endRowIndex) { | |||
| @@ -323,7 +331,7 @@ open class BomService( | |||
| } | |||
| private fun extractDurationStringToMinutes(str: String): Int { | |||
| val regex = """(\d+)(\D+)(\d+)(\D+)""".toRegex() | |||
| val regex = """(\d+)(\D+)""".toRegex() | |||
| val matchResult = regex.find(str) | |||
| // fun returnMultiplier(unit: String): { | |||
| // | |||
| @@ -352,7 +360,7 @@ open class BomService( | |||
| var startColumnIndex = 0 | |||
| val endColumnIndex = 11 | |||
| while (startRowIndex < endRowIndex) { | |||
| val tempRow = sheet.getRow(startRowIndex) | |||
| val tempRow = sheet.getRow(startRowIndex) ?: throw Exception() | |||
| val tempCell = tempRow.getCell(startColumnIndex) | |||
| if (tempCell != null && tempCell.cellType == CellType.STRING && tempCell.stringCellValue.trim() == "工序") { | |||
| startRowIndex += 2 // skip column header | |||
| @@ -367,7 +375,7 @@ open class BomService( | |||
| while (startRowIndex != endRowIndex || startColumnIndex != endColumnIndex) { | |||
| val tempRow = sheet.getRow(startRowIndex) | |||
| val tempCell = tempRow.getCell(startColumnIndex) | |||
| val checkCell = tempRow.getCell(0) | |||
| // val checkCell = tempRow.getCell(0) | |||
| if (startColumnIndex == 0 && (tempCell == null || tempCell.cellType == CellType.BLANK)) { | |||
| // println("hi") | |||
| break | |||
| @@ -461,24 +469,102 @@ open class BomService( | |||
| ) | |||
| } | |||
| } catch (e: Error) { | |||
| throw e | |||
| // throw e | |||
| } | |||
| } | |||
| } | |||
| } | |||
| open fun exportProblematicBOM() { | |||
| val resolver = PathMatchingResourcePatternResolver() | |||
| val successExcel = resolver.getResources("file:C:/Users/2Fi/Desktop/fail/*.xlsx") | |||
| val failedExcels = resolver.getResources("file:C:/Users/2Fi/Desktop/fail/*.xlsx") | |||
| open fun importBOM() { | |||
| } | |||
| open fun importBOM(): ByteArray { | |||
| // val folderPath = "bomImport" | |||
| // val folder = File(folderPath) | |||
| val resolver = PathMatchingResourcePatternResolver() | |||
| val excels = resolver.getResources("bomImport/*.xlsx") | |||
| // val excels = resolver.getResources("bomImport/*.xlsx") | |||
| val excels = resolver.getResources("file:C:/Users/2Fi/Desktop/test folder/*.xlsx") | |||
| println("size: ${excels.size}") | |||
| val logExcel = ClassPathResource("excelTemplate/bom_excel_issue_log.xlsx") | |||
| val templateInputStream = logExcel.inputStream | |||
| val workbook: Workbook = XSSFWorkbook(templateInputStream) | |||
| val logSheet: Sheet = workbook.getSheetAt(0) | |||
| var rowIndex = 0 | |||
| /////// for outputing issue log //////////// | |||
| // excels.forEachIndexed { index, resource -> | |||
| //get sheet | |||
| // println(resource.filename) | |||
| // val templateInputStream = resource.inputStream | |||
| // val workbook: Workbook = XSSFWorkbook(templateInputStream) | |||
| // val sheet: Sheet = workbook.getSheetAt(0) | |||
| // println("sheetName") | |||
| // println(sheet.sheetName) | |||
| // val successPath: Path = Paths.get("C:/Users/2Fi/Desktop/success/${resource.filename}") | |||
| // val failPath: Path = Paths.get("C:/Users/2Fi/Desktop/fail/${resource.filename}") | |||
| // val bom = try { | |||
| // importExcelBomBasicInfo(sheet) // Updating BOM table | |||
| // } catch (e: Exception) { | |||
| // println("Error in importExcelBomBasicInfo: ${e.message}") | |||
| // rowIndex++ | |||
| // val row = logSheet.createRow(rowIndex) | |||
| // row.createCell(0).setCellValue(resource.filename) | |||
| // row.createCell(1).setCellValue(false) | |||
| // row.createCell(2).setCellValue(e.message) | |||
| // Files.copy(templateInputStream, failPath, StandardCopyOption.REPLACE_EXISTING) | |||
| // throw | |||
| // return@forEachIndexed | |||
| //// throw e // Rethrow to handle in the outer try-catch | |||
| // } | |||
| // try { | |||
| // importExcelBomProcess(bom, sheet) | |||
| // } catch (e: Exception) { | |||
| // println("Error in importExcelBomProcess: ${e.message}") | |||
| // rowIndex++ | |||
| // val row = logSheet.createRow(rowIndex) | |||
| // row.createCell(0).setCellValue(resource.filename) | |||
| // row.createCell(1).setCellValue(false) | |||
| // row.createCell(2).setCellValue(e.message) | |||
| // Files.copy(templateInputStream, failPath, StandardCopyOption.REPLACE_EXISTING) | |||
| // throw | |||
| // return@forEachIndexed | |||
| //// throw e // Rethrow to handle in the outer try-catch | |||
| // } | |||
| // try { | |||
| // importExcelBomMaterial(bom, sheet) | |||
| // } catch (e: Exception) { | |||
| // println("Error in importExcelBomMaterial: ${e.message}") | |||
| // rowIndex++ | |||
| // val row = logSheet.createRow(rowIndex) | |||
| // row.createCell(0).setCellValue(resource.filename) | |||
| // row.createCell(1).setCellValue(false) | |||
| // row.createCell(2).setCellValue(e.message) | |||
| // Files.copy(templateInputStream, failPath, StandardCopyOption.REPLACE_EXISTING) | |||
| // throw | |||
| // return@forEachIndexed | |||
| //// throw e // Rethrow to handle in the outer try-catch | |||
| // } | |||
| // rowIndex++ | |||
| // val row = logSheet.createRow(rowIndex) | |||
| // row.createCell(0).setCellValue(resource.filename) | |||
| // row.createCell(1).setCellValue(true) | |||
| // Files.copy(templateInputStream, successPath, StandardCopyOption.REPLACE_EXISTING) | |||
| // } | |||
| // val outputStream = ByteArrayOutputStream() | |||
| // workbook.write(outputStream) | |||
| // workbook.close() | |||
| // return outputStream.toByteArray() | |||
| //////////////////////////////////////////////////////////////////////////////////////////// | |||
| 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 templateInputStream2 = resource.inputStream | |||
| val workbook2: Workbook = XSSFWorkbook(templateInputStream2) | |||
| val sheet: Sheet = workbook2.getSheetAt(0) | |||
| val bom = importExcelBomBasicInfo(sheet) // updating bom table | |||
| // import bom process / create new process | |||
| importExcelBomProcess(bom, sheet) | |||
| @@ -487,5 +573,9 @@ open class BomService( | |||
| // break | |||
| } | |||
| val outputStream = ByteArrayOutputStream() | |||
| workbook.write(outputStream) | |||
| workbook.close() | |||
| return outputStream.toByteArray() | |||
| } | |||
| } | |||
| @@ -2,10 +2,16 @@ 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.core.io.ByteArrayResource | |||
| import org.springframework.core.io.Resource | |||
| import org.springframework.http.HttpHeaders | |||
| import org.springframework.http.ResponseEntity | |||
| import org.springframework.web.bind.annotation.GetMapping | |||
| import org.springframework.web.bind.annotation.PathVariable | |||
| import org.springframework.web.bind.annotation.PostMapping | |||
| import org.springframework.web.bind.annotation.RequestMapping | |||
| import org.springframework.web.bind.annotation.RestController | |||
| import java.time.LocalDate | |||
| @RestController | |||
| @RequestMapping("/bom") | |||
| @@ -18,7 +24,17 @@ class BomController ( | |||
| } | |||
| @PostMapping("/import-bom") | |||
| fun importBom() { | |||
| return bomService.importBOM() | |||
| fun importBom(): ResponseEntity<Resource> { | |||
| val reportResult = bomService.importBOM() | |||
| val filename = "bom_excel_issue_log_${LocalDate.now()}.xlsx" | |||
| return ResponseEntity.ok() | |||
| .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"$filename\"") | |||
| .header(HttpHeaders.CONTENT_TYPE, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") | |||
| .body(ByteArrayResource(reportResult)) | |||
| } | |||
| // @PostMapping("/export-problematic-bom") | |||
| // fun exportProblematicBom() { | |||
| // return bomService.importBOM() | |||
| // } | |||
| } | |||
| @@ -4,6 +4,7 @@ import com.ffii.core.response.RecordsRes | |||
| import com.ffii.core.support.AbstractBaseEntityService | |||
| import com.ffii.core.support.JdbcDao | |||
| import com.ffii.fpsms.modules.common.SecurityUtils | |||
| import com.ffii.fpsms.modules.master.web.models.MessageResponse | |||
| import com.ffii.fpsms.modules.pickOrder.entity.PickOrder | |||
| import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository | |||
| import com.ffii.fpsms.modules.pickOrder.entity.projection.PickOrderInfo | |||
| @@ -154,13 +155,14 @@ open class PickOrderService( | |||
| throw IllegalArgumentException("consoCode must not be null") | |||
| } | |||
| val sql = StringBuilder("select" | |||
| + " po.status as poStatus," | |||
| + " pol.id, " | |||
| + " i.name as itemName, " | |||
| + " pol.qty as qty, " | |||
| + " max(uc.code) as uom, " | |||
| + " group_concat(JSON_ARRAY(ill.id)) as lotLineId, " | |||
| + " group_concat(JSON_ARRAY(w.name)) as warehouse, " | |||
| + " group_concat(JSON_ARRAY(il.lotNo)) as suggestedLotNo, " | |||
| + " group_concat(ill.id) as lotLineId, " | |||
| + " group_concat(w.name) as warehouse, " | |||
| + " group_concat(il.lotNo) as suggestedLotNo, " | |||
| + " pol.status " | |||
| + " from pick_order po " | |||
| + " left join pick_order_line pol on pol.poId = po.id " | |||
| @@ -172,7 +174,7 @@ open class PickOrderService( | |||
| + " left join uom_conversion uc on uc.id = pol.uomId " | |||
| + " where po.deleted = false " | |||
| + " and po.consoCode = :consoCode " | |||
| + " group by pol.id, i.name, pol.qty, pol.status " | |||
| + " group by po.status, pol.id, i.name, pol.qty, pol.status " | |||
| ) | |||
| return jdbcDao.queryForList(sql.toString(), args); | |||
| } | |||
| @@ -432,7 +434,7 @@ open class PickOrderService( | |||
| @Throws(IOException::class) | |||
| @Transactional | |||
| open fun completeStockOut(consoCode: String): Boolean { | |||
| open fun completeStockOut(consoCode: String): MessageResponse { | |||
| val stockOut = stockOutRepository.findByConsoPickOrderCode(consoCode).orElseThrow() | |||
| val stockOutLines = stockOutLIneRepository.findAllByStockOutId(stockOut.id!!) | |||
| val unfinishedLines = stockOutLines.filter { | |||
| @@ -443,16 +445,30 @@ open class PickOrderService( | |||
| stockOut.apply { | |||
| this.status = StockOutStatus.COMPLETE.status | |||
| } | |||
| stockOutRepository.save(stockOut) | |||
| val savedStockOut = stockOutRepository.saveAndFlush(stockOut) | |||
| val pickOrderEntries = pickOrderRepository.findAllByConsoCode(consoCode).map { | |||
| it.apply { | |||
| it.status = PickOrderStatus.COMPLETED | |||
| } | |||
| } | |||
| pickOrderRepository.saveAll(pickOrderEntries) | |||
| return true | |||
| return MessageResponse( | |||
| id = savedStockOut.id, | |||
| name = savedStockOut.consoPickOrderCode ?: savedStockOut.deliveryOrderCode, | |||
| code = savedStockOut.consoPickOrderCode ?: savedStockOut.deliveryOrderCode, | |||
| type = savedStockOut.type, | |||
| message = "completed", | |||
| errorPosition = null, | |||
| ) | |||
| } else { | |||
| return false | |||
| return MessageResponse( | |||
| id = stockOut.id, | |||
| name = stockOut.consoPickOrderCode ?: stockOut.deliveryOrderCode, | |||
| code = stockOut.consoPickOrderCode ?: stockOut.deliveryOrderCode, | |||
| type = stockOut.type, | |||
| message = "not completed", | |||
| errorPosition = null, | |||
| ) | |||
| } | |||
| } | |||
| } | |||
| @@ -3,6 +3,7 @@ package com.ffii.fpsms.modules.pickOrder.web | |||
| import com.ffii.core.response.RecordsRes | |||
| import com.ffii.core.utils.CriteriaArgsBuilder | |||
| import com.ffii.core.utils.PagingUtils | |||
| import com.ffii.fpsms.modules.master.web.models.MessageResponse | |||
| import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository | |||
| import com.ffii.fpsms.modules.pickOrder.entity.projection.PickOrderInfo | |||
| import com.ffii.fpsms.modules.pickOrder.service.PickOrderService | |||
| @@ -103,7 +104,7 @@ class PickOrderController( | |||
| } | |||
| @PostMapping("/consoPickOrder/complete/{consoCode}") | |||
| fun completeConsoPickOrders(@PathVariable consoCode: String): Boolean { | |||
| fun completeConsoPickOrders(@PathVariable consoCode: String): MessageResponse { | |||
| return pickOrderService.completeStockOut(consoCode) | |||
| } | |||
| @@ -5,6 +5,7 @@ import com.ffii.fpsms.modules.master.entity.Items | |||
| import com.ffii.fpsms.modules.stock.entity.projection.InventoryInfo | |||
| import org.springframework.stereotype.Repository | |||
| import java.io.Serializable | |||
| import java.util.Optional | |||
| @Repository | |||
| interface InventoryRepository: AbstractRepository<Inventory, Long> { | |||
| @@ -13,4 +14,6 @@ interface InventoryRepository: AbstractRepository<Inventory, Long> { | |||
| fun findInventoryInfoByItemIdInAndDeletedIsFalse(itemIds: List<Serializable>): List<InventoryInfo> | |||
| fun findInventoryInfoByItemInAndDeletedIsFalse(items: List<Items>): List<InventoryInfo> | |||
| fun findByItemId(itemId: Long): Optional<Inventory> | |||
| } | |||
| @@ -13,6 +13,7 @@ import org.springframework.data.repository.query.Param | |||
| import org.springframework.stereotype.Service | |||
| import org.springframework.transaction.annotation.Transactional | |||
| import java.io.IOException | |||
| import java.math.BigDecimal | |||
| import java.time.LocalDate | |||
| import java.time.LocalDateTime | |||
| @@ -115,6 +116,20 @@ open class StockOutLineService( | |||
| this.qty = request.qty | |||
| this.status = StockOutLineStatus.COMPLETE.status // complete | |||
| } | |||
| // update inventory lot line | |||
| val targetLotLine = inventoryLotLineRepository.findById(request.inventoryLotLineId!!).orElseThrow() | |||
| val targetLotLineEntry = targetLotLine.apply { | |||
| this.outQty = (this.outQty?: BigDecimal.ZERO) + request.qty.toBigDecimal() | |||
| this.holdQty = this.holdQty!!.minus(request.qty.toBigDecimal()) | |||
| } | |||
| inventoryLotLineRepository.save(targetLotLineEntry) | |||
| // update inventory | |||
| val inventory = inventoryRepository.findByItemId(request.itemId).orElseThrow() | |||
| val inventoryEntry = inventory.apply { | |||
| this.onHandQty = this.onHandQty!!.minus(request.qty.toBigDecimal()) | |||
| this.onHoldQty = this.onHoldQty!!.minus(request.qty.toBigDecimal()) | |||
| } | |||
| return listOf(stockOutLine, newStockOutLine) | |||
| } | |||
| @Transactional | |||
| @@ -27,4 +27,7 @@ open class StockOutService( | |||
| private val stockLedgerRepository: StockLedgerRepository, | |||
| ): AbstractBaseEntityService<StockOut, Long, StockOutRepository>(jdbcDao, stockOutRepository) { | |||
| open fun getConsoStatus(consoCode: String): String { | |||
| return stockOutRepository.findByConsoPickOrderCode(consoCode).orElseThrow().status!! | |||
| } | |||
| } | |||
| @@ -1,12 +1,22 @@ | |||
| package com.ffii.fpsms.modules.stock.web | |||
| import com.ffii.fpsms.modules.stock.service.StockOutService | |||
| import org.springframework.web.bind.annotation.GetMapping | |||
| import org.springframework.web.bind.annotation.PathVariable | |||
| import org.springframework.web.bind.annotation.PostMapping | |||
| import org.springframework.web.bind.annotation.RequestMapping | |||
| import org.springframework.web.bind.annotation.RestController | |||
| data class Status( | |||
| val status: String | |||
| ) | |||
| @RestController | |||
| @RequestMapping("/stockOut") | |||
| class StockOutController( | |||
| private val stockOutService: StockOutService | |||
| ) { | |||
| @GetMapping("/get-status/{consoCode}") | |||
| fun getConsoStatus(@PathVariable consoCode: String): Status { | |||
| val status = stockOutService.getConsoStatus(consoCode) | |||
| return Status(status) | |||
| } | |||
| } | |||