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